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