| /* |
| * 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 <allocman/cspace/two_level.h> |
| #include <allocman/util.h> |
| #include <allocman/allocman.h> |
| #include <sel4/sel4.h> |
| #include <string.h> |
| #include <utils/attribute.h> |
| |
| cspacepath_t _cspace_two_level_make_path(void *_cspace, seL4_CPtr slot) |
| { |
| cspacepath_t l1_path, l2_path; |
| size_t l1slot, l2slot; |
| cspace_two_level_t *cspace = (cspace_two_level_t*)_cspace; |
| l1slot = slot >> cspace->config.level_two_bits; |
| l2slot = slot & MASK(cspace->config.level_two_bits); |
| /* This is an excessive way of constructing the path. But it's cool to do it in a way that |
| makes no assumptions about the underlying cspace structures. In particular the second level |
| could be a two level cspace and this would still work */ |
| if(!cspace->second_levels[l1slot]) { |
| assert(!"ERROR: Tried make a path to a non existant slot\n"); |
| return (cspacepath_t) {0, 0, 0, 0, 0, 0, 0}; |
| } |
| l1_path = _cspace_single_level_make_path(&cspace->first_level, l1slot); |
| l2_path =_cspace_single_level_make_path(&cspace->second_levels[l1slot]->second_level, l2slot); |
| return (cspacepath_t) { |
| .root = l1_path.root, |
| .capPtr = (l1_path.capPtr << l2_path.capDepth) | l2_path.capPtr, |
| .capDepth = l1_path.capDepth + l2_path.capDepth, |
| .dest = (l1_path.capPtr << l2_path.destDepth) | l2_path.dest, |
| .destDepth = l1_path.capDepth + l2_path.destDepth, |
| .offset = l2_path.offset |
| }; |
| /* This is the simpler version */ |
| /* path->root = cspace->config.cnode; |
| path->slot = slot; |
| path->slot_depth = cspace->config.cnode_size_bits + cspace->config.cnode_guard_bits + cspace->config.level_two_bits; |
| path->cnode = l1slot; |
| path->cnode_depth = cspace->config.cnode_size_bits + cspace->config.cnode_guard_bits; |
| path->cnode_offset = l2slot;*/ |
| } |
| |
| static int _create_second_level(allocman_t *alloc, cspace_two_level_t *cspace, size_t index, int alloc_node) |
| { |
| int error; |
| struct cspace_single_level_config single_config = { |
| /* There is no cap at depth 32 to reference the cnode of this cspace, fortunately |
| though we don't actually need it */ |
| .cnode = 0, |
| .cnode_size_bits = cspace->config.level_two_bits, |
| .cnode_guard_bits = 0, |
| .first_slot = 0, |
| .end_slot = BIT(cspace->config.level_two_bits) |
| }; |
| cspace->second_levels[index] = (struct cspace_two_level_node*) allocman_mspace_alloc(alloc, sizeof(struct cspace_two_level_node), &error); |
| if (error) { |
| return error; |
| } |
| if (alloc_node) { |
| cspacepath_t path = _cspace_single_level_make_path(&cspace->first_level, index); |
| cspace->second_levels[index]->cookie = allocman_utspace_alloc(alloc, cspace->config.level_two_bits + seL4_SlotBits, seL4_CapTableObject, &path, false, &error); |
| cspace->second_levels[index]->cookie_valid = 1; |
| } else { |
| cspace->second_levels[index]->cookie_valid = 0; |
| } |
| if (error) { |
| allocman_mspace_free(alloc, cspace->second_levels[index], sizeof(struct cspace_two_level_node)); |
| cspace->second_levels[index] = NULL; |
| return error; |
| } |
| error = cspace_single_level_create(alloc, &cspace->second_levels[index]->second_level, single_config); |
| if (error) { |
| allocman_utspace_free(alloc, cspace->second_levels[index]->cookie, cspace->config.level_two_bits + seL4_SlotBits); |
| allocman_mspace_free(alloc, cspace->second_levels[index], sizeof(struct cspace_two_level_node)); |
| cspace->second_levels[index] = NULL; |
| return error; |
| } |
| cspace->second_levels[index]->count = 0; |
| return 0; |
| } |
| |
| int cspace_two_level_create(allocman_t *alloc, cspace_two_level_t *cspace, struct cspace_two_level_config config) |
| { |
| int error; |
| size_t i; |
| struct cspace_single_level_config single_config = { |
| .cnode = config.cnode, |
| .cnode_size_bits = config.cnode_size_bits, |
| .cnode_guard_bits = config.cnode_guard_bits, |
| .first_slot = config.first_slot, |
| .end_slot = config.end_slot |
| }; |
| cspace->config = config; |
| cspace->second_levels = (struct cspace_two_level_node **)allocman_mspace_alloc(alloc, sizeof(struct cspace_two_level_node*) * BIT(config.cnode_size_bits), &error); |
| if (error) { |
| return error; |
| } |
| error = cspace_single_level_create(alloc, &cspace->first_level, single_config); |
| if (error) { |
| allocman_mspace_free(alloc, cspace->second_levels, sizeof(struct cspace_two_level_node*) * BIT(config.cnode_size_bits)); |
| return error; |
| } |
| for (i = 0; i < BIT(config.cnode_size_bits); i++) { |
| cspace->second_levels[i] = NULL; |
| } |
| cspace->last_second_level = 0; |
| for (i = config.start_existing_index; i < config.end_existing_index; i++) { |
| error = _cspace_single_level_alloc_at(alloc, &cspace->first_level, (seL4_CPtr) i); |
| if (error) { |
| cspace_two_level_destroy(alloc, cspace); |
| return error; |
| } |
| error = _create_second_level(alloc, cspace, i, 0); |
| if (error) { |
| cspace_two_level_destroy(alloc, cspace); |
| return error; |
| } |
| } |
| /* now fill in any slots that may already be allocated */ |
| for (i = config.start_existing_slot; i < config.end_existing_slot; i++) { |
| error = _cspace_two_level_alloc_at(alloc, cspace, (seL4_CPtr) i); |
| if (error) { |
| cspace_two_level_destroy(alloc, cspace); |
| return error; |
| } |
| } |
| return 0; |
| } |
| |
| int _cspace_two_level_alloc_at(allocman_t *alloc, void *_cspace, seL4_CPtr slot) { |
| cspace_two_level_t *cspace = (cspace_two_level_t*)_cspace; |
| size_t l1slot; |
| size_t l2slot; |
| int error; |
| l1slot = slot >> cspace->config.level_two_bits; |
| l2slot = slot & MASK(cspace->config.level_two_bits); |
| /* see if the first level exists */ |
| if (!cspace->second_levels[l1slot]) { |
| error = _cspace_single_level_alloc_at(alloc, &cspace->first_level, l1slot); |
| if(error) { |
| return error; |
| } |
| error = _create_second_level(alloc, cspace, l1slot, 1); |
| if (error) { |
| return error; |
| } |
| } |
| /* now try and allocate from the second level */ |
| error = _cspace_single_level_alloc_at(alloc, &cspace->second_levels[l1slot]->second_level, (seL4_CPtr) l2slot); |
| if (error) { |
| return error; |
| } |
| cspace->second_levels[l1slot]->count++; |
| return 0; |
| } |
| |
| int _cspace_two_level_alloc(allocman_t *alloc, void *_cspace, cspacepath_t *slot) |
| { |
| cspace_two_level_t *cspace = (cspace_two_level_t*)_cspace; |
| size_t i; |
| int found; |
| int first; |
| int error; |
| cspacepath_t level2_slot; |
| /* Hunt for a slot */ |
| i = cspace->last_second_level; |
| found = 0; |
| first = 1; |
| while (!found && (first || i != cspace->last_second_level)) { |
| first = 0; |
| if (cspace->second_levels[i] && cspace->second_levels[i]->count < MASK(cspace->config.level_two_bits)) { |
| found = 1; |
| } else { |
| i = (i + 1) % BIT(cspace->config.cnode_size_bits); |
| } |
| } |
| if (!found) { |
| /* ask the first level node for an empty slot */ |
| cspacepath_t l1slot; |
| error = _cspace_single_level_alloc(alloc, &cspace->first_level, &l1slot); |
| if (error) { |
| /* our cspace is just full */ |
| return error; |
| } |
| /* use this index */ |
| error = _create_second_level(alloc, cspace, l1slot.offset, 1); |
| if (error) { |
| return error; |
| } |
| } |
| cspace->last_second_level = i; |
| error = _cspace_single_level_alloc(alloc, &cspace->second_levels[i]->second_level, &level2_slot); |
| if (error) { |
| /* This just shouldn't be possible */ |
| assert(!"cspace_single_level not behaving as expected"); |
| return error; |
| } |
| cspace->second_levels[i]->count++; |
| *slot = _cspace_two_level_make_path(cspace, (i << cspace->config.level_two_bits) | level2_slot.capPtr); |
| return 0; |
| } |
| |
| static void _destroy_second_level(allocman_t *alloc, cspace_two_level_t *cspace, size_t index) |
| { |
| cspacepath_t path; |
| cspace_single_level_destroy(alloc, &cspace->second_levels[index]->second_level); |
| if (cspace->second_levels[index]->cookie_valid) { |
| int error = seL4_CNode_Delete(cspace->config.cnode, index, |
| seL4_WordBits - cspace->config.level_two_bits); |
| assert(error == seL4_NoError); |
| allocman_utspace_free(alloc, cspace->second_levels[index]->cookie, cspace->config.level_two_bits + seL4_SlotBits); |
| } |
| allocman_mspace_free(alloc, cspace->second_levels[index], sizeof(struct cspace_two_level_node)); |
| path = _cspace_single_level_make_path(&cspace->first_level, index); |
| _cspace_single_level_free(alloc, &cspace->first_level, &path); |
| } |
| |
| void _cspace_two_level_free(struct allocman *alloc, void *_cspace, const cspacepath_t *slot) |
| { |
| size_t l1slot; |
| size_t l2slot; |
| seL4_CPtr cptr = slot->capPtr; |
| cspacepath_t path; |
| cspace_two_level_t *cspace = (cspace_two_level_t*)_cspace; |
| l1slot = cptr >> cspace->config.level_two_bits; |
| l2slot = cptr & MASK(cspace->config.level_two_bits); |
| path = _cspace_single_level_make_path(&cspace->second_levels[l1slot]->second_level, l2slot); |
| _cspace_single_level_free(alloc, &cspace->second_levels[l1slot]->second_level, &path); |
| cspace->second_levels[l1slot]->count--; |
| if (cspace->second_levels[l1slot]->count == 0) { |
| _destroy_second_level(alloc, cspace, l1slot); |
| cspace->second_levels[l1slot] = NULL; |
| } |
| } |
| |
| void cspace_two_level_destroy(struct allocman *alloc, cspace_two_level_t *cspace) |
| { |
| size_t i; |
| for (i = 0; i < BIT(cspace->config.cnode_size_bits); i++) { |
| if (cspace->second_levels[i]) { |
| _destroy_second_level(alloc, cspace, i); |
| } |
| } |
| allocman_mspace_free(alloc, cspace->second_levels, sizeof(struct cspace_two_level_node*) * BIT(cspace->config.cnode_size_bits)); |
| cspace_single_level_destroy(alloc, &cspace->first_level); |
| } |