1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7#include <config.h> 8#include <types.h> 9#include <api/failures.h> 10#include <api/syscall.h> 11#include <api/invocation.h> 12#include <machine/io.h> 13#include <object/structures.h> 14#include <object/untyped.h> 15#include <object/objecttype.h> 16#include <object/cnode.h> 17#include <kernel/cspace.h> 18#include <kernel/thread.h> 19#include <util.h> 20 21static word_t alignUp(word_t baseValue, word_t alignment) 22{ 23 return (baseValue + (BIT(alignment) - 1)) & ~MASK(alignment); 24} 25 26exception_t decodeUntypedInvocation(word_t invLabel, word_t length, cte_t *slot, 27 cap_t cap, extra_caps_t excaps, 28 bool_t call, word_t *buffer) 29{ 30 word_t newType, userObjSize, nodeIndex; 31 word_t nodeDepth, nodeOffset, nodeWindow; 32 cte_t *rootSlot UNUSED; 33 exception_t status; 34 cap_t nodeCap; 35 lookupSlot_ret_t lu_ret; 36 word_t nodeSize; 37 word_t i; 38 slot_range_t slots; 39 word_t freeRef, alignedFreeRef, objectSize, untypedFreeBytes; 40 word_t freeIndex; 41 bool_t deviceMemory; 42 bool_t reset; 43 44 /* Ensure operation is valid. */ 45 if (invLabel != UntypedRetype) { 46 userError("Untyped cap: Illegal operation attempted."); 47 current_syscall_error.type = seL4_IllegalOperation; 48 return EXCEPTION_SYSCALL_ERROR; 49 } 50 51 /* Ensure message length valid. */ 52 if (length < 6 || excaps.excaprefs[0] == NULL) { 53 userError("Untyped invocation: Truncated message."); 54 current_syscall_error.type = seL4_TruncatedMessage; 55 return EXCEPTION_SYSCALL_ERROR; 56 } 57 58 /* Fetch arguments. */ 59 newType = getSyscallArg(0, buffer); 60 userObjSize = getSyscallArg(1, buffer); 61 nodeIndex = getSyscallArg(2, buffer); 62 nodeDepth = getSyscallArg(3, buffer); 63 nodeOffset = getSyscallArg(4, buffer); 64 nodeWindow = getSyscallArg(5, buffer); 65 66 rootSlot = excaps.excaprefs[0]; 67 68 /* Is the requested object type valid? */ 69 if (newType >= seL4_ObjectTypeCount) { 70 userError("Untyped Retype: Invalid object type."); 71 current_syscall_error.type = seL4_InvalidArgument; 72 current_syscall_error.invalidArgumentNumber = 0; 73 return EXCEPTION_SYSCALL_ERROR; 74 } 75 76 objectSize = getObjectSize(newType, userObjSize); 77 78 /* Exclude impossibly large object sizes. getObjectSize can overflow if userObjSize 79 is close to 2^wordBits, which is nonsensical in any case, so we check that this 80 did not happen. userObjSize will always need to be less than wordBits. */ 81 if (userObjSize >= wordBits || objectSize > seL4_MaxUntypedBits) { 82 userError("Untyped Retype: Invalid object size."); 83 current_syscall_error.type = seL4_RangeError; 84 current_syscall_error.rangeErrorMin = 0; 85 current_syscall_error.rangeErrorMax = seL4_MaxUntypedBits; 86 return EXCEPTION_SYSCALL_ERROR; 87 } 88 89 /* If the target object is a CNode, is it at least size 1? */ 90 if (newType == seL4_CapTableObject && userObjSize == 0) { 91 userError("Untyped Retype: Requested CapTable size too small."); 92 current_syscall_error.type = seL4_InvalidArgument; 93 current_syscall_error.invalidArgumentNumber = 1; 94 return EXCEPTION_SYSCALL_ERROR; 95 } 96 97 /* If the target object is a Untyped, is it at least size 4? */ 98 if (newType == seL4_UntypedObject && userObjSize < seL4_MinUntypedBits) { 99 userError("Untyped Retype: Requested UntypedItem size too small."); 100 current_syscall_error.type = seL4_InvalidArgument; 101 current_syscall_error.invalidArgumentNumber = 1; 102 return EXCEPTION_SYSCALL_ERROR; 103 } 104 105#ifdef CONFIG_KERNEL_MCS 106 if (newType == seL4_SchedContextObject && userObjSize < seL4_MinSchedContextBits) { 107 userError("Untyped retype: Requested a scheduling context too small."); 108 current_syscall_error.type = seL4_InvalidArgument; 109 current_syscall_error.invalidArgumentNumber = 1; 110 return EXCEPTION_SYSCALL_ERROR; 111 } 112#endif 113 114 /* Lookup the destination CNode (where our caps will be placed in). */ 115 if (nodeDepth == 0) { 116 nodeCap = excaps.excaprefs[0]->cap; 117 } else { 118 cap_t rootCap = excaps.excaprefs[0]->cap; 119 lu_ret = lookupTargetSlot(rootCap, nodeIndex, nodeDepth); 120 if (lu_ret.status != EXCEPTION_NONE) { 121 userError("Untyped Retype: Invalid destination address."); 122 return lu_ret.status; 123 } 124 nodeCap = lu_ret.slot->cap; 125 } 126 127 /* Is the destination actually a CNode? */ 128 if (cap_get_capType(nodeCap) != cap_cnode_cap) { 129 userError("Untyped Retype: Destination cap invalid or read-only."); 130 current_syscall_error.type = seL4_FailedLookup; 131 current_syscall_error.failedLookupWasSource = 0; 132 current_lookup_fault = lookup_fault_missing_capability_new(nodeDepth); 133 return EXCEPTION_SYSCALL_ERROR; 134 } 135 136 /* Is the region where the user wants to put the caps valid? */ 137 nodeSize = 1ul << cap_cnode_cap_get_capCNodeRadix(nodeCap); 138 if (nodeOffset > nodeSize - 1) { 139 userError("Untyped Retype: Destination node offset #%d too large.", 140 (int)nodeOffset); 141 current_syscall_error.type = seL4_RangeError; 142 current_syscall_error.rangeErrorMin = 0; 143 current_syscall_error.rangeErrorMax = nodeSize - 1; 144 return EXCEPTION_SYSCALL_ERROR; 145 } 146 if (nodeWindow < 1 || nodeWindow > CONFIG_RETYPE_FAN_OUT_LIMIT) { 147 userError("Untyped Retype: Number of requested objects (%d) too small or large.", 148 (int)nodeWindow); 149 current_syscall_error.type = seL4_RangeError; 150 current_syscall_error.rangeErrorMin = 1; 151 current_syscall_error.rangeErrorMax = CONFIG_RETYPE_FAN_OUT_LIMIT; 152 return EXCEPTION_SYSCALL_ERROR; 153 } 154 if (nodeWindow > nodeSize - nodeOffset) { 155 userError("Untyped Retype: Requested destination window overruns size of node."); 156 current_syscall_error.type = seL4_RangeError; 157 current_syscall_error.rangeErrorMin = 1; 158 current_syscall_error.rangeErrorMax = nodeSize - nodeOffset; 159 return EXCEPTION_SYSCALL_ERROR; 160 } 161 162 /* Ensure that the destination slots are all empty. */ 163 slots.cnode = CTE_PTR(cap_cnode_cap_get_capCNodePtr(nodeCap)); 164 slots.offset = nodeOffset; 165 slots.length = nodeWindow; 166 for (i = nodeOffset; i < nodeOffset + nodeWindow; i++) { 167 status = ensureEmptySlot(slots.cnode + i); 168 if (status != EXCEPTION_NONE) { 169 userError("Untyped Retype: Slot #%d in destination window non-empty.", 170 (int)i); 171 return status; 172 } 173 } 174 175 /* 176 * Determine where in the Untyped region we should start allocating new 177 * objects. 178 * 179 * If we have no children, we can start allocating from the beginning of 180 * our untyped, regardless of what the "free" value in the cap states. 181 * (This may happen if all of the objects beneath us got deleted). 182 * 183 * If we have children, we just keep allocating from the "free" value 184 * recorded in the cap. 185 */ 186 status = ensureNoChildren(slot); 187 if (status != EXCEPTION_NONE) { 188 freeIndex = cap_untyped_cap_get_capFreeIndex(cap); 189 reset = false; 190 } else { 191 freeIndex = 0; 192 reset = true; 193 } 194 freeRef = GET_FREE_REF(cap_untyped_cap_get_capPtr(cap), freeIndex); 195 196 /* 197 * Determine the maximum number of objects we can create, and return an 198 * error if we don't have enough space. 199 * 200 * We don't need to worry about alignment in this case, because if anything 201 * fits, it will also fit aligned up (by packing it on the right hand side 202 * of the untyped). 203 */ 204 untypedFreeBytes = BIT(cap_untyped_cap_get_capBlockSize(cap)) - 205 FREE_INDEX_TO_OFFSET(freeIndex); 206 207 if ((untypedFreeBytes >> objectSize) < nodeWindow) { 208 userError("Untyped Retype: Insufficient memory " 209 "(%lu * %lu bytes needed, %lu bytes available).", 210 (word_t)nodeWindow, 211 (objectSize >= wordBits ? -1 : (1ul << objectSize)), 212 (word_t)(untypedFreeBytes)); 213 current_syscall_error.type = seL4_NotEnoughMemory; 214 current_syscall_error.memoryLeft = untypedFreeBytes; 215 return EXCEPTION_SYSCALL_ERROR; 216 } 217 218 deviceMemory = cap_untyped_cap_get_capIsDevice(cap); 219 if ((deviceMemory && !Arch_isFrameType(newType)) 220 && newType != seL4_UntypedObject) { 221 userError("Untyped Retype: Creating kernel objects with device untyped"); 222 current_syscall_error.type = seL4_InvalidArgument; 223 current_syscall_error.invalidArgumentNumber = 1; 224 return EXCEPTION_SYSCALL_ERROR; 225 } 226 227 /* Align up the free region so that it is aligned to the target object's 228 * size. */ 229 alignedFreeRef = alignUp(freeRef, objectSize); 230 231 /* Perform the retype. */ 232 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart); 233 return invokeUntyped_Retype(slot, reset, 234 (void *)alignedFreeRef, newType, userObjSize, 235 slots, deviceMemory); 236} 237 238static exception_t resetUntypedCap(cte_t *srcSlot) 239{ 240 cap_t prev_cap = srcSlot->cap; 241 word_t block_size = cap_untyped_cap_get_capBlockSize(prev_cap); 242 void *regionBase = WORD_PTR(cap_untyped_cap_get_capPtr(prev_cap)); 243 int chunk = CONFIG_RESET_CHUNK_BITS; 244 word_t offset = FREE_INDEX_TO_OFFSET(cap_untyped_cap_get_capFreeIndex(prev_cap)); 245 exception_t status; 246 bool_t deviceMemory = cap_untyped_cap_get_capIsDevice(prev_cap); 247 248 if (offset == 0) { 249 return EXCEPTION_NONE; 250 } 251 252 /** AUXUPD: "(True, typ_region_bytes (ptr_val \<acute>regionBase) 253 (unat \<acute>block_size))" */ 254 /** GHOSTUPD: "(True, gs_clear_region (ptr_val \<acute>regionBase) 255 (unat \<acute>block_size))" */ 256 257 if (deviceMemory || block_size < chunk) { 258 if (! deviceMemory) { 259 clearMemory(regionBase, block_size); 260 } 261 srcSlot->cap = cap_untyped_cap_set_capFreeIndex(prev_cap, 0); 262 } else { 263 for (offset = ROUND_DOWN(offset - 1, chunk); 264 offset != - BIT(chunk); offset -= BIT(chunk)) { 265 clearMemory(GET_OFFSET_FREE_PTR(regionBase, offset), chunk); 266 srcSlot->cap = cap_untyped_cap_set_capFreeIndex(prev_cap, OFFSET_TO_FREE_INDEX(offset)); 267 status = preemptionPoint(); 268 if (status != EXCEPTION_NONE) { 269 return status; 270 } 271 } 272 } 273 return EXCEPTION_NONE; 274} 275 276exception_t invokeUntyped_Retype(cte_t *srcSlot, 277 bool_t reset, void *retypeBase, 278 object_t newType, word_t userSize, 279 slot_range_t destSlots, bool_t deviceMemory) 280{ 281 word_t freeRef; 282 word_t totalObjectSize; 283 void *regionBase = WORD_PTR(cap_untyped_cap_get_capPtr(srcSlot->cap)); 284 exception_t status; 285 286 if (reset) { 287 status = resetUntypedCap(srcSlot); 288 if (status != EXCEPTION_NONE) { 289 return status; 290 } 291 } 292 293 /* Update the amount of free space left in this untyped cap. 294 * 295 * Note that userSize is not necessarily the true size of the object in 296 * memory. In the case where newType is seL4_CapTableObject, the size is 297 * transformed by getObjectSize. */ 298 totalObjectSize = destSlots.length << getObjectSize(newType, userSize); 299 freeRef = (word_t)retypeBase + totalObjectSize; 300 srcSlot->cap = cap_untyped_cap_set_capFreeIndex(srcSlot->cap, 301 GET_FREE_INDEX(regionBase, freeRef)); 302 303 /* Create new objects and caps. */ 304 createNewObjects(newType, srcSlot, destSlots, retypeBase, userSize, 305 deviceMemory); 306 307 return EXCEPTION_NONE; 308} 309