| /* | |
| * 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; | |
| } |