/* * Copyright 2014, General Dynamics C4 Systems * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include 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; /* Ensure operation is valid. */ if (invLabel != UntypedRetype) { userError("Untyped cap: Illegal operation attempted."); current_syscall_error.type = seL4_IllegalOperation; return EXCEPTION_SYSCALL_ERROR; } /* Ensure message length valid. */ if (length < 6 || excaps.excaprefs[0] == NULL) { userError("Untyped invocation: Truncated message."); current_syscall_error.type = seL4_TruncatedMessage; return EXCEPTION_SYSCALL_ERROR; } /* Fetch arguments. */ 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]; /* Is the requested object type valid? */ 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; } objectSize = getObjectSize(newType, userObjSize); /* Exclude impossibly large object sizes. getObjectSize can overflow if userObjSize is close to 2^wordBits, which is nonsensical in any case, so we check that this did not happen. userObjSize will always need to be less than wordBits. */ if (userObjSize >= wordBits || objectSize > seL4_MaxUntypedBits) { userError("Untyped Retype: Invalid object size."); current_syscall_error.type = seL4_RangeError; current_syscall_error.rangeErrorMin = 0; current_syscall_error.rangeErrorMax = seL4_MaxUntypedBits; return EXCEPTION_SYSCALL_ERROR; } /* If the target object is a CNode, is it at least size 1? */ 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 the target object is a Untyped, is it at least size 4? */ if (newType == seL4_UntypedObject && userObjSize < seL4_MinUntypedBits) { userError("Untyped Retype: Requested UntypedItem size too small."); current_syscall_error.type = seL4_InvalidArgument; current_syscall_error.invalidArgumentNumber = 1; return EXCEPTION_SYSCALL_ERROR; } #ifdef CONFIG_KERNEL_MCS if (newType == seL4_SchedContextObject && userObjSize < seL4_MinSchedContextBits) { userError("Untyped retype: Requested a scheduling context too small."); current_syscall_error.type = seL4_InvalidArgument; current_syscall_error.invalidArgumentNumber = 1; return EXCEPTION_SYSCALL_ERROR; } #endif /* Lookup the destination CNode (where our caps will be placed in). */ 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; } /* Is the destination actually a CNode? */ 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; } /* Is the region where the user wants to put the caps valid? */ nodeSize = 1ul << 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; } /* Ensure that the destination slots are all empty. */ 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; } } /* * Determine where in the Untyped region we should start allocating new * objects. * * If we have no children, we can start allocating from the beginning of * our untyped, regardless of what the "free" value in the cap states. * (This may happen if all of the objects beneath us got deleted). * * If we have children, we just keep allocating from the "free" value * recorded in the cap. */ 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); /* * Determine the maximum number of objects we can create, and return an * error if we don't have enough space. * * We don't need to worry about alignment in this case, because if anything * fits, it will also fit aligned up (by packing it on the right hand side * of the untyped). */ untypedFreeBytes = BIT(cap_untyped_cap_get_capBlockSize(cap)) - FREE_INDEX_TO_OFFSET(freeIndex); if ((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; } /* Align up the free region so that it is aligned to the target object's * size. */ alignedFreeRef = alignUp(freeRef, objectSize); /* Perform the retype. */ 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; } /** AUXUPD: "(True, typ_region_bytes (ptr_val \regionBase) (unat \block_size))" */ /** GHOSTUPD: "(True, gs_clear_region (ptr_val \regionBase) (unat \block_size))" */ 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; if (reset) { status = resetUntypedCap(srcSlot); if (status != EXCEPTION_NONE) { return status; } } /* Update the amount of free space left in this untyped cap. * * Note that userSize is not necessarily the true size of the object in * memory. In the case where newType is seL4_CapTableObject, the size is * transformed by getObjectSize. */ 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)); /* Create new objects and caps. */ createNewObjects(newType, srcSlot, destSlots, retypeBase, userSize, deviceMemory); return EXCEPTION_NONE; }