| /* |
| * Copyright 2014, NICTA |
| * |
| * 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(NICTA_BSD) |
| */ |
| |
| #include <autoconf.h> |
| #include <sel4/sel4.h> |
| #include <string.h> |
| #include <allocman/allocman.h> |
| #include <allocman/cspace/simple1level.h> |
| #include <allocman/cspace/two_level.h> |
| #include <allocman/mspace/dual_pool.h> |
| #include <allocman/utspace/split.h> |
| #include <allocman/bootstrap.h> |
| #include <allocman/sel4_arch/reservation.h> |
| #include <stdio.h> |
| #include <vka/capops.h> |
| #include <vka/object.h> |
| #include <sel4utils/mapping.h> |
| #include <simple/simple.h> |
| #include <simple/simple_helpers.h> |
| #include <utils/arith.h> |
| |
| /* configure the choice of boot strapping allocators */ |
| #define UTMAN split |
| |
| /*do some nasty macro expansion to get the string substitution to happen how we want */ |
| #define CON2(a, b,c) a##b##c |
| #define CON(a, b,c) CON2(a,b,c) |
| #define UTMAN_TYPE CON(utspace_, UTMAN,_t) |
| #define UTMAN_CREATE CON(utspace_, UTMAN,_create) |
| #define UTMAN_MAKE_INTERFACE CON(utspace_, UTMAN,_make_interface) |
| #define UTMAN_ADD_UTS CON(_utspace_, UTMAN,_add_uts) |
| |
| struct bootstrap_info { |
| allocman_t *alloc; |
| int have_boot_cspace; |
| /* a path to the current boot cnode */ |
| cspacepath_t boot_cnode; |
| /* this is prefixed with 'maybe' since we are just using it as some preallocated memory |
| * if a temp bootstrapping cspace is needed. it may not actually be used if a more |
| * complicated cspace is passed in directly by the user */ |
| cspace_simple1level_t maybe_boot_cspace; |
| cspace_interface_t boot_cspace; |
| /* if we have switched cspaces, a patch to our old root cnode (based in the current cnode) */ |
| cspacepath_t old_cnode; |
| /* a path to the page directory cap, we need this to be able to change the cspace root */ |
| cspacepath_t pd; |
| cspacepath_t tcb; |
| int uts_in_current_cspace; |
| size_t num_uts; |
| cspacepath_t *uts; |
| size_t *ut_size_bits; |
| uintptr_t *ut_paddr; |
| simple_t *simple; |
| }; |
| |
| static void bootstrap_free_info(bootstrap_info_t *bs) { |
| if (bs->uts) { |
| allocman_mspace_free(bs->alloc, bs->uts, sizeof(cspacepath_t) * bs->num_uts); |
| allocman_mspace_free(bs->alloc, bs->ut_size_bits, sizeof(size_t) * bs->num_uts); |
| } |
| allocman_mspace_free(bs->alloc, bs, sizeof(bootstrap_info_t)); |
| } |
| |
| allocman_t *bootstrap_create_allocman(size_t pool_size, void *pool) { |
| uintptr_t cur_pool = (uintptr_t)pool; |
| uintptr_t pool_top = cur_pool + pool_size; |
| mspace_dual_pool_t *mspace; |
| allocman_t *alloc; |
| int error; |
| /* first align the pool */ |
| cur_pool = ALIGN_UP(cur_pool, sizeof(size_t)); |
| /* carve off some of the pool for the allocman and memory allocator itself */ |
| alloc = (allocman_t*)cur_pool; |
| cur_pool += sizeof(*alloc); |
| mspace = (mspace_dual_pool_t*)cur_pool; |
| cur_pool += sizeof(*mspace); |
| if (cur_pool >= pool_top) { |
| LOG_ERROR("Initial memory pool too small"); |
| return NULL; |
| } |
| /* create the allocator */ |
| mspace_dual_pool_create(mspace, (struct mspace_fixed_pool_config){(void*)cur_pool, pool_top - cur_pool}); |
| error = allocman_create(alloc, mspace_dual_pool_make_interface(mspace)); |
| if (error) { |
| return NULL; |
| } |
| return alloc; |
| } |
| |
| int bootstrap_set_boot_cspace(bootstrap_info_t *bs, cspace_interface_t cspace, cspacepath_t root_cnode) { |
| bs->boot_cspace = cspace; |
| bs->boot_cnode = root_cnode; |
| bs->have_boot_cspace = 1; |
| return 0; |
| } |
| |
| static int bootstrap_create_temp_bootinfo_cspace_at(bootstrap_info_t *bs, seL4_BootInfo *bi, seL4_CPtr root_cnode) { |
| /* create a cspace to represent the bootinfo cspace. */ |
| cspace_simple1level_create(&bs->maybe_boot_cspace, (struct cspace_simple1level_config){ |
| .cnode = root_cnode, |
| .cnode_size_bits = bi->initThreadCNodeSizeBits, |
| .cnode_guard_bits = seL4_WordBits - bi->initThreadCNodeSizeBits, |
| .first_slot = bi->empty.start, |
| .end_slot = bi->empty.end - 1 |
| }); |
| return bootstrap_set_boot_cspace(bs, |
| cspace_simple1level_make_interface( |
| &bs->maybe_boot_cspace), |
| _cspace_simple1level_make_path(&bs->maybe_boot_cspace, root_cnode)); |
| } |
| |
| static int bootstrap_create_temp_bootinfo_cspace(bootstrap_info_t *bs, seL4_BootInfo *bi) { |
| return bootstrap_create_temp_bootinfo_cspace_at(bs, bi, seL4_CapInitThreadCNode); |
| } |
| |
| static bootstrap_info_t *_create_info(allocman_t *alloc) { |
| int error; |
| bootstrap_info_t *bs = allocman_mspace_alloc(alloc, sizeof(bootstrap_info_t), &error); |
| if (error) { |
| LOG_ERROR("Failed to allocate bootstrap_info"); |
| return NULL; |
| } |
| memset(bs, 0, sizeof(bootstrap_info_t)); |
| bs->alloc = alloc; |
| /* currently have no untypeds so this is true */ |
| bs->uts_in_current_cspace = 1; |
| return bs; |
| } |
| |
| static int _add_ut(bootstrap_info_t *bs, cspacepath_t slot, size_t size_bits, uintptr_t paddr) { |
| cspacepath_t *new_uts; |
| size_t *new_size_bits; |
| uintptr_t *new_paddr; |
| int error; |
| new_uts = allocman_mspace_alloc(bs->alloc, sizeof(cspacepath_t) * (bs->num_uts + 1), &error); |
| if (error) { |
| LOG_ERROR("Failed to allocate space for untypeds (new_uts)"); |
| return error; |
| } |
| new_size_bits = allocman_mspace_alloc(bs->alloc, sizeof(size_t) * (bs->num_uts + 1), &error); |
| if (error) { |
| LOG_ERROR("Failed to allocate space for untypeds (new_size_bits)"); |
| return error; |
| } |
| new_paddr = allocman_mspace_alloc(bs->alloc, sizeof(uintptr_t) * (bs->num_uts + 1), &error); |
| if (error) { |
| LOG_ERROR("Failed to allocate space for untypeds (new_paddr)"); |
| return error; |
| } |
| if (bs->uts) { |
| memcpy(new_uts, bs->uts, sizeof(cspacepath_t) * bs->num_uts); |
| memcpy(new_size_bits, bs->ut_size_bits, sizeof(size_t) * bs->num_uts); |
| memcpy(new_paddr, bs->ut_paddr, sizeof(uintptr_t) * bs->num_uts); |
| allocman_mspace_free(bs->alloc, bs->uts, sizeof(cspacepath_t) * bs->num_uts); |
| allocman_mspace_free(bs->alloc, bs->ut_size_bits, sizeof(size_t) * bs->num_uts); |
| allocman_mspace_free(bs->alloc, bs->ut_paddr, sizeof(uintptr_t) * bs->num_uts); |
| } |
| new_uts[bs->num_uts] = slot; |
| new_size_bits[bs->num_uts] = size_bits; |
| new_paddr[bs->num_uts] = paddr; |
| bs->uts = new_uts; |
| bs->ut_size_bits = new_size_bits; |
| bs->ut_paddr = new_paddr; |
| bs->num_uts++; |
| return 0; |
| } |
| |
| int bootstrap_add_untypeds(bootstrap_info_t *bs, size_t num, const cspacepath_t *uts, size_t *size_bits, uintptr_t *paddr) { |
| size_t i; |
| int error; |
| for (i = 0; i < num; i++) { |
| error = _add_ut(bs, uts[i], size_bits[i], paddr ? paddr[i] : 0); |
| if (error) { |
| return error; |
| } |
| } |
| return 0; |
| } |
| |
| int bootstrap_add_untypeds_from_bootinfo(bootstrap_info_t *bs, seL4_BootInfo *bi) { |
| int error; |
| seL4_CPtr i; |
| /* if we do not have a boot cspace, or we have added some uts that aren't in the |
| * current space then just bail */ |
| if (!bs->have_boot_cspace || (bs->uts && !bs->uts_in_current_cspace)) { |
| return 1; |
| } |
| for (i = bi->untyped.start; i < bi->untyped.end; i++) { |
| cspacepath_t slot = bs->boot_cspace.make_path(bs->boot_cspace.cspace, i); |
| size_t size_bits = bi->untypedSizeBitsList[i - bi->untyped.start]; |
| uintptr_t paddr = bi->untypedPaddrList[i - bi->untyped.start]; |
| error = _add_ut(bs, slot, size_bits, paddr); |
| if (error) { |
| return error; |
| } |
| } |
| /* we assume this is true here */ |
| bs->uts_in_current_cspace = 1; |
| return 0; |
| } |
| |
| static int bootstrap_add_untypeds_from_simple(bootstrap_info_t *bs, simple_t *simple) { |
| int error; |
| int i; |
| /* if we do not have a boot cspace, or we have added some uts that aren't in the |
| * current space then just bail */ |
| if (!bs->have_boot_cspace || (bs->uts && !bs->uts_in_current_cspace)) { |
| return 1; |
| } |
| for (i = 0; i < simple_get_untyped_count(simple); i++) { |
| size_t size_bits; |
| uintptr_t paddr; |
| cspacepath_t slot = bs->boot_cspace.make_path(bs->boot_cspace.cspace, |
| simple_get_nth_untyped(simple, i, &size_bits, &paddr)); |
| error = _add_ut(bs, slot, size_bits, paddr); |
| if (error) { |
| return error; |
| } |
| } |
| /* we assume this is true here */ |
| bs->uts_in_current_cspace = 1; |
| return 0; |
| } |
| |
| static int _remove_ut(bootstrap_info_t *bs, size_t i) { |
| cspacepath_t *new_uts = NULL; |
| size_t *new_size_bits = NULL; |
| uintptr_t *new_paddr = NULL; |
| int error; |
| if (bs->num_uts == 0) { |
| /* what? */ |
| return 1; |
| } |
| /* if there is only 1 then we will just skip straight to freeing the memory and setting |
| * the arrays to NULL */ |
| if (bs->num_uts > 1) { |
| new_uts = allocman_mspace_alloc(bs->alloc, sizeof(cspacepath_t) * (bs->num_uts - 1), &error); |
| if (error) { |
| return error; |
| } |
| new_size_bits = allocman_mspace_alloc(bs->alloc, sizeof(size_t) * (bs->num_uts - 1), & error); |
| if (error) { |
| return error; |
| } |
| new_paddr = allocman_mspace_alloc(bs->alloc, sizeof(uintptr_t) * (bs->num_uts - 1), & error); |
| if (error) { |
| return error; |
| } |
| memcpy(&new_uts[0], &bs->uts[0], i * sizeof(cspacepath_t)); |
| memcpy(&new_uts[i], &bs->uts[i + 1], (bs->num_uts - i - 1) * sizeof(cspacepath_t)); |
| memcpy(&new_size_bits[0], &bs->ut_size_bits[0], i * sizeof(size_t)); |
| memcpy(&new_size_bits[i], &bs->ut_size_bits[i + 1], (bs->num_uts - i - 1) * sizeof(size_t)); |
| memcpy(&new_paddr[0], &bs->ut_paddr[0], i * sizeof(uintptr_t)); |
| memcpy(&new_paddr[i], &bs->ut_paddr[i + 1], (bs->num_uts - i - 1) * sizeof(uintptr_t)); |
| } |
| allocman_mspace_free(bs->alloc, bs->uts, sizeof(cspacepath_t) * (bs->num_uts)); |
| allocman_mspace_free(bs->alloc, bs->ut_size_bits, sizeof(size_t) * (bs->num_uts)); |
| allocman_mspace_free(bs->alloc, bs->ut_paddr, sizeof(uintptr_t) * (bs->num_uts)); |
| bs->uts = new_uts; |
| bs->ut_size_bits = new_size_bits; |
| bs->ut_paddr = new_paddr; |
| bs->num_uts--; |
| return 0; |
| } |
| |
| static int _split_ut(bootstrap_info_t *bs, cspacepath_t ut, cspacepath_t p1, cspacepath_t p2, size_t size) { |
| int error; |
| error = seL4_Untyped_Retype(ut.capPtr, seL4_UntypedObject, size, p1.root, p1.dest, p1.destDepth, p1.offset, 1); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| error = seL4_Untyped_Retype(ut.capPtr, seL4_UntypedObject, size, p2.root, p2.dest, p2.destDepth, p2.offset, 1); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int _retype_cnode(bootstrap_info_t *bs, cspacepath_t ut, cspacepath_t cnode, seL4_Word sel4_size) { |
| int error; |
| error = seL4_Untyped_Retype(ut.capPtr, seL4_CapTableObject, sel4_size, |
| cnode.root, cnode.dest, cnode.destDepth, cnode.offset, 1); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int bootstrap_allocate_cnode(bootstrap_info_t *bs, size_t size, cspacepath_t *slot) { |
| size_t ut_size; |
| int i; |
| int best = -1; |
| uintptr_t best_paddr; |
| size_t best_size; |
| int error; |
| cspacepath_t best_path; |
| if (!bs->have_boot_cspace) { |
| return 1; |
| } |
| ut_size = size + seL4_SlotBits; |
| /* find the smallest untyped to allocate from */ |
| for (i = 0; i < bs->num_uts; i++) { |
| if (bs->ut_size_bits[i] >= ut_size && ( best == -1 || (bs->ut_size_bits[best] > bs->ut_size_bits[i]) ) ) { |
| best = i; |
| } |
| } |
| if (best == -1) { |
| return 1; |
| } |
| best_size = bs->ut_size_bits[best]; |
| best_path = bs->uts[best]; |
| best_paddr = bs->ut_paddr[best]; |
| error = _remove_ut(bs, best); |
| if (error) { |
| return error; |
| } |
| while(best_size > ut_size) { |
| /* split the untyped in half */ |
| cspacepath_t slot1, slot2; |
| size_t temp_size; |
| error = bs->boot_cspace.alloc(bs->alloc, bs->boot_cspace.cspace, &slot1); |
| if (error) { |
| return error; |
| } |
| error = bs->boot_cspace.alloc(bs->alloc, bs->boot_cspace.cspace, &slot2); |
| if (error) { |
| return error; |
| } |
| error = _split_ut(bs, best_path, slot1, slot2, best_size - 1); |
| if (error) { |
| return error; |
| } |
| /* put one half back in the uts list, and then we keep going with the other one. for the purposes |
| * of book keeping physical addresses it is important to note that we are putting the |
| * FIRST half back in */ |
| temp_size = best_size - 1; |
| error = bootstrap_add_untypeds(bs, 1, &slot1, &temp_size, &best_paddr); |
| if (error) { |
| return error; |
| } |
| best_size--; |
| best_paddr = best_paddr ? best_paddr + BIT(best_size) : 0; |
| best_path = slot2; |
| } |
| error = bs->boot_cspace.alloc(bs->alloc, bs->boot_cspace.cspace, slot); |
| if (error) { |
| return error; |
| } |
| error = _retype_cnode(bs, best_path, *slot, size); |
| return error; |
| } |
| |
| static int bootstrap_use_current_cspace(bootstrap_info_t *bs, cspace_interface_t cspace) { |
| int error; |
| error = allocman_attach_cspace(bs->alloc, cspace); |
| if (error) { |
| return error; |
| } |
| bs->boot_cspace = cspace; |
| bs->have_boot_cspace = 1; |
| return 0; |
| } |
| |
| static int bootstrap_use_current_1level_cspace(bootstrap_info_t *bs, seL4_CPtr cnode, size_t size_bits, size_t start_free_index, size_t end_free_index) { |
| cspace_single_level_t *cspace; |
| int error; |
| cspace = allocman_mspace_alloc(bs->alloc, sizeof(*cspace), &error); |
| if (error) { |
| LOG_ERROR("Initial memory pool too small to allocate cspace allocator"); |
| return error; |
| } |
| error = cspace_single_level_create(bs->alloc, cspace, (struct cspace_single_level_config){ |
| .cnode = cnode, |
| .cnode_size_bits = size_bits, |
| .cnode_guard_bits = seL4_WordBits - size_bits, |
| .first_slot = start_free_index, |
| .end_slot = end_free_index |
| }); |
| if (error) { |
| LOG_ERROR("Failed to initialize cspace allocator"); |
| return error; |
| } |
| error = bootstrap_use_current_cspace(bs, cspace_single_level_make_interface(cspace)); |
| if (error) { |
| LOG_ERROR("Failed to bootstrap current cspace"); |
| } |
| return error; |
| } |
| |
| static int bootstrap_new_1level_cspace(bootstrap_info_t *bs, int size) { |
| cspacepath_t node; |
| cspace_single_level_t *cspace; |
| int error; |
| /* create the actual cnodes */ |
| error = bootstrap_allocate_cnode(bs, size, &node); |
| if (error) { |
| return error; |
| } |
| /* put a cap back to ourselves */ |
| error = seL4_CNode_Mint( |
| node.capPtr, 1, size, |
| node.root, node.capPtr, node.capDepth, |
| seL4_AllRights, seL4_CapData_Guard_new(0, seL4_WordBits - size)); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| /* put our old cnode into a slot */ |
| error = seL4_CNode_Copy( |
| node.capPtr, 2, size, |
| bs->boot_cnode.root, bs->boot_cnode.capPtr, bs->boot_cnode.capDepth, |
| seL4_AllRights); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| /* now we can call set space */ |
| error = seL4_TCB_SetSpace(bs->tcb.capPtr, 0, node.capPtr, |
| seL4_CapData_Guard_new(0, seL4_WordBits - size), |
| bs->pd.capPtr, seL4_NilData); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| /* create the cspace now */ |
| cspace = allocman_mspace_alloc(bs->alloc, sizeof(*cspace), &error); |
| if (error) { |
| return error; |
| } |
| error = cspace_single_level_create(bs->alloc, cspace, (struct cspace_single_level_config){ |
| .cnode = 1, |
| .cnode_size_bits = size, |
| .cnode_guard_bits = seL4_WordBits - size, |
| .first_slot = 3, |
| .end_slot = BIT(size)}); |
| if (error) { |
| return error; |
| } |
| error = allocman_attach_cspace(bs->alloc, cspace_single_level_make_interface(cspace)); |
| if (error) { |
| return error; |
| } |
| /* construct path to our old cnode */ |
| bs->old_cnode = allocman_cspace_make_path(bs->alloc, 2); |
| /* update where our current booting cnode is */ |
| bs->boot_cnode = allocman_cspace_make_path(bs->alloc, 1); |
| /* any untypeds are no longer valid */ |
| bs->uts_in_current_cspace = 0; |
| return 0; |
| } |
| int bootstrap_transfer_caps_simple(bootstrap_info_t *bs, simple_t *simple) { |
| |
| int error; |
| size_t i; |
| size_t cap_count = simple_get_cap_count(simple); |
| seL4_CPtr cnode = simple_get_cnode(simple); |
| |
| for(i = 0; i < cap_count; i++) { |
| seL4_CPtr pos = simple_get_nth_cap(simple,i); |
| |
| /* Because we are going to switch root cnodes don't move the old cnode cap |
| * The cap to the real root cnode is already there. |
| * Also don't move any untyped caps, the untyped allocator is looking |
| * after those now. |
| */ |
| if(pos == cnode || simple_is_untyped_cap(simple, pos)) continue; |
| |
| cspacepath_t slot = _cspace_two_level_make_path(bs->alloc->cspace.cspace, pos); |
| |
| if(pos == bs->tcb.capPtr || pos == bs->pd.capPtr) { |
| error = seL4_CNode_Copy(slot.root, pos, slot.capDepth, |
| bs->old_cnode.capPtr, pos, bs->old_cnode.capDepth, |
| seL4_AllRights); |
| } else { |
| error = seL4_CNode_Move( |
| slot.root, pos, slot.capDepth, |
| bs->old_cnode.capPtr, pos, bs->old_cnode.capDepth); |
| } |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int bootstrap_new_2level_cspace(bootstrap_info_t *bs, size_t l1size, size_t l2size, seL4_CPtr cnode, seL4_CPtr old_cnode, size_t total_caps) { |
| cspacepath_t l1node; |
| cspacepath_t l2node; |
| cspace_two_level_t *cspace; |
| int error; |
| size_t i; |
| seL4_CPtr l2nodeforbackpointer = 0; |
| seL4_CPtr last_cap = MAX(cnode, old_cnode); |
| /* create the actual cnodes */ |
| error = bootstrap_allocate_cnode(bs, l1size, &l1node); |
| if (error) { |
| return error; |
| } |
| error = bootstrap_allocate_cnode(bs, l2size, &l2node); |
| if (error) { |
| return error; |
| } |
| /* put a cap back to the level 1 in the level 2. That way we have a cap at depth 32 to our cspace */ |
| error = seL4_CNode_Mint( |
| l2node.capPtr, cnode, l2size, |
| l1node.root, l1node.capPtr, l1node.capDepth, |
| seL4_AllRights, seL4_CapData_Guard_new(0, seL4_WordBits - l1size - l2size)); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| |
| for(i = 0; i < (total_caps / BIT(l2size)) + 1; i++) { |
| if(i != 0) { |
| error = bootstrap_allocate_cnode(bs, l2size, &l2node); |
| if(error) { |
| return error; |
| } |
| } |
| /* see if this is the l2 node we will need for installing |
| * the pointer back to our old cnode */ |
| if (old_cnode / BIT(l2size) == i) { |
| l2nodeforbackpointer = l2node.capPtr; |
| } |
| /* put the level 2 slot into the level 1 */ |
| error = seL4_CNode_Copy( |
| l1node.capPtr, i, l1size, |
| l2node.root, l2node.capPtr, l2node.capDepth, |
| seL4_AllRights); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| |
| } |
| size_t end_existing_index = i; |
| /* put our old cnode into a slot in the level 2 one */ |
| error = seL4_CNode_Copy( |
| l2nodeforbackpointer, old_cnode & MASK(l2size), l2size, |
| bs->boot_cnode.root, bs->boot_cnode.capPtr, |
| bs->boot_cnode.capDepth, seL4_AllRights); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| /* now we can call set space */ |
| error = seL4_TCB_SetSpace(bs->tcb.capPtr, 0, l1node.capPtr, |
| seL4_CapData_Guard_new(0, seL4_WordBits - l1size - l2size), |
| bs->pd.capPtr, seL4_NilData); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| /* create the cspace now */ |
| cspace = allocman_mspace_alloc(bs->alloc, sizeof(*cspace), &error); |
| if (error) { |
| return error; |
| } |
| error = cspace_two_level_create(bs->alloc, cspace, (struct cspace_two_level_config){ |
| .cnode = cnode, |
| .cnode_size_bits = l1size, |
| .cnode_guard_bits = seL4_WordBits - l1size - l2size, |
| .first_slot = 0, |
| .end_slot = BIT(l1size), |
| .level_two_bits = l2size, |
| .start_existing_index = 0, |
| .end_existing_index = end_existing_index, |
| .start_existing_slot = 0, |
| .end_existing_slot = last_cap + 1}); |
| if (error) { |
| return error; |
| } |
| error = allocman_attach_cspace(bs->alloc, cspace_two_level_make_interface(cspace)); |
| if (error) { |
| return error; |
| } |
| /* construct path to our old cnode */ |
| bs->old_cnode = _cspace_two_level_make_path(cspace, old_cnode); |
| /* update where our current booting cnode is */ |
| bs->boot_cnode = _cspace_two_level_make_path(cspace, cnode); |
| /* any untypeds are no longer valid */ |
| bs->uts_in_current_cspace = 0; |
| return 0; |
| } |
| |
| static void bootstrap_update_untypeds(bootstrap_info_t *bs) { |
| int i; |
| for (i = 0; i < bs->num_uts; i++) { |
| bs->uts[i].root = bs->old_cnode.capPtr; |
| } |
| } |
| |
| static void bootstrap_set_pd_tcb_bootinfo(bootstrap_info_t *bs) { |
| bs->pd = bs->boot_cspace.make_path(bs->boot_cspace.cspace, seL4_CapInitThreadPD); |
| bs->tcb = bs->boot_cspace.make_path(bs->boot_cspace.cspace, seL4_CapInitThreadTCB); |
| } |
| |
| static void bootstrap_set_pd_tcb(bootstrap_info_t *bs, cspacepath_t pd, cspacepath_t tcb) { |
| bs->pd = pd; |
| bs->tcb = tcb; |
| } |
| |
| static int bootstrap_move_untypeds(bootstrap_info_t *bs) { |
| int i; |
| int error; |
| for (i = 0; i < bs->num_uts; i++) { |
| cspacepath_t slot; |
| error = allocman_cspace_alloc(bs->alloc, &slot); |
| if (error) { |
| return error; |
| } |
| error = vka_cnode_move(&slot, &bs->uts[i]); |
| if (error != seL4_NoError) { |
| return 1; |
| } |
| bs->uts[i] = slot; |
| } |
| bs->uts_in_current_cspace = 1; |
| return 0; |
| } |
| |
| static int bootstrap_create_utspace(bootstrap_info_t *bs) { |
| int error; |
| UTMAN_TYPE *utspace; |
| utspace = allocman_mspace_alloc(bs->alloc, sizeof(*utspace), &error); |
| if (error) { |
| LOG_ERROR("Initial memory pool too small to allocate untyped allocator"); |
| return error; |
| } |
| UTMAN_CREATE(utspace); |
| /* we can shove it in the allocman before we start telling it about its untypeds */ |
| error = allocman_attach_utspace(bs->alloc, UTMAN_MAKE_INTERFACE(utspace)); |
| if (error) { |
| return error; |
| } |
| |
| if (bs->num_uts > 0) { |
| error = UTMAN_ADD_UTS(bs->alloc, utspace, bs->num_uts, bs->uts, bs->ut_size_bits, bs->ut_paddr); |
| if (error) { |
| LOG_ERROR("Failed to add untypeds to untyped allocator"); |
| return error; |
| } |
| } |
| return 0; |
| } |
| |
| bootstrap_info_t *bootstrap_create_info(size_t pool_size, void *pool) { |
| allocman_t *alloc; |
| bootstrap_info_t *info; |
| /* bootstrap an allocman from the pool */ |
| alloc = bootstrap_create_allocman(pool_size, pool); |
| if (!alloc) { |
| return NULL; |
| } |
| /* create the bootstrapping info */ |
| info = _create_info(alloc); |
| if (!info) { |
| return NULL; |
| } |
| return info; |
| } |
| |
| static int _slot_memory_reservations(allocman_t *alloc) { |
| int error; |
| /* these numbers are pulled completely out of my arse (although given I wrote |
| * the allocators my gut feeling should have some weight...) */ |
| error = allocman_configure_max_freed_slots(alloc, 10); |
| if (error) { |
| return error; |
| } |
| error = allocman_configure_max_freed_memory_chunks(alloc, 20); |
| if (error) { |
| return error; |
| } |
| error = allocman_configure_max_freed_untyped_chunks(alloc, 10); |
| if (error) { |
| return error; |
| } |
| error = allocman_configure_cspace_reserve(alloc, 30); |
| if (error) { |
| return error; |
| } |
| return 0; |
| } |
| |
| static int _cnode_reservation(allocman_t *alloc, size_t cnode_size) { |
| /* assume one extra level 2 cnode is all we need to keep around */ |
| return allocman_configure_utspace_reserve(alloc, (struct allocman_utspace_chunk) {cnode_size + seL4_SlotBits, seL4_CapTableObject, 1}); |
| } |
| |
| allocman_t *bootstrap_use_current_1level(seL4_CPtr root_cnode, size_t cnode_size, seL4_CPtr start_slot, seL4_CPtr end_slot, size_t pool_size, void *pool) { |
| allocman_t *alloc; |
| int error; |
| bootstrap_info_t *info; |
| /* create the bootstrapping info */ |
| info = bootstrap_create_info(pool_size, pool); |
| if (!info) { |
| LOG_ERROR("Failed to create bootstrap info"); |
| return NULL; |
| } |
| /* we will use the current cspace */ |
| error = bootstrap_use_current_1level_cspace(info, root_cnode, cnode_size, start_slot, end_slot); |
| if (error) { |
| LOG_ERROR("Failed to boostrap in the current cspace"); |
| return NULL; |
| } |
| /* set the pd and tcb */ |
| bootstrap_set_pd_tcb_bootinfo(info); |
| /* can now create untyped allocator */ |
| error = bootstrap_create_utspace(info); |
| if (error) { |
| LOG_ERROR("Failed to create the untyped allocator"); |
| return NULL; |
| } |
| /* add normal slot reservations as well as reservations for memory system. No cnode |
| * reservations since using current cnode */ |
| error = _slot_memory_reservations(info->alloc); |
| if (error) { |
| LOG_ERROR("Failed to add slot and memory reservations"); |
| return NULL; |
| } |
| /* all done */ |
| alloc = info->alloc; |
| bootstrap_free_info(info); |
| return alloc; |
| } |
| |
| static allocman_t *_post_new_cspace_common(bootstrap_info_t *info, cspacepath_t *oldroot) { |
| allocman_t *alloc; |
| int error; |
| /* update any resources */ |
| bootstrap_update_untypeds(info); |
| /* move untypeds into new cspace */ |
| error = bootstrap_move_untypeds(info); |
| if (error) { |
| return NULL; |
| } |
| /* can now create untyped allocator */ |
| error = bootstrap_create_utspace(info); |
| if (error) { |
| return NULL; |
| } |
| /* add normal slot reservations as well as reservations for memory system. We do not |
| * know the cspace geometry, so cannot add cnode reservations */ |
| error = _slot_memory_reservations(info->alloc); |
| if (error) { |
| return NULL; |
| } |
| /* give the location of the cap back to the original root cnode */ |
| if (oldroot) { |
| *oldroot = info->old_cnode; |
| } |
| /* all done */ |
| alloc = info->alloc; |
| return alloc; |
| } |
| |
| /* this does not free the underlying 'info' */ |
| static allocman_t *_bootstrap_new_level1(bootstrap_info_t *info, size_t cnode_size, cspacepath_t tcb, cspacepath_t pd, cspacepath_t *oldroot) { |
| int error; |
| /* set the pd and tcb */ |
| bootstrap_set_pd_tcb(info, pd, tcb); |
| /* create a new one level cspace and switch to it */ |
| error = bootstrap_new_1level_cspace(info, cnode_size); |
| if (error) { |
| return NULL; |
| } |
| /* perform post cspace switch tasks (move untypeds and finish creating the allocman) */ |
| return _post_new_cspace_common(info, oldroot); |
| } |
| |
| /* this does not free the underlying 'info' */ |
| static allocman_t *_bootstrap_new_level2(bootstrap_info_t *info, size_t l1size, size_t l2size, cspacepath_t tcb, cspacepath_t pd, cspacepath_t *oldroot) { |
| int error; |
| allocman_t *alloc; |
| /* set the pd and tcb */ |
| bootstrap_set_pd_tcb(info, pd, tcb); |
| /* create a new one level cspace and switch to it |
| * place the cap to the root cnode at slot 2 and the cap to the old cnode at slot 1 |
| */ |
| size_t total_caps = info->num_uts; |
| if(info->have_boot_cspace) { |
| cspacepath_t to_slot; |
| info->boot_cspace.alloc(info->alloc, info->boot_cspace.cspace, &to_slot); |
| total_caps += MAX(2, to_slot.capPtr); |
| error = bootstrap_new_2level_cspace(info, l1size, l2size, 2, to_slot.capPtr, total_caps); |
| } else { |
| total_caps += 2; |
| error = bootstrap_new_2level_cspace(info, l1size, l2size, 2, 1, total_caps); |
| } |
| if (error) { |
| return NULL; |
| } |
| /* perform post cspace switch tasks (move untypeds and finish creating the allocman) */ |
| alloc = _post_new_cspace_common(info, oldroot); |
| if (!alloc) { |
| return NULL; |
| } |
| /* add reservations for a second level cnode */ |
| error = _cnode_reservation(alloc, l2size); |
| if (error) { |
| return NULL; |
| } |
| return alloc; |
| } |
| |
| allocman_t *bootstrap_new_1level(bootstrap_info_t *info, size_t cnode_size, cspacepath_t tcb, cspacepath_t pd, cspacepath_t *oldroot) { |
| allocman_t *alloc = _bootstrap_new_level1(info, cnode_size, tcb, pd, oldroot); |
| if (!alloc) { |
| return NULL; |
| } |
| bootstrap_free_info(info); |
| return alloc; |
| } |
| |
| allocman_t *bootstrap_new_2level(bootstrap_info_t *info, size_t l1size, size_t l2size, cspacepath_t tcb, cspacepath_t pd, cspacepath_t *oldroot) { |
| allocman_t *alloc = _bootstrap_new_level2(info, l1size, l2size, tcb, pd, oldroot); |
| if (!alloc) { |
| return NULL; |
| } |
| bootstrap_free_info(info); |
| return alloc; |
| } |
| |
| static bootstrap_info_t *_start_new_from_bootinfo(seL4_BootInfo *bi, size_t pool_size, void *pool) { |
| int error; |
| bootstrap_info_t *info; |
| /* create the bootstrapping info */ |
| info = bootstrap_create_info(pool_size, pool); |
| if (!info) { |
| return NULL; |
| } |
| /* we will use the current cspace (as descrbied by bootinfo) temporarily for bootstrapping */ |
| error = bootstrap_create_temp_bootinfo_cspace(info, bi); |
| if (error) { |
| return NULL; |
| } |
| /* give all the bootinfo untypeds */ |
| error = bootstrap_add_untypeds_from_bootinfo(info, bi); |
| if (error) { |
| return NULL; |
| } |
| return info; |
| } |
| |
| static allocman_t *_new_from_bootinfo_common(bootstrap_info_t *info, cspace_simple1level_t **old_cspace) { |
| int error; |
| allocman_t *alloc; |
| /* allocate old cspace if desired */ |
| if (old_cspace) { |
| *old_cspace = allocman_mspace_alloc(info->alloc, sizeof(**old_cspace), &error); |
| if (error) { |
| return NULL; |
| } |
| /* we are relying on internal knowledge of this allocator to know that this copy operation is okay */ |
| **old_cspace = info->maybe_boot_cspace; |
| } |
| /* all done */ |
| alloc = info->alloc; |
| bootstrap_free_info(info); |
| return alloc; |
| } |
| |
| allocman_t *bootstrap_new_1level_bootinfo(seL4_BootInfo *bi, size_t cnode_size, size_t pool_size, void *pool, cspace_simple1level_t **old_cspace) { |
| allocman_t *alloc; |
| bootstrap_info_t *info; |
| /* create the bootstrapping info and fill in as much from bootinfo as we can */ |
| info = _start_new_from_bootinfo(bi, pool_size, pool); |
| if (!info) { |
| return NULL; |
| } |
| /* create cspace, switch to it and finish creating the allocman */ |
| alloc = _bootstrap_new_level1(info, |
| cnode_size, |
| info->boot_cspace.make_path(info->boot_cspace.cspace, seL4_CapInitThreadTCB), |
| info->boot_cspace.make_path(info->boot_cspace.cspace, seL4_CapInitThreadPD), |
| NULL); |
| if (!alloc) { |
| return NULL; |
| } |
| /* do common cleanup */ |
| return _new_from_bootinfo_common(info, old_cspace); |
| } |
| |
| allocman_t *bootstrap_new_2level_bootinfo(seL4_BootInfo *bi, size_t l1size, size_t l2size, size_t pool_size, void *pool, cspace_simple1level_t **old_cspace) { |
| allocman_t *alloc; |
| bootstrap_info_t *info; |
| /* create the bootstrapping info and fill in as much from bootinfo as we can */ |
| info = _start_new_from_bootinfo(bi, pool_size, pool); |
| if (!info) { |
| return NULL; |
| } |
| /* create cspace, switch to it and finish creating the allocman */ |
| alloc = _bootstrap_new_level2(info, |
| l1size, |
| l2size, |
| info->boot_cspace.make_path(info->boot_cspace.cspace, seL4_CapInitThreadTCB), |
| info->boot_cspace.make_path(info->boot_cspace.cspace, seL4_CapInitThreadPD), |
| NULL); |
| if (!alloc) { |
| return NULL; |
| } |
| /* do common cleanup */ |
| return _new_from_bootinfo_common(info, old_cspace); |
| } |
| |
| int allocman_add_simple_untypeds(allocman_t *alloc, simple_t *simple) { |
| int error; |
| size_t i; |
| size_t total_untyped = simple_get_untyped_count(simple); |
| |
| for(i = 0; i < total_untyped; i++) { |
| size_t size_bits; |
| uintptr_t paddr; |
| cspacepath_t path = allocman_cspace_make_path(alloc, simple_get_nth_untyped(simple, i, &size_bits, &paddr)); |
| error = allocman_utspace_add_uts(alloc, 1, &path, &size_bits, &paddr); |
| |
| if(error) { |
| return error; |
| } |
| } |
| return 0; |
| } |
| |
| allocman_t *bootstrap_use_current_simple(simple_t *simple, size_t pool_size, void *pool) { |
| allocman_t *allocman; |
| int error; |
| /* Initialize inside the current 1 level cspace as defined by simple */ |
| allocman = bootstrap_use_current_1level(simple_get_cnode(simple), simple_get_cnode_size_bits(simple), simple_last_valid_cap(simple) + 1, BIT(simple_get_cnode_size_bits(simple)), pool_size, pool); |
| if (!allocman) { |
| LOG_ERROR("Failed to initialize an allocman"); |
| return allocman; |
| } |
| error = allocman_add_simple_untypeds(allocman, simple); |
| if (error) { |
| /* We have no way to cleanup the allocman we started bootstrapping */ |
| LOG_ERROR("Failed in call to allocman_add_simple_untypeds, cannot cleanup, leaking memory"); |
| return NULL; |
| } |
| return allocman; |
| } |
| |
| allocman_t *bootstrap_new_2level_simple(simple_t *simple, size_t l1size, size_t l2size, size_t pool_size, void *pool) { |
| allocman_t *alloc; |
| int error; |
| |
| bootstrap_info_t *bootstrap = bootstrap_create_info(pool_size, pool); |
| if (bootstrap == NULL) { |
| return NULL; |
| } |
| bootstrap->simple = simple; |
| |
| cspace_simple1level_create(&bootstrap->maybe_boot_cspace, (struct cspace_simple1level_config){ |
| .cnode = simple_get_cnode(simple), |
| .cnode_size_bits = simple_get_cnode_size_bits(simple), |
| .cnode_guard_bits = seL4_WordBits - simple_get_cnode_size_bits(simple), |
| .first_slot = simple_get_nth_cap(simple, simple_get_cap_count(simple)-1) + 1, |
| .end_slot = BIT(simple_get_cnode_size_bits(simple)) |
| }); |
| |
| cspacepath_t init_cnode_path = _cspace_simple1level_make_path(&bootstrap->maybe_boot_cspace, simple_get_cnode(simple)); |
| bootstrap_set_boot_cspace(bootstrap, cspace_simple1level_make_interface(&bootstrap->maybe_boot_cspace), init_cnode_path); |
| |
| /* Tell the boostrapping allocator about the untypeds in the system */ |
| bootstrap_add_untypeds_from_simple(bootstrap, simple); |
| |
| cspacepath_t tcb = _cspace_simple1level_make_path(&bootstrap->maybe_boot_cspace, simple_get_tcb(simple)); |
| cspacepath_t pd = _cspace_simple1level_make_path(&bootstrap->maybe_boot_cspace, simple_get_pd(simple)); |
| |
| alloc = _bootstrap_new_level2(bootstrap, l1size, l2size, tcb, pd, NULL); |
| if (!alloc) { |
| return NULL; |
| } |
| |
| /* Take all the caps in the boot cnode and put in them in the same location in the new cspace */ |
| error = bootstrap_transfer_caps_simple(bootstrap, simple); |
| if(error) { |
| return NULL; |
| } |
| |
| /* Cleanup */ |
| bootstrap_free_info(bootstrap); |
| |
| return alloc; |
| } |
| |
| |
| static int allocman_add_bootinfo_untypeds(allocman_t *alloc, seL4_BootInfo *bi) { |
| seL4_CPtr i; |
| int error; |
| for (i = bi->untyped.start; i < bi->untyped.end; i++) { |
| cspacepath_t slot; |
| size_t size_bits; |
| uintptr_t paddr; |
| slot = allocman_cspace_make_path(alloc, i); |
| size_bits = bi->untypedSizeBitsList[i - bi->untyped.start]; |
| paddr = bi->untypedPaddrList[i - bi->untyped.start]; |
| error = allocman_utspace_add_uts(alloc, 1, &slot, &size_bits, &paddr); |
| if (error) { |
| return error; |
| } |
| } |
| return 0; |
| } |
| |
| allocman_t *bootstrap_use_bootinfo(seL4_BootInfo *bi, size_t pool_size, void *pool) { |
| allocman_t *alloc; |
| int error; |
| /* use the current single level cspace as described by boot info */ |
| alloc = bootstrap_use_current_1level(seL4_CapInitThreadCNode, |
| bi->initThreadCNodeSizeBits, |
| bi->empty.start, |
| bi->empty.end, |
| pool_size, |
| pool); |
| if (!alloc) { |
| return NULL; |
| } |
| /* now add all the untypeds described in bootinfo */ |
| error = allocman_add_bootinfo_untypeds(alloc, bi); |
| if (error) { |
| return NULL; |
| } |
| return alloc; |
| } |
| |
| void bootstrap_configure_virtual_pool(allocman_t *alloc, void *vstart, size_t vsize, seL4_CPtr pd) { |
| /* configure reservation for the virtual pool */ |
| /* assume we are using 4k pages. maybe this should be a Kconfig option at some point? |
| * we ignore any errors */ |
| allocman_configure_utspace_reserve(alloc, (struct allocman_utspace_chunk) {vka_get_object_size(seL4_ARCH_4KPage, 0), seL4_ARCH_4KPage, 3}); |
| allocman_configure_utspace_reserve(alloc, (struct allocman_utspace_chunk) {vka_get_object_size(seL4_ARCH_PageTableObject, 0), seL4_ARCH_PageTableObject, 1}); |
| allocman_sel4_arch_configure_reservations(alloc); |
| mspace_dual_pool_attach_virtual( |
| (mspace_dual_pool_t*)alloc->mspace.mspace, |
| (struct mspace_virtual_pool_config){ |
| .vstart = vstart, |
| .size = vsize, |
| .pd = pd |
| } |
| ); |
| } |