#include <config.h>
#include <types.h>
#include <api/failures.h>
#include <api/syscall.h>
#include <api/invocation.h>
#include <machine/io.h>
#include <object/structures.h>
#include <object/untyped.h>
#include <object/objecttype.h>
#include <object/cnode.h>
#include <kernel/cspace.h>
#include <kernel/thread.h>
#include <util.h>
static word_t
alignUp(word_t baseValue, word_t alignment)
{
return (baseValue + (BIT(alignment) - 1)) & ~MASK(alignment);
}
exception_t
decodeUntypedInvocation(word_t invLabel, word_t length, cte_t *slot,
cap_t cap, extra_caps_t excaps,
bool_t call, word_t *buffer)
{
word_t newType, userObjSize, nodeIndex;
word_t nodeDepth, nodeOffset, nodeWindow;
cte_t *rootSlot UNUSED;
exception_t status;
cap_t nodeCap;
lookupSlot_ret_t lu_ret;
word_t nodeSize;
word_t i;
slot_range_t slots;
word_t freeRef, alignedFreeRef, objectSize, untypedFreeBytes;
word_t freeIndex;
bool_t deviceMemory;
bool_t reset;
if (invLabel != UntypedRetype) {
userError("Untyped cap: Illegal operation attempted.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (length < 6 || excaps.excaprefs[0] == NULL) {
userError("Untyped invocation: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
newType = getSyscallArg(0, buffer);
userObjSize = getSyscallArg(1, buffer);
nodeIndex = getSyscallArg(2, buffer);
nodeDepth = getSyscallArg(3, buffer);
nodeOffset = getSyscallArg(4, buffer);
nodeWindow = getSyscallArg(5, buffer);
rootSlot = excaps.excaprefs[0];
if (newType >= seL4_ObjectTypeCount) {
userError("Untyped Retype: Invalid object type.");
current_syscall_error.type = seL4_InvalidArgument;
current_syscall_error.invalidArgumentNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
if (userObjSize > seL4_MaxUntypedBits) {
userError("Untyped Retype: Invalid object size.");
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = wordBits - 3;
return EXCEPTION_SYSCALL_ERROR;
}
if (newType == seL4_CapTableObject && userObjSize == 0) {
userError("Untyped Retype: Requested CapTable size too small.");
current_syscall_error.type = seL4_InvalidArgument;
current_syscall_error.invalidArgumentNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
if (newType == seL4_UntypedObject && userObjSize < 4) {
userError("Untyped Retype: Requested UntypedItem size too small.");
current_syscall_error.type = seL4_InvalidArgument;
current_syscall_error.invalidArgumentNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
if (nodeDepth == 0) {
nodeCap = excaps.excaprefs[0]->cap;
} else {
cap_t rootCap = excaps.excaprefs[0]->cap;
lu_ret = lookupTargetSlot(rootCap, nodeIndex, nodeDepth);
if (lu_ret.status != EXCEPTION_NONE) {
userError("Untyped Retype: Invalid destination address.");
return lu_ret.status;
}
nodeCap = lu_ret.slot->cap;
}
if (cap_get_capType(nodeCap) != cap_cnode_cap) {
userError("Untyped Retype: Destination cap invalid or read-only.");
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = 0;
current_lookup_fault = lookup_fault_missing_capability_new(nodeDepth);
return EXCEPTION_SYSCALL_ERROR;
}
nodeSize = 1 << cap_cnode_cap_get_capCNodeRadix(nodeCap);
if (nodeOffset > nodeSize - 1) {
userError("Untyped Retype: Destination node offset #%d too large.",
(int)nodeOffset);
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = nodeSize - 1;
return EXCEPTION_SYSCALL_ERROR;
}
if (nodeWindow < 1 || nodeWindow > CONFIG_RETYPE_FAN_OUT_LIMIT) {
userError("Untyped Retype: Number of requested objects (%d) too small or large.",
(int)nodeWindow);
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 1;
current_syscall_error.rangeErrorMax = CONFIG_RETYPE_FAN_OUT_LIMIT;
return EXCEPTION_SYSCALL_ERROR;
}
if (nodeWindow > nodeSize - nodeOffset) {
userError("Untyped Retype: Requested destination window overruns size of node.");
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 1;
current_syscall_error.rangeErrorMax = nodeSize - nodeOffset;
return EXCEPTION_SYSCALL_ERROR;
}
slots.cnode = CTE_PTR(cap_cnode_cap_get_capCNodePtr(nodeCap));
slots.offset = nodeOffset;
slots.length = nodeWindow;
for (i = nodeOffset; i < nodeOffset + nodeWindow; i++) {
status = ensureEmptySlot(slots.cnode + i);
if (status != EXCEPTION_NONE) {
userError("Untyped Retype: Slot #%d in destination window non-empty.",
(int)i);
return status;
}
}
status = ensureNoChildren(slot);
if (status != EXCEPTION_NONE) {
freeIndex = cap_untyped_cap_get_capFreeIndex(cap);
reset = false;
} else {
freeIndex = 0;
reset = true;
}
freeRef = GET_FREE_REF(cap_untyped_cap_get_capPtr(cap), freeIndex);
objectSize = getObjectSize(newType, userObjSize);
untypedFreeBytes = BIT(cap_untyped_cap_get_capBlockSize(cap)) -
FREE_INDEX_TO_OFFSET(freeIndex);
if (objectSize >= wordBits || (untypedFreeBytes >> objectSize) < nodeWindow) {
userError("Untyped Retype: Insufficient memory "
"(%lu * %lu bytes needed, %lu bytes available).",
(word_t)nodeWindow,
(objectSize >= wordBits ? -1 : (1ul << objectSize)),
(word_t)(untypedFreeBytes));
current_syscall_error.type = seL4_NotEnoughMemory;
current_syscall_error.memoryLeft = untypedFreeBytes;
return EXCEPTION_SYSCALL_ERROR;
}
deviceMemory = cap_untyped_cap_get_capIsDevice(cap);
if ((deviceMemory && !Arch_isFrameType(newType))
&& newType != seL4_UntypedObject) {
userError("Untyped Retype: Creating kernel objects with device untyped");
current_syscall_error.type = seL4_InvalidArgument;
current_syscall_error.invalidArgumentNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
alignedFreeRef = alignUp(freeRef, objectSize);
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeUntyped_Retype(slot, reset,
(void*)alignedFreeRef, newType, userObjSize,
slots, deviceMemory);
}
static exception_t
resetUntypedCap(cte_t *srcSlot)
{
cap_t prev_cap = srcSlot->cap;
word_t block_size = cap_untyped_cap_get_capBlockSize(prev_cap);
void *regionBase = WORD_PTR(cap_untyped_cap_get_capPtr(prev_cap));
int chunk = CONFIG_RESET_CHUNK_BITS;
word_t offset = FREE_INDEX_TO_OFFSET(cap_untyped_cap_get_capFreeIndex(prev_cap));
exception_t status;
bool_t deviceMemory = cap_untyped_cap_get_capIsDevice(prev_cap);
if (offset == 0) {
return EXCEPTION_NONE;
}
if (deviceMemory || block_size < chunk) {
if (! deviceMemory) {
clearMemory(regionBase, block_size);
}
srcSlot->cap = cap_untyped_cap_set_capFreeIndex(prev_cap, 0);
} else {
for (offset = ROUND_DOWN(offset - 1, chunk);
offset != - BIT (chunk); offset -= BIT (chunk)) {
clearMemory(GET_OFFSET_FREE_PTR(regionBase, offset), chunk);
srcSlot->cap = cap_untyped_cap_set_capFreeIndex(prev_cap, OFFSET_TO_FREE_INDEX(offset));
status = preemptionPoint();
if (status != EXCEPTION_NONE) {
return status;
}
}
}
return EXCEPTION_NONE;
}
exception_t
invokeUntyped_Retype(cte_t *srcSlot,
bool_t reset, void* retypeBase,
object_t newType, word_t userSize,
slot_range_t destSlots, bool_t deviceMemory)
{
word_t freeRef;
word_t totalObjectSize;
void *regionBase = WORD_PTR(cap_untyped_cap_get_capPtr(srcSlot->cap));
exception_t status;
freeRef = GET_FREE_REF(regionBase, cap_untyped_cap_get_capFreeIndex(srcSlot->cap));
if (reset) {
status = resetUntypedCap(srcSlot);
if (status != EXCEPTION_NONE) {
return status;
}
}
totalObjectSize = destSlots.length << getObjectSize(newType, userSize);
freeRef = (word_t)retypeBase + totalObjectSize;
srcSlot->cap = cap_untyped_cap_set_capFreeIndex(srcSlot->cap,
GET_FREE_INDEX(regionBase, freeRef));
createNewObjects(newType, srcSlot, destSlots, retypeBase, userSize,
deviceMemory);
return EXCEPTION_NONE;
}