/* * 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 #include #include #include #include #include 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 UNUSED 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); }