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