|  | /* | 
|  | * Copyright 2017, Data61 | 
|  | * Commonwealth Scientific and Industrial Research Organisation (CSIRO) | 
|  | * ABN 41 687 119 230. | 
|  | * | 
|  | * This software may be distributed and modified according to the terms of | 
|  | * the BSD 2-Clause license. Note that NO WARRANTY is provided. | 
|  | * See "LICENSE_BSD2.txt" for details. | 
|  | * | 
|  | * @TAG(DATA61_BSD) | 
|  | */ | 
|  |  | 
|  | #include <sel4utils/slab.h> | 
|  | #include <vka/capops.h> | 
|  | #include <vka/object.h> | 
|  | #include <utils/attribute.h> | 
|  |  | 
|  | typedef struct { | 
|  | /* number of objects in the slab */ | 
|  | seL4_CPtr n; | 
|  | /* next free object in the slab */ | 
|  | seL4_CPtr next; | 
|  | /* list of objects in the slab */ | 
|  | vka_object_t *objects; | 
|  | } slab_t; | 
|  |  | 
|  | typedef struct { | 
|  | /* allocator to delegate further allocations to */ | 
|  | vka_t *delegate; | 
|  | /* allocation slab of each type */ | 
|  | slab_t slabs[seL4_ObjectTypeCount]; | 
|  | /* untyped to allocate from */ | 
|  | vka_object_t untyped; | 
|  | } slab_data_t; | 
|  |  | 
|  | typedef struct { | 
|  | ssize_t size_bits; | 
|  | seL4_Word type; | 
|  | } object_desc_t; | 
|  |  | 
|  | static int compare_object_descs(const void *a, const void *b) | 
|  | { | 
|  | const object_desc_t *obj_a = a; | 
|  | const object_desc_t *obj_b = b; | 
|  | /* order by biggest first */ | 
|  | return obj_b->size_bits - obj_a->size_bits; | 
|  | } | 
|  |  | 
|  | static int delegate_cspace_alloc(void *data, seL4_CPtr *res) | 
|  | { | 
|  | slab_data_t *sdata = data; | 
|  | assert(data != NULL); | 
|  | assert(sdata->delegate != NULL); | 
|  | return vka_cspace_alloc(sdata->delegate, res); | 
|  | } | 
|  |  | 
|  | static void delegate_cspace_make_path(void *data, seL4_CPtr slot, cspacepath_t *res) | 
|  | { | 
|  | slab_data_t *sdata = data; | 
|  | vka_cspace_make_path(sdata->delegate, slot, res); | 
|  | } | 
|  |  | 
|  | static void delegate_cspace_free(void *data, seL4_CPtr slot) | 
|  | { | 
|  | slab_data_t *sdata = data; | 
|  | vka_cspace_free(sdata->delegate, slot); | 
|  | } | 
|  |  | 
|  | static int slab_utspace_alloc(void *data, const cspacepath_t *dest, seL4_Word type, | 
|  | seL4_Word size_bits, seL4_Word *res) | 
|  | { | 
|  | slab_data_t *sdata = data; | 
|  |  | 
|  | if (type >= seL4_ObjectTypeCount) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | slab_t *slab = &sdata->slabs[type]; | 
|  | if (slab->next == slab->n) { | 
|  | ZF_LOGW("Slab of type %lu expired, using delegate allocator", type); | 
|  | return vka_utspace_alloc(sdata->delegate, dest, type, size_bits, res); | 
|  | } | 
|  |  | 
|  | cspacepath_t src; | 
|  | vka_cspace_make_path(sdata->delegate, slab->objects[slab->next].cptr, &src); | 
|  | if (vka_cnode_move(dest, &src) != seL4_NoError) { | 
|  | ZF_LOGW("Dest invalid\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | slab->next++; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int slab_utspace_alloc_maybe_device(void *data, const cspacepath_t *dest, seL4_Word type, | 
|  | seL4_Word size_bits, bool can_use_dev, seL4_Word *res) | 
|  | { | 
|  | return slab_utspace_alloc(data, dest, type, size_bits, res); | 
|  | } | 
|  |  | 
|  | static int delegate_utspace_alloc_at(void *data, const cspacepath_t *dest, seL4_Word type, seL4_Word size_bits, | 
|  | uintptr_t paddr, seL4_Word *res) | 
|  | { | 
|  | slab_data_t *sdata = data; | 
|  | return vka_utspace_alloc_at(sdata->delegate, dest, type, size_bits, paddr, res); | 
|  | } | 
|  |  | 
|  | static void | 
|  | slab_utspace_free(void *data, seL4_Word type, seL4_Word size_bits, seL4_Word target) | 
|  | { | 
|  | //TODO this should be possible - swap the object in at next, but do we need it? | 
|  | ZF_LOGW("slab_utspace_free not implemented"); | 
|  | } | 
|  |  | 
|  | static size_t calculate_total_size(size_t object_freq[seL4_ObjectTypeCount]) { | 
|  | size_t total_size = 0; | 
|  | for (int i = 0; i < seL4_ObjectTypeCount; i++) { | 
|  | if (object_freq[i] > 0) { | 
|  | size_t object_size = vka_get_object_size(i, 0); | 
|  | if (object_size == 0 || object_size == -1) { | 
|  | ZF_LOGE("Object of undetermined size passed to slab allocator"); | 
|  | return 0; | 
|  | } | 
|  | total_size += (BIT(object_size)) * object_freq[i]; | 
|  | } | 
|  | } | 
|  | return total_size; | 
|  | } | 
|  |  | 
|  | static void slab_destroy(vka_t *slab) | 
|  | { | 
|  | ZF_LOGW("Slab destroy not implemented"); | 
|  | } | 
|  |  | 
|  | static seL4_Error alloc_object(vka_t *delegate, vka_object_t *untyped, size_t size_bits, seL4_Word type, | 
|  | vka_object_t *object) | 
|  | { | 
|  | /* allocate slot for object */ | 
|  | object->type = type; | 
|  | object->size_bits = size_bits; | 
|  | seL4_Error error = vka_cspace_alloc(delegate, &object->cptr); | 
|  | if (error != seL4_NoError) { | 
|  | ZF_LOGE("Failed to allocate cslot"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | /* convert to cspacepath */ | 
|  | cspacepath_t path; | 
|  | vka_cspace_make_path(delegate, object->cptr, &path); | 
|  |  | 
|  | /* retype object */ | 
|  | return seL4_Untyped_Retype(untyped->cptr, type, size_bits, path.root, path.dest, | 
|  | path.destDepth, path.offset, 1); | 
|  | } | 
|  |  | 
|  | static int alloc_object_slab(vka_t *delegate, vka_object_t *untyped, slab_t *slab, size_t n, | 
|  | size_t size_bits, seL4_Word type) | 
|  | { | 
|  | ZF_LOGI("Preallocating %zu objects of %zu size bits, %lu type\n", n, size_bits, (long) type); | 
|  |  | 
|  | slab->n = n; | 
|  | slab->next = 0; | 
|  |  | 
|  | if (n > 0) { | 
|  | slab->objects = calloc(n, sizeof(vka_object_t)); | 
|  | if (slab->objects == NULL) { | 
|  | ZF_LOGI("Failed to allocate %zu objects of %zu size bits, %lu type", n, size_bits, (long) type); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < slab->n; i++) { | 
|  | if (alloc_object(delegate, untyped, size_bits, type, &slab->objects[i]) != seL4_NoError) { | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* success */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int slab_init(vka_t *slab_vka, vka_t *delegate, size_t object_freq[seL4_ObjectTypeCount]) { | 
|  |  | 
|  | slab_data_t *data = calloc(1, sizeof(slab_data_t)); | 
|  |  | 
|  | if (!data) { | 
|  | slab_destroy(slab_vka); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | slab_vka->data = data; | 
|  | data->delegate = delegate; | 
|  |  | 
|  | slab_vka->cspace_alloc = delegate_cspace_alloc; | 
|  | slab_vka->cspace_make_path = delegate_cspace_make_path; | 
|  | slab_vka->cspace_free = delegate_cspace_free; | 
|  | slab_vka->utspace_alloc_at = delegate_utspace_alloc_at; | 
|  | slab_vka->utspace_alloc = slab_utspace_alloc; | 
|  | slab_vka->utspace_alloc_maybe_device = slab_utspace_alloc_maybe_device; | 
|  | slab_vka->utspace_free = slab_utspace_free; | 
|  |  | 
|  | /* allocate untyped */ | 
|  | size_t total_size = calculate_total_size(object_freq); | 
|  | size_t total_size_bits = seL4_WordBits - CLZL(total_size); | 
|  | /* sanity */ | 
|  | assert(BIT(total_size_bits) >= total_size); | 
|  | if (total_size == 0) { | 
|  | ZF_LOGW("No objects to allocate\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int error = vka_alloc_untyped(delegate, total_size_bits, &data->untyped); | 
|  | if (error != 0) { | 
|  | ZF_LOGE("Failed to allocate untyped of size bits %zu\n", total_size_bits); | 
|  | slab_destroy(slab_vka); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* order object sizes by size */ | 
|  | object_desc_t object_descs[seL4_ObjectTypeCount]; | 
|  | for (int i = 0; i < seL4_ObjectTypeCount; i++) { | 
|  | object_descs[i].type = i; | 
|  | object_descs[i].size_bits = vka_get_object_size(i, 0); | 
|  | } | 
|  |  | 
|  | qsort(object_descs, seL4_ObjectTypeCount, sizeof(object_desc_t), compare_object_descs); | 
|  |  | 
|  | /* allocate slabs */ | 
|  | for (int i = 0; i < seL4_ObjectTypeCount && object_descs[i].size_bits != 0; i++) { | 
|  | int type = object_descs[i].type; | 
|  | error = alloc_object_slab(delegate, &data->untyped, &data->slabs[type], | 
|  | object_freq[type], object_descs[i].size_bits, type); | 
|  | if (error != 0) { | 
|  | ZF_LOGE("Failed to create slab\n"); | 
|  | slab_destroy(slab_vka); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |