#include <assert.h>
#include <config.h>
#include <types.h>
#include <api/failures.h>
#include <api/syscall.h>
#include <arch/object/objecttype.h>
#include <machine/io.h>
#include <object/objecttype.h>
#include <object/structures.h>
#include <object/notification.h>
#include <object/endpoint.h>
#include <object/cnode.h>
#include <object/interrupt.h>
#include <object/tcb.h>
#include <object/untyped.h>
#include <model/statedata.h>
#include <kernel/thread.h>
#include <kernel/vspace.h>
#include <machine.h>
#include <util.h>
#include <string.h>
word_t getObjectSize(word_t t, word_t userObjSize)
{
if (t >= seL4_NonArchObjectTypeCount) {
return Arch_getObjectSize(t);
} else {
switch (t) {
case seL4_TCBObject:
return seL4_TCBBits;
case seL4_EndpointObject:
return seL4_EndpointBits;
case seL4_NotificationObject:
return seL4_NotificationBits;
case seL4_CapTableObject:
return seL4_SlotBits + userObjSize;
case seL4_UntypedObject:
return userObjSize;
default:
fail("Invalid object type");
return 0;
}
}
}
deriveCap_ret_t
deriveCap(cte_t *slot, cap_t cap)
{
deriveCap_ret_t ret;
if (isArchCap(cap)) {
return Arch_deriveCap(slot, cap);
}
switch (cap_get_capType(cap)) {
case cap_zombie_cap:
ret.status = EXCEPTION_NONE;
ret.cap = cap_null_cap_new();
break;
case cap_irq_control_cap:
ret.status = EXCEPTION_NONE;
ret.cap = cap_null_cap_new();
break;
case cap_untyped_cap:
ret.status = ensureNoChildren(slot);
if (ret.status != EXCEPTION_NONE) {
ret.cap = cap_null_cap_new();
} else {
ret.cap = cap;
}
break;
case cap_reply_cap:
ret.status = EXCEPTION_NONE;
ret.cap = cap_null_cap_new();
break;
default:
ret.status = EXCEPTION_NONE;
ret.cap = cap;
}
return ret;
}
finaliseCap_ret_t
finaliseCap(cap_t cap, bool_t final, bool_t exposed)
{
finaliseCap_ret_t fc_ret;
if (isArchCap(cap)) {
fc_ret.remainder = Arch_finaliseCap(cap, final);
fc_ret.irq = irqInvalid;
return fc_ret;
}
switch (cap_get_capType(cap)) {
case cap_endpoint_cap:
if (final) {
cancelAllIPC(EP_PTR(cap_endpoint_cap_get_capEPPtr(cap)));
}
fc_ret.remainder = cap_null_cap_new();
fc_ret.irq = irqInvalid;
return fc_ret;
case cap_notification_cap:
if (final) {
notification_t *ntfn = NTFN_PTR(cap_notification_cap_get_capNtfnPtr(cap));
unbindMaybeNotification(ntfn);
cancelAllSignals(ntfn);
}
fc_ret.remainder = cap_null_cap_new();
fc_ret.irq = irqInvalid;
return fc_ret;
case cap_reply_cap:
case cap_null_cap:
case cap_domain_cap:
fc_ret.remainder = cap_null_cap_new();
fc_ret.irq = irqInvalid;
return fc_ret;
}
if (exposed) {
fail("finaliseCap: failed to finalise immediately.");
}
switch (cap_get_capType(cap)) {
case cap_cnode_cap: {
if (final) {
fc_ret.remainder =
Zombie_new(
1 << cap_cnode_cap_get_capCNodeRadix(cap),
cap_cnode_cap_get_capCNodeRadix(cap),
cap_cnode_cap_get_capCNodePtr(cap)
);
fc_ret.irq = irqInvalid;
return fc_ret;
}
break;
}
case cap_thread_cap: {
if (final) {
tcb_t *tcb;
cte_t *cte_ptr;
tcb = TCB_PTR(cap_thread_cap_get_capTCBPtr(cap));
SMP_COND_STATEMENT(remoteTCBStall(tcb);)
cte_ptr = TCB_PTR_CTE_PTR(tcb, tcbCTable);
unbindNotification(tcb);
suspend(tcb);
Arch_prepareThreadDelete(tcb);
fc_ret.remainder =
Zombie_new(
tcbArchCNodeEntries,
ZombieType_ZombieTCB,
CTE_REF(cte_ptr)
);
fc_ret.irq = irqInvalid;
return fc_ret;
}
break;
}
case cap_zombie_cap:
fc_ret.remainder = cap;
fc_ret.irq = irqInvalid;
return fc_ret;
case cap_irq_handler_cap:
if (final) {
irq_t irq = cap_irq_handler_cap_get_capIRQ(cap);
deletingIRQHandler(irq);
fc_ret.remainder = cap_null_cap_new();
fc_ret.irq = irq;
return fc_ret;
}
break;
}
fc_ret.remainder = cap_null_cap_new();
fc_ret.irq = irqInvalid;
return fc_ret;
}
bool_t CONST
hasCancelSendRights(cap_t cap)
{
switch (cap_get_capType(cap)) {
case cap_endpoint_cap:
return cap_endpoint_cap_get_capCanSend(cap) &&
cap_endpoint_cap_get_capCanReceive(cap) &&
cap_endpoint_cap_get_capCanGrant(cap);
default:
return false;
}
}
bool_t CONST
sameRegionAs(cap_t cap_a, cap_t cap_b)
{
switch (cap_get_capType(cap_a)) {
case cap_untyped_cap:
if (cap_get_capIsPhysical(cap_b)) {
word_t aBase, bBase, aTop, bTop;
aBase = (word_t)WORD_PTR(cap_untyped_cap_get_capPtr(cap_a));
bBase = (word_t)cap_get_capPtr(cap_b);
aTop = aBase + MASK(cap_untyped_cap_get_capBlockSize(cap_a));
bTop = bBase + MASK(cap_get_capSizeBits(cap_b));
return (aBase <= bBase) && (bTop <= aTop) && (bBase <= bTop);
}
break;
case cap_endpoint_cap:
if (cap_get_capType(cap_b) == cap_endpoint_cap) {
return cap_endpoint_cap_get_capEPPtr(cap_a) ==
cap_endpoint_cap_get_capEPPtr(cap_b);
}
break;
case cap_notification_cap:
if (cap_get_capType(cap_b) == cap_notification_cap) {
return cap_notification_cap_get_capNtfnPtr(cap_a) ==
cap_notification_cap_get_capNtfnPtr(cap_b);
}
break;
case cap_cnode_cap:
if (cap_get_capType(cap_b) == cap_cnode_cap) {
return (cap_cnode_cap_get_capCNodePtr(cap_a) ==
cap_cnode_cap_get_capCNodePtr(cap_b)) &&
(cap_cnode_cap_get_capCNodeRadix(cap_a) ==
cap_cnode_cap_get_capCNodeRadix(cap_b));
}
break;
case cap_thread_cap:
if (cap_get_capType(cap_b) == cap_thread_cap) {
return cap_thread_cap_get_capTCBPtr(cap_a) ==
cap_thread_cap_get_capTCBPtr(cap_b);
}
break;
case cap_reply_cap:
if (cap_get_capType(cap_b) == cap_reply_cap) {
return cap_reply_cap_get_capTCBPtr(cap_a) ==
cap_reply_cap_get_capTCBPtr(cap_b);
}
break;
case cap_domain_cap:
if (cap_get_capType(cap_b) == cap_domain_cap) {
return true;
}
break;
case cap_irq_control_cap:
if (cap_get_capType(cap_b) == cap_irq_control_cap ||
cap_get_capType(cap_b) == cap_irq_handler_cap) {
return true;
}
break;
case cap_irq_handler_cap:
if (cap_get_capType(cap_b) == cap_irq_handler_cap) {
return (irq_t)cap_irq_handler_cap_get_capIRQ(cap_a) ==
(irq_t)cap_irq_handler_cap_get_capIRQ(cap_b);
}
break;
default:
if (isArchCap(cap_a) &&
isArchCap(cap_b)) {
return Arch_sameRegionAs(cap_a, cap_b);
}
break;
}
return false;
}
bool_t CONST
sameObjectAs(cap_t cap_a, cap_t cap_b)
{
if (cap_get_capType(cap_a) == cap_untyped_cap) {
return false;
}
if (cap_get_capType(cap_a) == cap_irq_control_cap &&
cap_get_capType(cap_b) == cap_irq_handler_cap) {
return false;
}
if (isArchCap(cap_a) && isArchCap(cap_b)) {
return Arch_sameObjectAs(cap_a, cap_b);
}
return sameRegionAs(cap_a, cap_b);
}
cap_t CONST
updateCapData(bool_t preserve, word_t newData, cap_t cap)
{
if (isArchCap(cap)) {
return Arch_updateCapData(preserve, newData, cap);
}
switch (cap_get_capType(cap)) {
case cap_endpoint_cap:
if (!preserve && cap_endpoint_cap_get_capEPBadge(cap) == 0) {
return cap_endpoint_cap_set_capEPBadge(cap, newData);
} else {
return cap_null_cap_new();
}
case cap_notification_cap:
if (!preserve && cap_notification_cap_get_capNtfnBadge(cap) == 0) {
return cap_notification_cap_set_capNtfnBadge(cap, newData);
} else {
return cap_null_cap_new();
}
case cap_cnode_cap: {
word_t guard, guardSize;
cnode_capdata_t w = { .words = { newData } };
guardSize = cnode_capdata_get_guardSize(w);
if (guardSize + cap_cnode_cap_get_capCNodeRadix(cap) > wordBits) {
return cap_null_cap_new();
} else {
cap_t new_cap;
guard = cnode_capdata_get_guard(w) & MASK(guardSize);
new_cap = cap_cnode_cap_set_capCNodeGuard(cap, guard);
new_cap = cap_cnode_cap_set_capCNodeGuardSize(new_cap,
guardSize);
return new_cap;
}
}
default:
return cap;
}
}
cap_t CONST
maskCapRights(seL4_CapRights_t cap_rights, cap_t cap)
{
if (isArchCap(cap)) {
return Arch_maskCapRights(cap_rights, cap);
}
switch (cap_get_capType(cap)) {
case cap_null_cap:
case cap_domain_cap:
case cap_cnode_cap:
case cap_untyped_cap:
case cap_reply_cap:
case cap_irq_control_cap:
case cap_irq_handler_cap:
case cap_zombie_cap:
case cap_thread_cap:
return cap;
case cap_endpoint_cap: {
cap_t new_cap;
new_cap = cap_endpoint_cap_set_capCanSend(
cap, cap_endpoint_cap_get_capCanSend(cap) &
seL4_CapRights_get_capAllowWrite(cap_rights));
new_cap = cap_endpoint_cap_set_capCanReceive(
new_cap, cap_endpoint_cap_get_capCanReceive(cap) &
seL4_CapRights_get_capAllowRead(cap_rights));
new_cap = cap_endpoint_cap_set_capCanGrant(
new_cap, cap_endpoint_cap_get_capCanGrant(cap) &
seL4_CapRights_get_capAllowGrant(cap_rights));
return new_cap;
}
case cap_notification_cap: {
cap_t new_cap;
new_cap = cap_notification_cap_set_capNtfnCanSend(
cap, cap_notification_cap_get_capNtfnCanSend(cap) &
seL4_CapRights_get_capAllowWrite(cap_rights));
new_cap = cap_notification_cap_set_capNtfnCanReceive(new_cap,
cap_notification_cap_get_capNtfnCanReceive(cap) &
seL4_CapRights_get_capAllowRead(cap_rights));
return new_cap;
}
default:
fail("Invalid cap type");
}
}
cap_t
createObject(object_t t, void *regionBase, word_t userSize, bool_t deviceMemory)
{
if (t >= (object_t) seL4_NonArchObjectTypeCount) {
return Arch_createObject(t, regionBase, userSize, deviceMemory);
}
switch ((api_object_t)t) {
case seL4_TCBObject: {
tcb_t *tcb;
tcb = TCB_PTR((word_t)regionBase + TCB_OFFSET);
Arch_initContext(&tcb->tcbArch.tcbContext);
tcb->tcbTimeSlice = CONFIG_TIME_SLICE;
tcb->tcbDomain = ksCurDomain;
SMP_COND_STATEMENT(tcb->tcbAffinity = getCurrentCPUIndex());
#ifdef CONFIG_DEBUG_BUILD
strlcpy(tcb->tcbName, "child of: '", TCB_NAME_LENGTH);
strlcat(tcb->tcbName, NODE_STATE(ksCurThread)->tcbName, TCB_NAME_LENGTH);
strlcat(tcb->tcbName, "'", TCB_NAME_LENGTH);
#endif
return cap_thread_cap_new(TCB_REF(tcb));
}
case seL4_EndpointObject:
return cap_endpoint_cap_new(0, true, true, true,
EP_REF(regionBase));
case seL4_NotificationObject:
return cap_notification_cap_new(0, true, true,
NTFN_REF(regionBase));
case seL4_CapTableObject:
return cap_cnode_cap_new(userSize, 0, 0, CTE_REF(regionBase));
case seL4_UntypedObject:
return cap_untyped_cap_new(0, !!deviceMemory, userSize, WORD_REF(regionBase));
default:
fail("Invalid object type");
}
}
void
createNewObjects(object_t t, cte_t *parent, slot_range_t slots,
void *regionBase, word_t userSize, bool_t deviceMemory)
{
word_t objectSize;
void *nextFreeArea;
word_t i;
word_t totalObjectSize UNUSED;
objectSize = getObjectSize(t, userSize);
totalObjectSize = slots.length << objectSize;
nextFreeArea = regionBase;
for (i = 0; i < slots.length; i++) {
cap_t cap = createObject(t, (void *)((word_t)nextFreeArea + (i << objectSize)), userSize, deviceMemory);
insertNewCap(parent, &slots.cnode[slots.offset + i], cap);
}
}
exception_t
decodeInvocation(word_t invLabel, word_t length,
cptr_t capIndex, cte_t *slot, cap_t cap,
extra_caps_t excaps, bool_t block, bool_t call,
word_t *buffer)
{
if (isArchCap(cap)) {
return Arch_decodeInvocation(invLabel, length, capIndex,
slot, cap, excaps, buffer);
}
switch (cap_get_capType(cap)) {
case cap_null_cap:
userError("Attempted to invoke a null cap #%lu.", capIndex);
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
case cap_zombie_cap:
userError("Attempted to invoke a zombie cap #%lu.", capIndex);
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
case cap_endpoint_cap:
if (unlikely(!cap_endpoint_cap_get_capCanSend(cap))) {
userError("Attempted to invoke a read-only endpoint cap #%lu.",
capIndex);
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return performInvocation_Endpoint(
EP_PTR(cap_endpoint_cap_get_capEPPtr(cap)),
cap_endpoint_cap_get_capEPBadge(cap),
cap_endpoint_cap_get_capCanGrant(cap), block, call);
case cap_notification_cap: {
if (unlikely(!cap_notification_cap_get_capNtfnCanSend(cap))) {
userError("Attempted to invoke a read-only notification cap #%lu.",
capIndex);
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return performInvocation_Notification(
NTFN_PTR(cap_notification_cap_get_capNtfnPtr(cap)),
cap_notification_cap_get_capNtfnBadge(cap));
}
case cap_reply_cap:
if (unlikely(cap_reply_cap_get_capReplyMaster(cap))) {
userError("Attempted to invoke an invalid reply cap #%lu.",
capIndex);
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return performInvocation_Reply(
TCB_PTR(cap_reply_cap_get_capTCBPtr(cap)), slot);
case cap_thread_cap:
return decodeTCBInvocation(invLabel, length, cap,
slot, excaps, call, buffer);
case cap_domain_cap:
return decodeDomainInvocation(invLabel, length, excaps, buffer);
case cap_cnode_cap:
return decodeCNodeInvocation(invLabel, length, cap, excaps, buffer);
case cap_untyped_cap:
return decodeUntypedInvocation(invLabel, length, slot, cap, excaps,
call, buffer);
case cap_irq_control_cap:
return decodeIRQControlInvocation(invLabel, length, slot,
excaps, buffer);
case cap_irq_handler_cap:
return decodeIRQHandlerInvocation(invLabel,
cap_irq_handler_cap_get_capIRQ(cap), excaps);
default:
fail("Invalid cap type");
}
}
exception_t
performInvocation_Endpoint(endpoint_t *ep, word_t badge,
bool_t canGrant, bool_t block,
bool_t call)
{
sendIPC(block, call, badge, canGrant, NODE_STATE(ksCurThread), ep);
return EXCEPTION_NONE;
}
exception_t
performInvocation_Notification(notification_t *ntfn, word_t badge)
{
sendSignal(ntfn, badge);
return EXCEPTION_NONE;
}
exception_t
performInvocation_Reply(tcb_t *thread, cte_t *slot)
{
doReplyTransfer(NODE_STATE(ksCurThread), thread, slot);
return EXCEPTION_NONE;
}