1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * This software may be distributed and modified according to the terms of
5 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
6 * See "LICENSE_GPLv2.txt" for details.
7 *
8 * @TAG(GD_GPL)
9 */
10
11#include <config.h>
12#include <types.h>
13#include <api/failures.h>
14#include <api/syscall.h>
15#include <api/invocation.h>
16#include <machine/io.h>
17#include <object/structures.h>
18#include <object/untyped.h>
19#include <object/objecttype.h>
20#include <object/cnode.h>
21#include <kernel/cspace.h>
22#include <kernel/thread.h>
23#include <util.h>
24
25static word_t
26alignUp(word_t baseValue, word_t alignment)
27{
28    return (baseValue + (BIT(alignment) - 1)) & ~MASK(alignment);
29}
30
31exception_t
32decodeUntypedInvocation(word_t invLabel, word_t length, cte_t *slot,
33                        cap_t cap, extra_caps_t excaps,
34                        bool_t call, word_t *buffer)
35{
36    word_t newType, userObjSize, nodeIndex;
37    word_t nodeDepth, nodeOffset, nodeWindow;
38    cte_t *rootSlot UNUSED;
39    exception_t status;
40    cap_t nodeCap;
41    lookupSlot_ret_t lu_ret;
42    word_t nodeSize;
43    word_t i;
44    slot_range_t slots;
45    word_t freeRef, alignedFreeRef, objectSize, untypedFreeBytes;
46    word_t freeIndex;
47    bool_t deviceMemory;
48    bool_t reset;
49
50    /* Ensure operation is valid. */
51    if (invLabel != UntypedRetype) {
52        userError("Untyped cap: Illegal operation attempted.");
53        current_syscall_error.type = seL4_IllegalOperation;
54        return EXCEPTION_SYSCALL_ERROR;
55    }
56
57    /* Ensure message length valid. */
58    if (length < 6 || excaps.excaprefs[0] == NULL) {
59        userError("Untyped invocation: Truncated message.");
60        current_syscall_error.type = seL4_TruncatedMessage;
61        return EXCEPTION_SYSCALL_ERROR;
62    }
63
64    /* Fetch arguments. */
65    newType     = getSyscallArg(0, buffer);
66    userObjSize = getSyscallArg(1, buffer);
67    nodeIndex   = getSyscallArg(2, buffer);
68    nodeDepth   = getSyscallArg(3, buffer);
69    nodeOffset  = getSyscallArg(4, buffer);
70    nodeWindow  = getSyscallArg(5, buffer);
71
72    rootSlot = excaps.excaprefs[0];
73
74    /* Is the requested object type valid? */
75    if (newType >= seL4_ObjectTypeCount) {
76        userError("Untyped Retype: Invalid object type.");
77        current_syscall_error.type = seL4_InvalidArgument;
78        current_syscall_error.invalidArgumentNumber = 0;
79        return EXCEPTION_SYSCALL_ERROR;
80    }
81
82    objectSize = getObjectSize(newType, userObjSize);
83
84    /* Exclude impossibly large object sizes. getObjectSize can overflow if userObjSize
85       is close to 2^wordBits, which is nonsensical in any case, so we check that this
86       did not happen. userObjSize will always need to be less than wordBits. */
87    if (userObjSize >= wordBits || objectSize > seL4_MaxUntypedBits) {
88        userError("Untyped Retype: Invalid object size.");
89        current_syscall_error.type = seL4_RangeError;
90        current_syscall_error.rangeErrorMin = 0;
91        current_syscall_error.rangeErrorMax = seL4_MaxUntypedBits;
92        return EXCEPTION_SYSCALL_ERROR;
93    }
94
95    /* If the target object is a CNode, is it at least size 1? */
96    if (newType == seL4_CapTableObject && userObjSize == 0) {
97        userError("Untyped Retype: Requested CapTable size too small.");
98        current_syscall_error.type = seL4_InvalidArgument;
99        current_syscall_error.invalidArgumentNumber = 1;
100        return EXCEPTION_SYSCALL_ERROR;
101    }
102
103    /* If the target object is a Untyped, is it at least size 4? */
104    if (newType == seL4_UntypedObject && userObjSize < seL4_MinUntypedBits) {
105        userError("Untyped Retype: Requested UntypedItem size too small.");
106        current_syscall_error.type = seL4_InvalidArgument;
107        current_syscall_error.invalidArgumentNumber = 1;
108        return EXCEPTION_SYSCALL_ERROR;
109    }
110
111    /* Lookup the destination CNode (where our caps will be placed in). */
112    if (nodeDepth == 0) {
113        nodeCap = excaps.excaprefs[0]->cap;
114    } else {
115        cap_t rootCap = excaps.excaprefs[0]->cap;
116        lu_ret = lookupTargetSlot(rootCap, nodeIndex, nodeDepth);
117        if (lu_ret.status != EXCEPTION_NONE) {
118            userError("Untyped Retype: Invalid destination address.");
119            return lu_ret.status;
120        }
121        nodeCap = lu_ret.slot->cap;
122    }
123
124    /* Is the destination actually a CNode? */
125    if (cap_get_capType(nodeCap) != cap_cnode_cap) {
126        userError("Untyped Retype: Destination cap invalid or read-only.");
127        current_syscall_error.type = seL4_FailedLookup;
128        current_syscall_error.failedLookupWasSource = 0;
129        current_lookup_fault = lookup_fault_missing_capability_new(nodeDepth);
130        return EXCEPTION_SYSCALL_ERROR;
131    }
132
133    /* Is the region where the user wants to put the caps valid? */
134    nodeSize = 1ul << cap_cnode_cap_get_capCNodeRadix(nodeCap);
135    if (nodeOffset > nodeSize - 1) {
136        userError("Untyped Retype: Destination node offset #%d too large.",
137                  (int)nodeOffset);
138        current_syscall_error.type = seL4_RangeError;
139        current_syscall_error.rangeErrorMin = 0;
140        current_syscall_error.rangeErrorMax = nodeSize - 1;
141        return EXCEPTION_SYSCALL_ERROR;
142    }
143    if (nodeWindow < 1 || nodeWindow > CONFIG_RETYPE_FAN_OUT_LIMIT) {
144        userError("Untyped Retype: Number of requested objects (%d) too small or large.",
145                  (int)nodeWindow);
146        current_syscall_error.type = seL4_RangeError;
147        current_syscall_error.rangeErrorMin = 1;
148        current_syscall_error.rangeErrorMax = CONFIG_RETYPE_FAN_OUT_LIMIT;
149        return EXCEPTION_SYSCALL_ERROR;
150    }
151    if (nodeWindow > nodeSize - nodeOffset) {
152        userError("Untyped Retype: Requested destination window overruns size of node.");
153        current_syscall_error.type = seL4_RangeError;
154        current_syscall_error.rangeErrorMin = 1;
155        current_syscall_error.rangeErrorMax = nodeSize - nodeOffset;
156        return EXCEPTION_SYSCALL_ERROR;
157    }
158
159    /* Ensure that the destination slots are all empty. */
160    slots.cnode = CTE_PTR(cap_cnode_cap_get_capCNodePtr(nodeCap));
161    slots.offset = nodeOffset;
162    slots.length = nodeWindow;
163    for (i = nodeOffset; i < nodeOffset + nodeWindow; i++) {
164        status = ensureEmptySlot(slots.cnode + 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                                slots, deviceMemory);
233}
234
235static exception_t
236resetUntypedCap(cte_t *srcSlot)
237{
238    cap_t prev_cap = srcSlot->cap;
239    word_t block_size = cap_untyped_cap_get_capBlockSize(prev_cap);
240    void *regionBase = WORD_PTR(cap_untyped_cap_get_capPtr(prev_cap));
241    int chunk = CONFIG_RESET_CHUNK_BITS;
242    word_t offset = FREE_INDEX_TO_OFFSET(cap_untyped_cap_get_capFreeIndex(prev_cap));
243    exception_t status;
244    bool_t deviceMemory = cap_untyped_cap_get_capIsDevice(prev_cap);
245
246    if (offset == 0) {
247        return EXCEPTION_NONE;
248    }
249
250    /** AUXUPD: "(True, typ_region_bytes (ptr_val \<acute>regionBase)
251        (unat \<acute>block_size))" */
252    /** GHOSTUPD: "(True, gs_clear_region (ptr_val \<acute>regionBase)
253        (unat \<acute>block_size))" */
254
255    if (deviceMemory || block_size < chunk) {
256        if (! deviceMemory) {
257            clearMemory(regionBase, block_size);
258        }
259        srcSlot->cap = cap_untyped_cap_set_capFreeIndex(prev_cap, 0);
260    } else {
261        for (offset = ROUND_DOWN(offset - 1, chunk);
262                offset != - BIT (chunk); offset -= BIT (chunk)) {
263            clearMemory(GET_OFFSET_FREE_PTR(regionBase, offset), chunk);
264            srcSlot->cap = cap_untyped_cap_set_capFreeIndex(prev_cap, OFFSET_TO_FREE_INDEX(offset));
265            status = preemptionPoint();
266            if (status != EXCEPTION_NONE) {
267                return status;
268            }
269        }
270    }
271    return EXCEPTION_NONE;
272}
273
274exception_t
275invokeUntyped_Retype(cte_t *srcSlot,
276                     bool_t reset, void* retypeBase,
277                     object_t newType, word_t userSize,
278                     slot_range_t destSlots, bool_t deviceMemory)
279{
280    word_t freeRef;
281    word_t totalObjectSize;
282    void *regionBase = WORD_PTR(cap_untyped_cap_get_capPtr(srcSlot->cap));
283    exception_t status;
284
285    freeRef = GET_FREE_REF(regionBase, cap_untyped_cap_get_capFreeIndex(srcSlot->cap));
286
287    if (reset) {
288        status = resetUntypedCap(srcSlot);
289        if (status != EXCEPTION_NONE) {
290            return status;
291        }
292    }
293
294    /* Update the amount of free space left in this untyped cap. */
295    totalObjectSize = destSlots.length << getObjectSize(newType, userSize);
296    freeRef = (word_t)retypeBase + totalObjectSize;
297    srcSlot->cap = cap_untyped_cap_set_capFreeIndex(srcSlot->cap,
298                                                    GET_FREE_INDEX(regionBase, freeRef));
299
300    /* Create new objects and caps. */
301    createNewObjects(newType, srcSlot, destSlots, retypeBase, userSize,
302                     deviceMemory);
303
304    return EXCEPTION_NONE;
305}
306