| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| #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; |
| } |