#include <assert.h>
#include <types.h>
#include <api/failures.h>
#include <api/invocation.h>
#include <api/syscall.h>
#include <api/types.h>
#include <machine/io.h>
#include <object/structures.h>
#include <object/objecttype.h>
#include <object/cnode.h>
#include <object/interrupt.h>
#include <object/untyped.h>
#include <kernel/cspace.h>
#include <kernel/thread.h>
#include <model/preemption.h>
#include <model/statedata.h>
#include <util.h>
struct finaliseSlot_ret {
exception_t status;
bool_t success;
irq_t irq;
};
typedef struct finaliseSlot_ret finaliseSlot_ret_t;
static finaliseSlot_ret_t finaliseSlot(cte_t *slot, bool_t exposed);
static void emptySlot(cte_t *slot, irq_t irq);
static exception_t reduceZombie(cte_t* slot, bool_t exposed);
exception_t
decodeCNodeInvocation(word_t invLabel, word_t length, cap_t cap,
extra_caps_t excaps, word_t *buffer)
{
lookupSlot_ret_t lu_ret;
cte_t *destSlot;
word_t index, w_bits;
exception_t status;
assert(cap_get_capType(cap) == cap_cnode_cap);
if (invLabel < CNodeRevoke || invLabel > CNodeSaveCaller) {
userError("CNodeCap: Illegal Operation attempted.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (length < 2) {
userError("CNode operation: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
index = getSyscallArg(0, buffer);
w_bits = getSyscallArg(1, buffer);
lu_ret = lookupTargetSlot(cap, index, w_bits);
if (lu_ret.status != EXCEPTION_NONE) {
userError("CNode operation: Target slot invalid.");
return lu_ret.status;
}
destSlot = lu_ret.slot;
if (invLabel >= CNodeCopy && invLabel <= CNodeMutate) {
cte_t *srcSlot;
word_t srcIndex, srcDepth, capData;
bool_t isMove;
seL4_CapRights_t cap_rights;
cap_t srcRoot, newCap;
deriveCap_ret_t dc_ret;
cap_t srcCap;
if (length < 4 || excaps.excaprefs[0] == NULL) {
userError("CNode Copy/Mint/Move/Mutate: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
srcIndex = getSyscallArg(2, buffer);
srcDepth = getSyscallArg(3, buffer);
srcRoot = excaps.excaprefs[0]->cap;
status = ensureEmptySlot(destSlot);
if (status != EXCEPTION_NONE) {
userError("CNode Copy/Mint/Move/Mutate: Destination not empty.");
return status;
}
lu_ret = lookupSourceSlot(srcRoot, srcIndex, srcDepth);
if (lu_ret.status != EXCEPTION_NONE) {
userError("CNode Copy/Mint/Move/Mutate: Invalid source slot.");
return lu_ret.status;
}
srcSlot = lu_ret.slot;
if (cap_get_capType(srcSlot->cap) == cap_null_cap) {
userError("CNode Copy/Mint/Move/Mutate: Source slot invalid or empty.");
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = 1;
current_lookup_fault =
lookup_fault_missing_capability_new(srcDepth);
return EXCEPTION_SYSCALL_ERROR;
}
switch (invLabel) {
case CNodeCopy:
if (length < 5) {
userError("Truncated message for CNode Copy operation.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
cap_rights = rightsFromWord(getSyscallArg(4, buffer));
srcCap = maskCapRights(cap_rights, srcSlot->cap);
dc_ret = deriveCap(srcSlot, srcCap);
if (dc_ret.status != EXCEPTION_NONE) {
userError("Error deriving cap for CNode Copy operation.");
return dc_ret.status;
}
newCap = dc_ret.cap;
isMove = false;
break;
case CNodeMint:
if (length < 6) {
userError("CNode Mint: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
cap_rights = rightsFromWord(getSyscallArg(4, buffer));
capData = getSyscallArg(5, buffer);
srcCap = maskCapRights(cap_rights, srcSlot->cap);
dc_ret = deriveCap(srcSlot,
updateCapData(false, capData, srcCap));
if (dc_ret.status != EXCEPTION_NONE) {
userError("Error deriving cap for CNode Mint operation.");
return dc_ret.status;
}
newCap = dc_ret.cap;
isMove = false;
break;
case CNodeMove:
newCap = srcSlot->cap;
isMove = true;
break;
case CNodeMutate:
if (length < 5) {
userError("CNode Mutate: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
capData = getSyscallArg(4, buffer);
newCap = updateCapData(true, capData, srcSlot->cap);
isMove = true;
break;
default:
assert (0);
return EXCEPTION_NONE;
}
if (cap_get_capType(newCap) == cap_null_cap) {
userError("CNode Copy/Mint/Move/Mutate: Mutated cap would be invalid.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
if (isMove) {
return invokeCNodeMove(newCap, srcSlot, destSlot);
} else {
return invokeCNodeInsert(newCap, srcSlot, destSlot);
}
}
if (invLabel == CNodeRevoke) {
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeCNodeRevoke(destSlot);
}
if (invLabel == CNodeDelete) {
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeCNodeDelete(destSlot);
}
if (invLabel == CNodeSaveCaller) {
status = ensureEmptySlot(destSlot);
if (status != EXCEPTION_NONE) {
userError("CNode SaveCaller: Destination slot not empty.");
return status;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeCNodeSaveCaller(destSlot);
}
if (invLabel == CNodeCancelBadgedSends) {
cap_t destCap;
destCap = destSlot->cap;
if (!hasCancelSendRights(destCap)) {
userError("CNode CancelBadgedSends: Target cap invalid.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeCNodeCancelBadgedSends(destCap);
}
if (invLabel == CNodeRotate) {
word_t pivotNewData, pivotIndex, pivotDepth;
word_t srcNewData, srcIndex, srcDepth;
cte_t *pivotSlot, *srcSlot;
cap_t pivotRoot, srcRoot, newSrcCap, newPivotCap;
if (length < 8 || excaps.excaprefs[0] == NULL
|| excaps.excaprefs[1] == NULL) {
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
pivotNewData = getSyscallArg(2, buffer);
pivotIndex = getSyscallArg(3, buffer);
pivotDepth = getSyscallArg(4, buffer);
srcNewData = getSyscallArg(5, buffer);
srcIndex = getSyscallArg(6, buffer);
srcDepth = getSyscallArg(7, buffer);
pivotRoot = excaps.excaprefs[0]->cap;
srcRoot = excaps.excaprefs[1]->cap;
lu_ret = lookupSourceSlot(srcRoot, srcIndex, srcDepth);
if (lu_ret.status != EXCEPTION_NONE) {
return lu_ret.status;
}
srcSlot = lu_ret.slot;
lu_ret = lookupPivotSlot(pivotRoot, pivotIndex, pivotDepth);
if (lu_ret.status != EXCEPTION_NONE) {
return lu_ret.status;
}
pivotSlot = lu_ret.slot;
if (pivotSlot == srcSlot || pivotSlot == destSlot) {
userError("CNode Rotate: Pivot slot the same as source or dest slot.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (srcSlot != destSlot) {
status = ensureEmptySlot(destSlot);
if (status != EXCEPTION_NONE) {
return status;
}
}
if (cap_get_capType(srcSlot->cap) == cap_null_cap) {
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = 1;
current_lookup_fault = lookup_fault_missing_capability_new(srcDepth);
return EXCEPTION_SYSCALL_ERROR;
}
if (cap_get_capType(pivotSlot->cap) == cap_null_cap) {
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = 0;
current_lookup_fault = lookup_fault_missing_capability_new(pivotDepth);
return EXCEPTION_SYSCALL_ERROR;
}
newSrcCap = updateCapData(true, srcNewData, srcSlot->cap);
newPivotCap = updateCapData(true, pivotNewData, pivotSlot->cap);
if (cap_get_capType(newSrcCap) == cap_null_cap) {
userError("CNode Rotate: Source cap invalid.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (cap_get_capType(newPivotCap) == cap_null_cap) {
userError("CNode Rotate: Pivot cap invalid.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeCNodeRotate(newSrcCap, newPivotCap,
srcSlot, pivotSlot, destSlot);
}
return EXCEPTION_NONE;
}
exception_t
invokeCNodeRevoke(cte_t *destSlot)
{
return cteRevoke(destSlot);
}
exception_t
invokeCNodeDelete(cte_t *destSlot)
{
return cteDelete(destSlot, true);
}
exception_t
invokeCNodeCancelBadgedSends(cap_t cap)
{
word_t badge = cap_endpoint_cap_get_capEPBadge(cap);
if (badge) {
endpoint_t* ep = (endpoint_t*)
cap_endpoint_cap_get_capEPPtr(cap);
cancelBadgedSends(ep, badge);
}
return EXCEPTION_NONE;
}
exception_t
invokeCNodeInsert(cap_t cap, cte_t *srcSlot, cte_t *destSlot)
{
cteInsert(cap, srcSlot, destSlot);
return EXCEPTION_NONE;
}
exception_t
invokeCNodeMove(cap_t cap, cte_t *srcSlot, cte_t *destSlot)
{
cteMove(cap, srcSlot, destSlot);
return EXCEPTION_NONE;
}
exception_t
invokeCNodeRotate(cap_t cap1, cap_t cap2, cte_t *slot1,
cte_t *slot2, cte_t *slot3)
{
if (slot1 == slot3) {
cteSwap(cap1, slot1, cap2, slot2);
} else {
cteMove(cap2, slot2, slot3);
cteMove(cap1, slot1, slot2);
}
return EXCEPTION_NONE;
}
exception_t
invokeCNodeSaveCaller(cte_t *destSlot)
{
cap_t cap;
cte_t *srcSlot;
srcSlot = TCB_PTR_CTE_PTR(NODE_STATE(ksCurThread), tcbCaller);
cap = srcSlot->cap;
switch (cap_get_capType(cap)) {
case cap_null_cap:
userError("CNode SaveCaller: Reply cap not present.");
break;
case cap_reply_cap:
if (!cap_reply_cap_get_capReplyMaster(cap)) {
cteMove(cap, srcSlot, destSlot);
}
break;
default:
fail("caller capability must be null or reply");
break;
}
return EXCEPTION_NONE;
}
static void
setUntypedCapAsFull(cap_t srcCap, cap_t newCap, cte_t *srcSlot)
{
if ((cap_get_capType(srcCap) == cap_untyped_cap)
&& (cap_get_capType(newCap) == cap_untyped_cap)) {
if ((cap_untyped_cap_get_capPtr(srcCap)
== cap_untyped_cap_get_capPtr(newCap))
&& (cap_untyped_cap_get_capBlockSize(newCap)
== cap_untyped_cap_get_capBlockSize(srcCap))) {
cap_untyped_cap_ptr_set_capFreeIndex(&(srcSlot->cap),
MAX_FREE_INDEX(cap_untyped_cap_get_capBlockSize(srcCap)));
}
}
}
void
cteInsert(cap_t newCap, cte_t *srcSlot, cte_t *destSlot)
{
mdb_node_t srcMDB, newMDB;
cap_t srcCap;
bool_t newCapIsRevocable;
srcMDB = srcSlot->cteMDBNode;
srcCap = srcSlot->cap;
switch (cap_get_capType(newCap)) {
case cap_endpoint_cap:
newCapIsRevocable = (cap_endpoint_cap_get_capEPBadge(newCap) !=
cap_endpoint_cap_get_capEPBadge(srcCap));
break;
case cap_notification_cap:
newCapIsRevocable =
(cap_notification_cap_get_capNtfnBadge(newCap) !=
cap_notification_cap_get_capNtfnBadge(srcCap));
break;
case cap_irq_handler_cap:
newCapIsRevocable = (cap_get_capType(srcCap) ==
cap_irq_control_cap);
break;
case cap_untyped_cap:
newCapIsRevocable = true;
break;
default:
newCapIsRevocable = false;
break;
}
newMDB = mdb_node_set_mdbPrev(srcMDB, CTE_REF(srcSlot));
newMDB = mdb_node_set_mdbRevocable(newMDB, newCapIsRevocable);
newMDB = mdb_node_set_mdbFirstBadged(newMDB, newCapIsRevocable);
assert(cap_get_capType(destSlot->cap) == cap_null_cap);
assert((cte_t*)mdb_node_get_mdbNext(destSlot->cteMDBNode) == NULL &&
(cte_t*)mdb_node_get_mdbPrev(destSlot->cteMDBNode) == NULL);
setUntypedCapAsFull(srcCap, newCap, srcSlot);
destSlot->cap = newCap;
destSlot->cteMDBNode = newMDB;
mdb_node_ptr_set_mdbNext(&srcSlot->cteMDBNode, CTE_REF(destSlot));
if (mdb_node_get_mdbNext(newMDB)) {
mdb_node_ptr_set_mdbPrev(
&CTE_PTR(mdb_node_get_mdbNext(newMDB))->cteMDBNode,
CTE_REF(destSlot));
}
}
void
cteMove(cap_t newCap, cte_t *srcSlot, cte_t *destSlot)
{
mdb_node_t mdb;
word_t prev_ptr, next_ptr;
assert(cap_get_capType(destSlot->cap) == cap_null_cap);
assert((cte_t*)mdb_node_get_mdbNext(destSlot->cteMDBNode) == NULL &&
(cte_t*)mdb_node_get_mdbPrev(destSlot->cteMDBNode) == NULL);
mdb = srcSlot->cteMDBNode;
destSlot->cap = newCap;
srcSlot->cap = cap_null_cap_new();
destSlot->cteMDBNode = mdb;
srcSlot->cteMDBNode = nullMDBNode;
prev_ptr = mdb_node_get_mdbPrev(mdb);
if (prev_ptr)
mdb_node_ptr_set_mdbNext(
&CTE_PTR(prev_ptr)->cteMDBNode,
CTE_REF(destSlot));
next_ptr = mdb_node_get_mdbNext(mdb);
if (next_ptr)
mdb_node_ptr_set_mdbPrev(
&CTE_PTR(next_ptr)->cteMDBNode,
CTE_REF(destSlot));
}
void
capSwapForDelete(cte_t *slot1, cte_t *slot2)
{
cap_t cap1, cap2;
if (slot1 == slot2) {
return;
}
cap1 = slot1->cap;
cap2 = slot2->cap;
cteSwap(cap1, slot1, cap2, slot2);
}
void
cteSwap(cap_t cap1, cte_t *slot1, cap_t cap2, cte_t *slot2)
{
mdb_node_t mdb1, mdb2;
word_t next_ptr, prev_ptr;
slot1->cap = cap2;
slot2->cap = cap1;
mdb1 = slot1->cteMDBNode;
prev_ptr = mdb_node_get_mdbPrev(mdb1);
if (prev_ptr)
mdb_node_ptr_set_mdbNext(
&CTE_PTR(prev_ptr)->cteMDBNode,
CTE_REF(slot2));
next_ptr = mdb_node_get_mdbNext(mdb1);
if (next_ptr)
mdb_node_ptr_set_mdbPrev(
&CTE_PTR(next_ptr)->cteMDBNode,
CTE_REF(slot2));
mdb2 = slot2->cteMDBNode;
slot1->cteMDBNode = mdb2;
slot2->cteMDBNode = mdb1;
prev_ptr = mdb_node_get_mdbPrev(mdb2);
if (prev_ptr)
mdb_node_ptr_set_mdbNext(
&CTE_PTR(prev_ptr)->cteMDBNode,
CTE_REF(slot1));
next_ptr = mdb_node_get_mdbNext(mdb2);
if (next_ptr)
mdb_node_ptr_set_mdbPrev(
&CTE_PTR(next_ptr)->cteMDBNode,
CTE_REF(slot1));
}
exception_t
cteRevoke(cte_t *slot)
{
cte_t *nextPtr;
exception_t status;
for (nextPtr = CTE_PTR(mdb_node_get_mdbNext(slot->cteMDBNode));
nextPtr && isMDBParentOf(slot, nextPtr);
nextPtr = CTE_PTR(mdb_node_get_mdbNext(slot->cteMDBNode))) {
status = cteDelete(nextPtr, true);
if (status != EXCEPTION_NONE) {
return status;
}
status = preemptionPoint();
if (status != EXCEPTION_NONE) {
return status;
}
}
return EXCEPTION_NONE;
}
exception_t
cteDelete(cte_t *slot, bool_t exposed)
{
finaliseSlot_ret_t fs_ret;
fs_ret = finaliseSlot(slot, exposed);
if (fs_ret.status != EXCEPTION_NONE) {
return fs_ret.status;
}
if (exposed || fs_ret.success) {
emptySlot(slot, fs_ret.irq);
}
return EXCEPTION_NONE;
}
static void
emptySlot(cte_t *slot, irq_t irq)
{
if (cap_get_capType(slot->cap) != cap_null_cap) {
mdb_node_t mdbNode;
cte_t *prev, *next;
mdbNode = slot->cteMDBNode;
prev = CTE_PTR(mdb_node_get_mdbPrev(mdbNode));
next = CTE_PTR(mdb_node_get_mdbNext(mdbNode));
if (prev) {
mdb_node_ptr_set_mdbNext(&prev->cteMDBNode, CTE_REF(next));
}
if (next) {
mdb_node_ptr_set_mdbPrev(&next->cteMDBNode, CTE_REF(prev));
}
if (next)
mdb_node_ptr_set_mdbFirstBadged(&next->cteMDBNode,
mdb_node_get_mdbFirstBadged(next->cteMDBNode) ||
mdb_node_get_mdbFirstBadged(mdbNode));
slot->cap = cap_null_cap_new();
slot->cteMDBNode = nullMDBNode;
if (irq != irqInvalid) {
deletedIRQHandler(irq);
}
}
}
static inline bool_t CONST
capRemovable(cap_t cap, cte_t* slot)
{
switch (cap_get_capType(cap)) {
case cap_null_cap:
return true;
case cap_zombie_cap: {
word_t n = cap_zombie_cap_get_capZombieNumber(cap);
cte_t* z_slot = (cte_t*)cap_zombie_cap_get_capZombiePtr(cap);
return (n == 0 || (n == 1 && slot == z_slot));
}
default:
fail("finaliseCap should only return Zombie or NullCap");
}
}
static inline bool_t CONST
capCyclicZombie(cap_t cap, cte_t *slot)
{
return cap_get_capType(cap) == cap_zombie_cap &&
CTE_PTR(cap_zombie_cap_get_capZombiePtr(cap)) == slot;
}
static finaliseSlot_ret_t
finaliseSlot(cte_t *slot, bool_t immediate)
{
bool_t final;
finaliseCap_ret_t fc_ret;
exception_t status;
finaliseSlot_ret_t ret;
while (cap_get_capType(slot->cap) != cap_null_cap) {
final = isFinalCapability(slot);
fc_ret = finaliseCap(slot->cap, final, false);
if (capRemovable(fc_ret.remainder, slot)) {
ret.status = EXCEPTION_NONE;
ret.success = true;
ret.irq = fc_ret.irq;
return ret;
}
slot->cap = fc_ret.remainder;
if (!immediate && capCyclicZombie(fc_ret.remainder, slot)) {
ret.status = EXCEPTION_NONE;
ret.success = false;
ret.irq = fc_ret.irq;
return ret;
}
status = reduceZombie(slot, immediate);
if (status != EXCEPTION_NONE) {
ret.status = status;
ret.success = false;
ret.irq = irqInvalid;
return ret;
}
status = preemptionPoint();
if (status != EXCEPTION_NONE) {
ret.status = status;
ret.success = false;
ret.irq = irqInvalid;
return ret;
}
}
ret.status = EXCEPTION_NONE;
ret.success = true;
ret.irq = irqInvalid;
return ret;
}
static exception_t
reduceZombie(cte_t* slot, bool_t immediate)
{
cte_t* ptr;
word_t n, type;
exception_t status;
assert(cap_get_capType(slot->cap) == cap_zombie_cap);
ptr = (cte_t*)cap_zombie_cap_get_capZombiePtr(slot->cap);
n = cap_zombie_cap_get_capZombieNumber(slot->cap);
type = cap_zombie_cap_get_capZombieType(slot->cap);
assert(n > 0);
if (immediate) {
cte_t* endSlot = &ptr[n - 1];
status = cteDelete(endSlot, false);
if (status != EXCEPTION_NONE) {
return status;
}
switch (cap_get_capType(slot->cap)) {
case cap_null_cap:
break;
case cap_zombie_cap: {
cte_t* ptr2 =
(cte_t*)cap_zombie_cap_get_capZombiePtr(slot->cap);
if (ptr == ptr2 &&
cap_zombie_cap_get_capZombieNumber(slot->cap) == n &&
cap_zombie_cap_get_capZombieType(slot->cap) == type) {
assert(cap_get_capType(endSlot->cap) == cap_null_cap);
slot->cap =
cap_zombie_cap_set_capZombieNumber(slot->cap, n - 1);
} else {
assert(ptr2 == slot && ptr != slot);
}
break;
}
default:
fail("Expected recursion to result in Zombie.");
}
} else {
assert(ptr != slot);
if (cap_get_capType(ptr->cap) == cap_zombie_cap) {
assert(ptr != CTE_PTR(cap_zombie_cap_get_capZombiePtr(ptr->cap)));
}
capSwapForDelete(ptr, slot);
}
return EXCEPTION_NONE;
}
void
cteDeleteOne(cte_t* slot)
{
word_t cap_type = cap_get_capType(slot->cap);
if (cap_type != cap_null_cap) {
bool_t final;
finaliseCap_ret_t fc_ret UNUSED;
final = isFinalCapability(slot);
fc_ret = finaliseCap(slot->cap, final, true);
assert(capRemovable(fc_ret.remainder, slot) &&
fc_ret.irq == irqInvalid);
emptySlot(slot, irqInvalid);
}
}
void
insertNewCap(cte_t *parent, cte_t *slot, cap_t cap)
{
cte_t *next;
next = CTE_PTR(mdb_node_get_mdbNext(parent->cteMDBNode));
slot->cap = cap;
slot->cteMDBNode = mdb_node_new(CTE_REF(next), true, true, CTE_REF(parent));
if (next) {
mdb_node_ptr_set_mdbPrev(&next->cteMDBNode, CTE_REF(slot));
}
mdb_node_ptr_set_mdbNext(&parent->cteMDBNode, CTE_REF(slot));
}
void
setupReplyMaster(tcb_t *thread)
{
cte_t *slot;
slot = TCB_PTR_CTE_PTR(thread, tcbReply);
if (cap_get_capType(slot->cap) == cap_null_cap) {
slot->cap = cap_reply_cap_new(true, TCB_REF(thread));
slot->cteMDBNode = nullMDBNode;
mdb_node_ptr_set_mdbRevocable(&slot->cteMDBNode, true);
mdb_node_ptr_set_mdbFirstBadged(&slot->cteMDBNode, true);
}
}
bool_t PURE
isMDBParentOf(cte_t *cte_a, cte_t *cte_b)
{
if (!mdb_node_get_mdbRevocable(cte_a->cteMDBNode)) {
return false;
}
if (!sameRegionAs(cte_a->cap, cte_b->cap)) {
return false;
}
switch (cap_get_capType(cte_a->cap)) {
case cap_endpoint_cap: {
word_t badge;
badge = cap_endpoint_cap_get_capEPBadge(cte_a->cap);
if (badge == 0) {
return true;
}
return (badge == cap_endpoint_cap_get_capEPBadge(cte_b->cap)) &&
!mdb_node_get_mdbFirstBadged(cte_b->cteMDBNode);
break;
}
case cap_notification_cap: {
word_t badge;
badge = cap_notification_cap_get_capNtfnBadge(cte_a->cap);
if (badge == 0) {
return true;
}
return
(badge == cap_notification_cap_get_capNtfnBadge(cte_b->cap)) &&
!mdb_node_get_mdbFirstBadged(cte_b->cteMDBNode);
break;
}
default:
return true;
break;
}
}
exception_t
ensureNoChildren(cte_t *slot)
{
if (mdb_node_get_mdbNext(slot->cteMDBNode) != 0) {
cte_t *next;
next = CTE_PTR(mdb_node_get_mdbNext(slot->cteMDBNode));
if (isMDBParentOf(slot, next)) {
current_syscall_error.type = seL4_RevokeFirst;
return EXCEPTION_SYSCALL_ERROR;
}
}
return EXCEPTION_NONE;
}
exception_t
ensureEmptySlot(cte_t *slot)
{
if (cap_get_capType(slot->cap) != cap_null_cap) {
current_syscall_error.type = seL4_DeleteFirst;
return EXCEPTION_SYSCALL_ERROR;
}
return EXCEPTION_NONE;
}
bool_t PURE
isFinalCapability(cte_t *cte)
{
mdb_node_t mdb;
bool_t prevIsSameObject;
mdb = cte->cteMDBNode;
if (mdb_node_get_mdbPrev(mdb) == 0) {
prevIsSameObject = false;
} else {
cte_t *prev;
prev = CTE_PTR(mdb_node_get_mdbPrev(mdb));
prevIsSameObject = sameObjectAs(prev->cap, cte->cap);
}
if (prevIsSameObject) {
return false;
} else {
if (mdb_node_get_mdbNext(mdb) == 0) {
return true;
} else {
cte_t *next;
next = CTE_PTR(mdb_node_get_mdbNext(mdb));
return !sameObjectAs(cte->cap, next->cap);
}
}
}
bool_t PURE
slotCapLongRunningDelete(cte_t *slot)
{
if (cap_get_capType(slot->cap) == cap_null_cap) {
return false;
} else if (! isFinalCapability(slot)) {
return false;
}
switch (cap_get_capType(slot->cap)) {
case cap_thread_cap:
case cap_zombie_cap:
case cap_cnode_cap:
return true;
default:
return false;
}
}
cte_t *
getReceiveSlots(tcb_t *thread, word_t *buffer)
{
cap_transfer_t ct;
cptr_t cptr;
lookupCap_ret_t luc_ret;
lookupSlot_ret_t lus_ret;
cte_t *slot;
cap_t cnode;
if (!buffer) {
return NULL;
}
ct = loadCapTransfer(buffer);
cptr = ct.ctReceiveRoot;
luc_ret = lookupCap(thread, cptr);
if (luc_ret.status != EXCEPTION_NONE) {
return NULL;
}
cnode = luc_ret.cap;
lus_ret = lookupTargetSlot(cnode, ct.ctReceiveIndex, ct.ctReceiveDepth);
if (lus_ret.status != EXCEPTION_NONE) {
return NULL;
}
slot = lus_ret.slot;
if (cap_get_capType(slot->cap) != cap_null_cap) {
return NULL;
}
return slot;
}
cap_transfer_t PURE
loadCapTransfer(word_t *buffer)
{
const int offset = seL4_MsgMaxLength + seL4_MsgMaxExtraCaps + 2;
return capTransferFromWords(buffer + offset);
}