This change allows us to know, from just the kernel and dtb, where user level untyped objects start being allocated from. - allocate rootserver objects from last available freemem region. - move create_rootserver_objects call into init_freemem.
632 lines
22 KiB
C
632 lines
22 KiB
C
/*
|
|
* Copyright 2014, General Dynamics C4 Systems
|
|
*
|
|
* This software may be distributed and modified according to the terms of
|
|
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
|
|
* See "LICENSE_GPLv2.txt" for details.
|
|
*
|
|
* @TAG(GD_GPL)
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <kernel/boot.h>
|
|
#include <kernel/thread.h>
|
|
#include <machine/io.h>
|
|
#include <machine/registerset.h>
|
|
#include <model/statedata.h>
|
|
#include <arch/machine.h>
|
|
#include <arch/kernel/boot.h>
|
|
#include <arch/kernel/vspace.h>
|
|
#include <linker.h>
|
|
#include <plat/machine/hardware.h>
|
|
#include <util.h>
|
|
|
|
/* (node-local) state accessed only during bootstrapping */
|
|
ndks_boot_t ndks_boot BOOT_DATA;
|
|
|
|
rootserver_mem_t rootserver BOOT_DATA;
|
|
static region_t rootserver_mem BOOT_DATA;
|
|
|
|
BOOT_CODE bool_t insert_region(region_t reg)
|
|
{
|
|
word_t i;
|
|
|
|
assert(reg.start <= reg.end);
|
|
if (is_reg_empty(reg)) {
|
|
return true;
|
|
}
|
|
for (i = 0; i < MAX_NUM_FREEMEM_REG; i++) {
|
|
if (is_reg_empty(ndks_boot.freemem[i])) {
|
|
ndks_boot.freemem[i] = reg;
|
|
return true;
|
|
}
|
|
}
|
|
#ifdef CONFIG_ARCH_ARM
|
|
/* boot.h should have calculated MAX_NUM_FREEMEM_REG correctly.
|
|
* If we've run out, then something is wrong.
|
|
* Note that the capDL allocation toolchain does not know about
|
|
* MAX_NUM_FREEMEM_REG, so throwing away regions may prevent
|
|
* capDL applications from being loaded! */
|
|
printf("Can't fit memory region 0x%lx-0x%lx, try increasing MAX_NUM_FREEMEM_REG (currently %d)\n",
|
|
reg.start, reg.end, (int)MAX_NUM_FREEMEM_REG);
|
|
assert(!"Ran out of freemem slots");
|
|
#else
|
|
printf("Dropping memory region 0x%lx-0x%lx, try increasing MAX_NUM_FREEMEM_REG (currently %d)\n",
|
|
reg.start, reg.end, (int)MAX_NUM_FREEMEM_REG);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
BOOT_CODE static pptr_t alloc_rootserver_obj(word_t size_bits, word_t n)
|
|
{
|
|
pptr_t allocated = rootserver_mem.start;
|
|
/* allocated memory must be aligned */
|
|
assert(allocated % BIT(size_bits) == 0);
|
|
rootserver_mem.start += (n * BIT(size_bits));
|
|
/* we must not have run out of memory */
|
|
assert(rootserver_mem.start <= rootserver_mem.end);
|
|
memzero((void *) allocated, n * BIT(size_bits));
|
|
return allocated;
|
|
}
|
|
|
|
BOOT_CODE static word_t rootserver_max_size_bits(word_t extra_bi_size_bits)
|
|
{
|
|
word_t cnode_size_bits = CONFIG_ROOT_CNODE_SIZE_BITS + seL4_SlotBits;
|
|
word_t max = MAX(cnode_size_bits, seL4_VSpaceBits);
|
|
return MAX(max, extra_bi_size_bits);
|
|
}
|
|
|
|
BOOT_CODE static word_t calculate_rootserver_size(v_region_t v_reg, word_t extra_bi_size_bits)
|
|
{
|
|
/* work out how much memory we need for root server objects */
|
|
word_t size = BIT(CONFIG_ROOT_CNODE_SIZE_BITS + seL4_SlotBits);
|
|
size += BIT(seL4_TCBBits); // root thread tcb
|
|
size += 2 * BIT(seL4_PageBits); // boot info + ipc buf
|
|
size += BIT(seL4_ASIDPoolBits);
|
|
size += extra_bi_size_bits > 0 ? BIT(extra_bi_size_bits) : 0;
|
|
size += BIT(seL4_VSpaceBits); // root vspace
|
|
/* for all archs, seL4_PageTable Bits is the size of all non top-level paging structures */
|
|
return size + arch_get_n_paging(v_reg) * BIT(seL4_PageTableBits);
|
|
}
|
|
|
|
BOOT_CODE static void maybe_alloc_extra_bi(word_t cmp_size_bits, word_t extra_bi_size_bits)
|
|
{
|
|
if (extra_bi_size_bits >= cmp_size_bits && rootserver.extra_bi == 0) {
|
|
rootserver.extra_bi = alloc_rootserver_obj(extra_bi_size_bits, 1);
|
|
}
|
|
}
|
|
|
|
BOOT_CODE void create_rootserver_objects(pptr_t start, v_region_t v_reg, word_t extra_bi_size_bits)
|
|
{
|
|
/* the largest object the PD, the root cnode, or the extra boot info */
|
|
word_t cnode_size_bits = CONFIG_ROOT_CNODE_SIZE_BITS + seL4_SlotBits;
|
|
word_t max = rootserver_max_size_bits(extra_bi_size_bits);
|
|
|
|
word_t size = calculate_rootserver_size(v_reg, extra_bi_size_bits);
|
|
rootserver_mem.start = start;
|
|
rootserver_mem.end = start + size;
|
|
|
|
maybe_alloc_extra_bi(max, extra_bi_size_bits);
|
|
|
|
/* the root cnode is at least 4k, so it could be larger or smaller than a pd. */
|
|
#if (CONFIG_ROOT_CNODE_SIZE_BITS + seL4_SlotBits) > seL4_VSpaceBits
|
|
rootserver.cnode = alloc_rootserver_obj(cnode_size_bits, 1);
|
|
maybe_alloc_extra_bi(seL4_VSpaceBits, extra_bi_size_bits);
|
|
rootserver.vspace = alloc_rootserver_obj(seL4_VSpaceBits, 1);
|
|
#else
|
|
rootserver.vspace = alloc_rootserver_obj(seL4_VSpaceBits, 1);
|
|
maybe_alloc_extra_bi(cnode_size_bits, extra_bi_size_bits);
|
|
rootserver.cnode = alloc_rootserver_obj(cnode_size_bits, 1);
|
|
#endif
|
|
|
|
/* at this point we are up to creating 4k objects - which is the min size of
|
|
* extra_bi so this is the last chance to allocate it */
|
|
maybe_alloc_extra_bi(seL4_PageBits, extra_bi_size_bits);
|
|
rootserver.asid_pool = alloc_rootserver_obj(seL4_ASIDPoolBits, 1);
|
|
rootserver.ipc_buf = alloc_rootserver_obj(seL4_PageBits, 1);
|
|
rootserver.boot_info = alloc_rootserver_obj(seL4_PageBits, 1);
|
|
|
|
/* TCBs on aarch32 can be larger than page tables in certain configs */
|
|
#if seL4_TCBBits >= seL4_PageTableBits
|
|
rootserver.tcb = alloc_rootserver_obj(seL4_TCBBits, 1);
|
|
#endif
|
|
|
|
/* paging structures are 4k on every arch except aarch32 (1k) */
|
|
word_t n = arch_get_n_paging(v_reg);
|
|
rootserver.paging.start = alloc_rootserver_obj(seL4_PageTableBits, n);
|
|
rootserver.paging.end = rootserver.paging.start + n * BIT(seL4_PageTableBits);
|
|
|
|
/* for most archs, TCBs are smaller than page tables */
|
|
#if seL4_TCBBits < seL4_PageTableBits
|
|
rootserver.tcb = alloc_rootserver_obj(seL4_TCBBits, 1);
|
|
#endif
|
|
/* we should have allocated all our memory */
|
|
assert(rootserver_mem.start == rootserver_mem.end);
|
|
}
|
|
|
|
BOOT_CODE void write_slot(slot_ptr_t slot_ptr, cap_t cap)
|
|
{
|
|
slot_ptr->cap = cap;
|
|
|
|
slot_ptr->cteMDBNode = nullMDBNode;
|
|
mdb_node_ptr_set_mdbRevocable(&slot_ptr->cteMDBNode, true);
|
|
mdb_node_ptr_set_mdbFirstBadged(&slot_ptr->cteMDBNode, true);
|
|
}
|
|
|
|
/* Our root CNode needs to be able to fit all the initial caps and not
|
|
* cover all of memory.
|
|
*/
|
|
compile_assert(root_cnode_size_valid,
|
|
CONFIG_ROOT_CNODE_SIZE_BITS < 32 - seL4_SlotBits &&
|
|
BIT(CONFIG_ROOT_CNODE_SIZE_BITS) >= seL4_NumInitialCaps &&
|
|
BIT(CONFIG_ROOT_CNODE_SIZE_BITS) >= (seL4_PageBits - seL4_SlotBits))
|
|
|
|
BOOT_CODE cap_t
|
|
create_root_cnode(void)
|
|
{
|
|
/* write the number of root CNode slots to global state */
|
|
ndks_boot.slot_pos_max = BIT(CONFIG_ROOT_CNODE_SIZE_BITS);
|
|
|
|
cap_t cap =
|
|
cap_cnode_cap_new(
|
|
CONFIG_ROOT_CNODE_SIZE_BITS, /* radix */
|
|
wordBits - CONFIG_ROOT_CNODE_SIZE_BITS, /* guard size */
|
|
0, /* guard */
|
|
rootserver.cnode /* pptr */
|
|
);
|
|
|
|
/* write the root CNode cap into the root CNode */
|
|
write_slot(SLOT_PTR(rootserver.cnode, seL4_CapInitThreadCNode), cap);
|
|
|
|
return cap;
|
|
}
|
|
|
|
/* Check domain scheduler assumptions. */
|
|
compile_assert(num_domains_valid,
|
|
CONFIG_NUM_DOMAINS >= 1 && CONFIG_NUM_DOMAINS <= 256)
|
|
compile_assert(num_priorities_valid,
|
|
CONFIG_NUM_PRIORITIES >= 1 && CONFIG_NUM_PRIORITIES <= 256)
|
|
|
|
BOOT_CODE void
|
|
create_domain_cap(cap_t root_cnode_cap)
|
|
{
|
|
/* Check domain scheduler assumptions. */
|
|
assert(ksDomScheduleLength > 0);
|
|
for (word_t i = 0; i < ksDomScheduleLength; i++) {
|
|
assert(ksDomSchedule[i].domain < CONFIG_NUM_DOMAINS);
|
|
assert(ksDomSchedule[i].length > 0);
|
|
}
|
|
|
|
cap_t cap = cap_domain_cap_new();
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapDomain), cap);
|
|
}
|
|
|
|
|
|
BOOT_CODE cap_t create_ipcbuf_frame_cap(cap_t root_cnode_cap, cap_t pd_cap, vptr_t vptr)
|
|
{
|
|
clearMemory((void *)rootserver.ipc_buf, PAGE_BITS);
|
|
|
|
/* create a cap of it and write it into the root CNode */
|
|
cap_t cap = create_mapped_it_frame_cap(pd_cap, rootserver.ipc_buf, vptr, IT_ASID, false, false);
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadIPCBuffer), cap);
|
|
|
|
return cap;
|
|
}
|
|
|
|
BOOT_CODE void create_bi_frame_cap(cap_t root_cnode_cap, cap_t pd_cap, vptr_t vptr)
|
|
{
|
|
/* create a cap of it and write it into the root CNode */
|
|
cap_t cap = create_mapped_it_frame_cap(pd_cap, rootserver.boot_info, vptr, IT_ASID, false, false);
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapBootInfoFrame), cap);
|
|
}
|
|
|
|
BOOT_CODE word_t calculate_extra_bi_size_bits(word_t extra_size)
|
|
{
|
|
if (extra_size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
word_t clzl_ret = clzl(ROUND_UP(extra_size, seL4_PageBits));
|
|
/* If region is bigger than a page, make sure we overallocate rather than underallocate */
|
|
if (extra_size & ((1 << clzl_ret) - 1)) {
|
|
clzl_ret--;
|
|
}
|
|
return seL4_WordBits - 1 - clzl_ret;
|
|
}
|
|
|
|
BOOT_CODE void populate_bi_frame(node_id_t node_id, word_t num_nodes, vptr_t ipcbuf_vptr,
|
|
word_t extra_bi_size)
|
|
{
|
|
clearMemory((void *) rootserver.boot_info, BI_FRAME_SIZE_BITS);
|
|
if (extra_bi_size) {
|
|
clearMemory((void *) rootserver.extra_bi, calculate_extra_bi_size_bits(extra_bi_size));
|
|
}
|
|
|
|
/* initialise bootinfo-related global state */
|
|
ndks_boot.bi_frame = BI_PTR(rootserver.boot_info);
|
|
ndks_boot.slot_pos_cur = seL4_NumInitialCaps;
|
|
BI_PTR(rootserver.boot_info)->nodeID = node_id;
|
|
BI_PTR(rootserver.boot_info)->numNodes = num_nodes;
|
|
BI_PTR(rootserver.boot_info)->numIOPTLevels = 0;
|
|
BI_PTR(rootserver.boot_info)->ipcBuffer = (seL4_IPCBuffer *) ipcbuf_vptr;
|
|
BI_PTR(rootserver.boot_info)->initThreadCNodeSizeBits = CONFIG_ROOT_CNODE_SIZE_BITS;
|
|
BI_PTR(rootserver.boot_info)->initThreadDomain = ksDomSchedule[ksDomScheduleIdx].domain;
|
|
BI_PTR(rootserver.boot_info)->extraLen = extra_bi_size;
|
|
}
|
|
|
|
BOOT_CODE bool_t provide_cap(cap_t root_cnode_cap, cap_t cap)
|
|
{
|
|
if (ndks_boot.slot_pos_cur >= ndks_boot.slot_pos_max) {
|
|
printf("Kernel init failed: ran out of cap slots\n");
|
|
return false;
|
|
}
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), ndks_boot.slot_pos_cur), cap);
|
|
ndks_boot.slot_pos_cur++;
|
|
return true;
|
|
}
|
|
|
|
BOOT_CODE create_frames_of_region_ret_t create_frames_of_region(
|
|
cap_t root_cnode_cap,
|
|
cap_t pd_cap,
|
|
region_t reg,
|
|
bool_t do_map,
|
|
sword_t pv_offset
|
|
)
|
|
{
|
|
pptr_t f;
|
|
cap_t frame_cap;
|
|
seL4_SlotPos slot_pos_before;
|
|
seL4_SlotPos slot_pos_after;
|
|
|
|
slot_pos_before = ndks_boot.slot_pos_cur;
|
|
|
|
for (f = reg.start; f < reg.end; f += BIT(PAGE_BITS)) {
|
|
if (do_map) {
|
|
frame_cap = create_mapped_it_frame_cap(pd_cap, f, pptr_to_paddr((void *)(f - pv_offset)), IT_ASID, false, true);
|
|
} else {
|
|
frame_cap = create_unmapped_it_frame_cap(f, false);
|
|
}
|
|
if (!provide_cap(root_cnode_cap, frame_cap))
|
|
return (create_frames_of_region_ret_t) {
|
|
S_REG_EMPTY, false
|
|
};
|
|
}
|
|
|
|
slot_pos_after = ndks_boot.slot_pos_cur;
|
|
|
|
return (create_frames_of_region_ret_t) {
|
|
(seL4_SlotRegion) { slot_pos_before, slot_pos_after }, true
|
|
};
|
|
}
|
|
|
|
BOOT_CODE cap_t create_it_asid_pool(cap_t root_cnode_cap)
|
|
{
|
|
cap_t ap_cap = cap_asid_pool_cap_new(IT_ASID >> asidLowBits, rootserver.asid_pool);
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadASIDPool), ap_cap);
|
|
|
|
/* create ASID control cap */
|
|
write_slot(
|
|
SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapASIDControl),
|
|
cap_asid_control_cap_new()
|
|
);
|
|
|
|
return ap_cap;
|
|
}
|
|
|
|
BOOT_CODE bool_t create_idle_thread(void)
|
|
{
|
|
pptr_t pptr;
|
|
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
for (int i = 0; i < CONFIG_MAX_NUM_NODES; i++) {
|
|
#endif /* ENABLE_SMP_SUPPORT */
|
|
pptr = (pptr_t) &ksIdleThreadTCB[SMP_TERNARY(i, 0)];
|
|
NODE_STATE_ON_CORE(ksIdleThread, i) = TCB_PTR(pptr + TCB_OFFSET);
|
|
configureIdleThread(NODE_STATE_ON_CORE(ksIdleThread, i));
|
|
#ifdef CONFIG_DEBUG_BUILD
|
|
setThreadName(NODE_STATE_ON_CORE(ksIdleThread, i), "idle_thread");
|
|
#endif
|
|
SMP_COND_STATEMENT(NODE_STATE_ON_CORE(ksIdleThread, i)->tcbAffinity = i);
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
}
|
|
#endif /* ENABLE_SMP_SUPPORT */
|
|
return true;
|
|
}
|
|
|
|
BOOT_CODE tcb_t *create_initial_thread(cap_t root_cnode_cap, cap_t it_pd_cap, vptr_t ui_v_entry, vptr_t bi_frame_vptr,
|
|
vptr_t ipcbuf_vptr, cap_t ipcbuf_cap)
|
|
{
|
|
tcb_t *tcb = TCB_PTR(rootserver.tcb + TCB_OFFSET);
|
|
tcb->tcbTimeSlice = CONFIG_TIME_SLICE;
|
|
Arch_initContext(&tcb->tcbArch.tcbContext);
|
|
|
|
/* derive a copy of the IPC buffer cap for inserting */
|
|
deriveCap_ret_t dc_ret = deriveCap(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadIPCBuffer), ipcbuf_cap);
|
|
if (dc_ret.status != EXCEPTION_NONE) {
|
|
printf("Failed to derive copy of IPC Buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* initialise TCB (corresponds directly to abstract specification) */
|
|
cteInsert(
|
|
root_cnode_cap,
|
|
SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadCNode),
|
|
SLOT_PTR(rootserver.tcb, tcbCTable)
|
|
);
|
|
cteInsert(
|
|
it_pd_cap,
|
|
SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadVSpace),
|
|
SLOT_PTR(rootserver.tcb, tcbVTable)
|
|
);
|
|
cteInsert(
|
|
dc_ret.cap,
|
|
SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadIPCBuffer),
|
|
SLOT_PTR(rootserver.tcb, tcbBuffer)
|
|
);
|
|
tcb->tcbIPCBuffer = ipcbuf_vptr;
|
|
|
|
/* Set the root thread's IPC buffer */
|
|
Arch_setTCBIPCBuffer(tcb, ipcbuf_vptr);
|
|
|
|
setRegister(tcb, capRegister, bi_frame_vptr);
|
|
setNextPC(tcb, ui_v_entry);
|
|
|
|
/* initialise TCB */
|
|
tcb->tcbPriority = seL4_MaxPrio;
|
|
tcb->tcbMCP = seL4_MaxPrio;
|
|
setupReplyMaster(tcb);
|
|
setThreadState(tcb, ThreadState_Running);
|
|
|
|
ksCurDomain = ksDomSchedule[ksDomScheduleIdx].domain;
|
|
ksDomainTime = ksDomSchedule[ksDomScheduleIdx].length;
|
|
assert(ksCurDomain < CONFIG_NUM_DOMAINS && ksDomainTime > 0);
|
|
|
|
SMP_COND_STATEMENT(tcb->tcbAffinity = 0);
|
|
|
|
/* create initial thread's TCB cap */
|
|
cap_t cap = cap_thread_cap_new(TCB_REF(tcb));
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadTCB), cap);
|
|
|
|
#ifdef CONFIG_DEBUG_BUILD
|
|
setThreadName(tcb, "rootserver");
|
|
#endif
|
|
|
|
return tcb;
|
|
}
|
|
|
|
BOOT_CODE void init_core_state(tcb_t *scheduler_action)
|
|
{
|
|
#ifdef CONFIG_HAVE_FPU
|
|
NODE_STATE(ksActiveFPUState) = NULL;
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_BUILD
|
|
/* add initial threads to the debug queue */
|
|
NODE_STATE(ksDebugTCBs) = NULL;
|
|
if (scheduler_action != SchedulerAction_ResumeCurrentThread &&
|
|
scheduler_action != SchedulerAction_ChooseNewThread) {
|
|
tcbDebugAppend(scheduler_action);
|
|
}
|
|
tcbDebugAppend(NODE_STATE(ksIdleThread));
|
|
#endif
|
|
NODE_STATE(ksSchedulerAction) = scheduler_action;
|
|
NODE_STATE(ksCurThread) = NODE_STATE(ksIdleThread);
|
|
}
|
|
|
|
BOOT_CODE static bool_t provide_untyped_cap(
|
|
cap_t root_cnode_cap,
|
|
bool_t device_memory,
|
|
pptr_t pptr,
|
|
word_t size_bits,
|
|
seL4_SlotPos first_untyped_slot
|
|
)
|
|
{
|
|
bool_t ret;
|
|
cap_t ut_cap;
|
|
word_t i = ndks_boot.slot_pos_cur - first_untyped_slot;
|
|
if (i < CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS) {
|
|
ndks_boot.bi_frame->untypedList[i] = (seL4_UntypedDesc) {
|
|
pptr_to_paddr((void *)pptr), 0, 0, size_bits, device_memory
|
|
};
|
|
ut_cap = cap_untyped_cap_new(MAX_FREE_INDEX(size_bits),
|
|
device_memory, size_bits, pptr);
|
|
ret = provide_cap(root_cnode_cap, ut_cap);
|
|
} else {
|
|
printf("Kernel init: Too many untyped regions for boot info\n");
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
BOOT_CODE bool_t create_untypeds_for_region(
|
|
cap_t root_cnode_cap,
|
|
bool_t device_memory,
|
|
region_t reg,
|
|
seL4_SlotPos first_untyped_slot
|
|
)
|
|
{
|
|
word_t align_bits;
|
|
word_t size_bits;
|
|
|
|
while (!is_reg_empty(reg)) {
|
|
/* Determine the maximum size of the region */
|
|
size_bits = seL4_WordBits - 1 - clzl(reg.end - reg.start);
|
|
|
|
/* Determine the alignment of the region */
|
|
if (reg.start != 0) {
|
|
align_bits = ctzl(reg.start);
|
|
} else {
|
|
align_bits = size_bits;
|
|
}
|
|
/* Reduce size bits to align if needed */
|
|
if (align_bits < size_bits) {
|
|
size_bits = align_bits;
|
|
}
|
|
if (size_bits > seL4_MaxUntypedBits) {
|
|
size_bits = seL4_MaxUntypedBits;
|
|
}
|
|
|
|
if (size_bits >= seL4_MinUntypedBits) {
|
|
if (!provide_untyped_cap(root_cnode_cap, device_memory, reg.start, size_bits, first_untyped_slot)) {
|
|
return false;
|
|
}
|
|
}
|
|
reg.start += BIT(size_bits);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
BOOT_CODE bool_t create_kernel_untypeds(cap_t root_cnode_cap, region_t boot_mem_reuse_reg,
|
|
seL4_SlotPos first_untyped_slot)
|
|
{
|
|
word_t i;
|
|
region_t reg;
|
|
|
|
/* if boot_mem_reuse_reg is not empty, we can create UT objs from boot code/data frames */
|
|
if (!create_untypeds_for_region(root_cnode_cap, false, boot_mem_reuse_reg, first_untyped_slot)) {
|
|
return false;
|
|
}
|
|
|
|
/* convert remaining freemem into UT objects and provide the caps */
|
|
for (i = 0; i < MAX_NUM_FREEMEM_REG; i++) {
|
|
reg = ndks_boot.freemem[i];
|
|
ndks_boot.freemem[i] = REG_EMPTY;
|
|
if (!create_untypeds_for_region(root_cnode_cap, false, reg, first_untyped_slot)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BOOT_CODE void bi_finalise(void)
|
|
{
|
|
seL4_SlotPos slot_pos_start = ndks_boot.slot_pos_cur;
|
|
seL4_SlotPos slot_pos_end = ndks_boot.slot_pos_max;
|
|
ndks_boot.bi_frame->empty = (seL4_SlotRegion) {
|
|
slot_pos_start, slot_pos_end
|
|
};
|
|
}
|
|
|
|
static inline pptr_t ceiling_kernel_window(pptr_t p)
|
|
{
|
|
/* Adjust address if it exceeds the kernel window
|
|
* Note that we compare physical address in case of overflow.
|
|
*/
|
|
if (pptr_to_paddr((void *)p) > PADDR_TOP) {
|
|
p = PPTR_TOP;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/* we can't delcare arrays on the stack, so this is space for
|
|
* the below function to use. */
|
|
static BOOT_DATA region_t avail_reg[MAX_NUM_FREEMEM_REG];
|
|
/**
|
|
* Dynamically initialise the available memory on the platform.
|
|
* A region represents an area of memory.
|
|
*/
|
|
BOOT_CODE void init_freemem(word_t n_available, const p_region_t *available,
|
|
word_t n_reserved, region_t *reserved,
|
|
v_region_t it_v_reg, word_t extra_bi_size_bits)
|
|
{
|
|
/* Force ordering and exclusivity of reserved regions */
|
|
for (word_t i = 0; n_reserved > 0 && i < n_reserved - 1; i++) {
|
|
assert(reserved[i].start <= reserved[i].end);
|
|
assert(reserved[i].end <= reserved[i + 1].start);
|
|
}
|
|
|
|
/* Force ordering and exclusivity of available regions */
|
|
assert(n_available > 0);
|
|
for (word_t i = 0; i < n_available - 1; i++) {
|
|
assert(available[i].start < available[i].end);
|
|
assert(available[i].end <= available[i + 1].start);
|
|
}
|
|
|
|
for (word_t i = 0; i < MAX_NUM_FREEMEM_REG; i++) {
|
|
ndks_boot.freemem[i] = REG_EMPTY;
|
|
}
|
|
|
|
/* convert the available regions to pptrs */
|
|
for (word_t i = 0; i < n_available; i++) {
|
|
avail_reg[i] = paddr_to_pptr_reg(available[i]);
|
|
avail_reg[i].end = ceiling_kernel_window(avail_reg[i].end);
|
|
avail_reg[i].start = ceiling_kernel_window(avail_reg[i].start);
|
|
}
|
|
|
|
word_t a = 0;
|
|
word_t r = 0;
|
|
/* Now iterate through the available regions, removing any reserved regions. */
|
|
while (a < n_available && r < n_reserved) {
|
|
if (reserved[r].start == reserved[r].end) {
|
|
/* reserved region is empty - skip it */
|
|
r++;
|
|
} else if (avail_reg[a].start >= avail_reg[a].end) {
|
|
/* skip the entire region - it's empty now after trimming */
|
|
a++;
|
|
} else if (reserved[r].end <= avail_reg[a].start) {
|
|
/* the reserved region is below the available region - skip it*/
|
|
r++;
|
|
} else if (reserved[r].start >= avail_reg[a].end) {
|
|
/* the reserved region is above the available region - take the whole thing */
|
|
insert_region(avail_reg[a]);
|
|
a++;
|
|
} else {
|
|
/* the reserved region overlaps with the available region */
|
|
if (reserved[r].start <= avail_reg[a].start) {
|
|
/* the region overlaps with the start of the available region.
|
|
* trim start of the available region */
|
|
avail_reg[a].start = MIN(avail_reg[a].end, reserved[r].end);
|
|
r++;
|
|
} else {
|
|
assert(reserved[r].start < avail_reg[a].end);
|
|
/* take the first chunk of the available region and move
|
|
* the start to the end of the reserved region */
|
|
region_t m = avail_reg[a];
|
|
m.end = reserved[r].start;
|
|
insert_region(m);
|
|
if (avail_reg[a].end > reserved[r].end) {
|
|
avail_reg[a].start = reserved[r].end;
|
|
r++;
|
|
} else {
|
|
a++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* no more reserved regions - add the rest */
|
|
for (; a < n_available; a++) {
|
|
if (avail_reg[a].start < avail_reg[a].end) {
|
|
insert_region(avail_reg[a]);
|
|
}
|
|
}
|
|
|
|
/* now try to fit the root server objects into a region */
|
|
word_t i = MAX_NUM_FREEMEM_REG - 1;
|
|
if (!is_reg_empty(ndks_boot.freemem[i])) {
|
|
printf("Insufficient MAX_NUM_FREEMEM_REG");
|
|
halt();
|
|
}
|
|
/* skip any empty regions */
|
|
for (; is_reg_empty(ndks_boot.freemem[i]) && i >= 0; i--);
|
|
|
|
/* try to grab the last available p region to create the root server objects
|
|
* from. If possible, retain any left over memory as an extra p region */
|
|
word_t size = calculate_rootserver_size(it_v_reg, extra_bi_size_bits);
|
|
word_t max = rootserver_max_size_bits(extra_bi_size_bits);
|
|
for (; i >= 0; i--) {
|
|
word_t next = i + 1;
|
|
pptr_t start = ROUND_DOWN(ndks_boot.freemem[i].end - size, max);
|
|
if (start >= ndks_boot.freemem[i].start) {
|
|
create_rootserver_objects(start, it_v_reg, extra_bi_size_bits);
|
|
if (i < MAX_NUM_FREEMEM_REG) {
|
|
ndks_boot.freemem[next].end = ndks_boot.freemem[i].end;
|
|
ndks_boot.freemem[next].start = start + size;
|
|
}
|
|
ndks_boot.freemem[i].end = start;
|
|
break;
|
|
} else if (i < MAX_NUM_FREEMEM_REG) {
|
|
ndks_boot.freemem[next] = ndks_boot.freemem[i];
|
|
}
|
|
}
|
|
}
|