1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <allocman/utspace/twinkle.h>
15#include <allocman/allocman.h>
16#include <allocman/util.h>
17#include <sel4/sel4.h>
18#include <vka/object.h>
19#include <string.h>
20
21static inline size_t _round_up(size_t v, size_t bits)
22{
23    size_t mask = MASK(bits);
24    if ((v & mask) != 0) {
25        v = (v & ~mask) + BIT(bits);
26    }
27    return v;
28}
29
30void utspace_twinkle_create(utspace_twinkle_t *twinkle)
31{
32    twinkle->num_uts = 0;
33    twinkle->uts = NULL;
34}
35
36int _utspace_twinkle_add_uts(allocman_t *alloc, void *_twinkle, size_t num, const cspacepath_t *uts, size_t *size_bits,
37                             uintptr_t *paddr, int utType)
38{
39    utspace_twinkle_t *twinkle = (utspace_twinkle_t *) _twinkle;
40    struct utspace_twinkle_ut *new_uts;
41    int error;
42    size_t i;
43    if (utType != ALLOCMAN_UT_KERNEL) {
44        ZF_LOGE("Twinkle does not support device untypeds");
45        return -1;
46    }
47    new_uts = allocman_mspace_alloc(alloc, sizeof(struct utspace_twinkle_ut) * (num + twinkle->num_uts), &error);
48    if (error) {
49        return error;
50    }
51    if (twinkle->uts) {
52        memcpy(new_uts, twinkle->uts, sizeof(struct utspace_twinkle_ut) * twinkle->num_uts);
53        allocman_mspace_free(alloc, twinkle->uts, sizeof(struct utspace_twinkle_ut) * twinkle->num_uts);
54    }
55    for (i = 0; i < num; i++, twinkle->num_uts++) {
56        new_uts[twinkle->num_uts] = (struct utspace_twinkle_ut) {
57            uts[i], 0, size_bits[i]
58        };
59    }
60    twinkle->uts = new_uts;
61    return 0;
62}
63
64seL4_Word _utspace_twinkle_alloc(allocman_t *alloc, void *_twinkle, size_t size_bits, seL4_Word type,
65                                 const cspacepath_t *slot, uintptr_t paddr, bool canBeDev, int *error)
66{
67    utspace_twinkle_t *twinkle = (utspace_twinkle_t *)_twinkle;
68    size_t sel4_size_bits;
69    int sel4_error;
70    size_t i, j;
71    if (paddr != ALLOCMAN_NO_PADDR) {
72        ZF_LOGE("Twinkle does not support allocating explicit physical addresses");
73        return -1;
74    }
75    /* get size of untyped call */
76    sel4_size_bits = get_sel4_object_size(type, size_bits);
77    if (size_bits != vka_get_object_size(type, sel4_size_bits) || size_bits == 0) {
78        SET_ERROR(error, 1);
79        return 0;
80    }
81    /* Search for the first thing that will support us */
82    for (i = 0; i < twinkle->num_uts; i++) {
83        if (_round_up(twinkle->uts[i].offset, size_bits) + BIT(size_bits) <= BIT(twinkle->uts[i].size_bits)) {
84            break;
85        }
86    }
87    if (i == twinkle->num_uts) {
88        SET_ERROR(error, 1);
89        return 0;
90    }
91    /* Check all untypeds of this size, and pick the one that will waste the least space */
92    for (j = i; j < twinkle->num_uts && twinkle->uts[i].size_bits == twinkle->uts[j].size_bits; j++) {
93        if ((_round_up(twinkle->uts[j].offset, size_bits) + BIT(size_bits) <= BIT(twinkle->uts[j].size_bits)) &&
94            (_round_up(twinkle->uts[j].offset, size_bits) - twinkle->uts[j].offset <
95             _round_up(twinkle->uts[i].offset, size_bits) - twinkle->uts[i].offset)) {
96            i = j;
97        }
98    }
99    /* if using inc retype then our offset calculation is effectively emulating the kernels calculations. This
100     * means we track the free space of the untyped correctly, and since we are not going to try and free then
101     * allocate again, this allocator can be used with either allocation scheme */
102    sel4_error = seL4_Untyped_Retype(twinkle->uts[i].path.capPtr, type, sel4_size_bits, slot->root, slot->dest,
103                                     slot->destDepth, slot->offset, 1);
104    if (sel4_error != seL4_NoError) {
105        /* Well this shouldn't happen */
106        SET_ERROR(error, 1);
107        return 0;
108    }
109
110    /* Update allocation information */
111    twinkle->uts[i].offset = _round_up(twinkle->uts[i].offset, size_bits) + BIT(size_bits);
112
113    /* We do not support free so we return an empty cookie */
114    SET_ERROR(error, 0);
115    return 0;
116}
117
118void _utspace_twinkle_free(allocman_t *alloc, void *_twinkle, seL4_Word cookie, size_t size_bits)
119{
120    /* Do nothing */
121}
122