blob: ea530d0e45fde525da709313c49548ac147477ed [file] [log] [blame]
/*
* 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;
}