#include <config.h>
#include <assert.h>
#include <kernel/boot.h>
#include <machine/io.h>
#include <model/statedata.h>
#include <object/interrupt.h>
#include <arch/machine.h>
#include <arch/kernel/boot.h>
#include <arch/kernel/vspace.h>
#include <arch/benchmark.h>
#include <arch/user_access.h>
#include <arch/object/iospace.h>
#include <arch/linker.h>
#include <plat/machine/hardware.h>
#include <machine.h>
#include <machine/timer.h>
#include <arch/machine/fpu.h>
extern char ki_boot_end[1];
extern char ki_end[1];
#if CONFIG_MAX_NUM_NODES > 1
BOOT_DATA static volatile int node_boot_lock = 0;
#endif
BOOT_CODE static region_t
insert_region_excluded(region_t mem_reg, region_t reserved_reg)
{
region_t residual_reg = mem_reg;
bool_t result UNUSED;
if (reserved_reg.start < mem_reg.start) {
mem_reg.end = 0;
mem_reg.start = 0;
if (reserved_reg.end > residual_reg.start) {
residual_reg.start = reserved_reg.end;
}
} else if (mem_reg.end > reserved_reg.start) {
mem_reg.end = reserved_reg.start;
residual_reg.start = reserved_reg.end;
} else {
residual_reg.start = 0;
residual_reg.end = 0;
}
if (mem_reg.start < mem_reg.end) {
result = insert_region(mem_reg);
assert(result);
}
if (residual_reg.start > residual_reg.end) {
residual_reg.start = residual_reg.end;
}
return residual_reg;
}
BOOT_CODE static region_t
get_reserved_region(int i, pptr_t res_reg_end)
{
region_t res_reg = mode_reserved_region[i];
assert(res_reg.start < res_reg.end);
assert(res_reg_end <= res_reg.start);
return res_reg;
}
BOOT_CODE static int
get_num_reserved_region(void)
{
return sizeof(mode_reserved_region) / sizeof(region_t);
}
BOOT_CODE static void
init_freemem(region_t ui_reg)
{
word_t i;
bool_t result UNUSED;
region_t cur_reg;
region_t res_reg[] = {
{
.start = kernelBase,
.end = (pptr_t)ki_end
},
{
.start = ui_reg.start,
.end = ui_reg.end
},
};
for (i = 0; i < MAX_NUM_FREEMEM_REG; i++) {
ndks_boot.freemem[i] = REG_EMPTY;
}
assert(res_reg[0].start < res_reg[0].end);
assert(res_reg[1].start < res_reg[1].end);
assert(res_reg[0].end <= res_reg[1].start);
for (i = 0; i < get_num_avail_p_regs(); i++) {
cur_reg = paddr_to_pptr_reg(get_avail_p_reg(i));
if (pptr_to_paddr((void*)cur_reg.end) > PADDR_TOP) {
cur_reg.end = PPTR_TOP;
}
if (pptr_to_paddr((void*)cur_reg.start) > PADDR_TOP) {
cur_reg.start = PPTR_TOP;
}
cur_reg = insert_region_excluded(cur_reg, res_reg[0]);
cur_reg = insert_region_excluded(cur_reg, res_reg[1]);
region_t mode_res_reg = res_reg[1];
for (int m = 0; m < get_num_reserved_region(); m++) {
mode_res_reg = get_reserved_region(i, mode_res_reg.end);
cur_reg = insert_region_excluded(cur_reg, mode_res_reg);
}
if (cur_reg.start != cur_reg.end) {
result = insert_region(cur_reg);
assert(result);
}
}
}
BOOT_CODE static void
init_irqs(cap_t root_cnode_cap)
{
irq_t i;
for (i = 0; i <= maxIRQ; i++) {
setIRQState(IRQInactive, i);
}
setIRQState(IRQTimer, KERNEL_TIMER_IRQ);
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
setIRQState(IRQReserved, INTERRUPT_VGIC_MAINTENANCE);
#endif
#ifdef CONFIG_ARM_SMMU
setIRQState(IRQReserved, INTERRUPT_SMMU);
#endif
#ifdef CONFIG_ARM_ENABLE_PMU_OVERFLOW_INTERRUPT
#ifdef KERNEL_PMU_IRQ
setIRQState(IRQReserved, KERNEL_PMU_IRQ);
#else
#error "This platform doesn't support tracking CPU utilisation feature"
#endif
#endif
#if CONFIG_MAX_NUM_NODES > 1
setIRQState(IRQIPI, irq_remote_call_ipi);
setIRQState(IRQIPI, irq_remote_reschedule_ipi);
#endif
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapIRQControl), cap_irq_control_cap_new());
}
BOOT_CODE static bool_t
create_untypeds(cap_t root_cnode_cap, region_t boot_mem_reuse_reg)
{
seL4_SlotPos slot_pos_before;
seL4_SlotPos slot_pos_after;
region_t dev_reg;
word_t i;
slot_pos_before = ndks_boot.slot_pos_cur;
create_kernel_untypeds(root_cnode_cap, boot_mem_reuse_reg, slot_pos_before);
for (i = 0; i < get_num_dev_p_regs(); i++) {
dev_reg = paddr_to_pptr_reg(get_dev_p_reg(i));
if (!create_untypeds_for_region(root_cnode_cap, true,
dev_reg, slot_pos_before)) {
return false;
}
}
slot_pos_after = ndks_boot.slot_pos_cur;
ndks_boot.bi_frame->untyped = (seL4_SlotRegion) {
slot_pos_before, slot_pos_after
};
return true;
}
BOOT_CODE static bool_t
init_cpu(void)
{
activate_global_pd();
if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
vcpu_boot_init();
}
#ifdef CONFIG_HARDWARE_DEBUG_API
if (!Arch_initHardwareBreakpoints()) {
printf("Kernel built with CONFIG_HARDWARE_DEBUG_API, but this board doesn't "
"reliably support it.\n");
return false;
}
#endif
#ifndef CONFIG_ARCH_ARM_V6
word_t stack_top = ((word_t) kernel_stack_alloc[SMP_TERNARY(getCurrentCPUIndex(), 0)]) + BIT(CONFIG_KERNEL_STACK_BITS);
setKernelStack(stack_top);
#endif
#ifdef CONFIG_ARCH_AARCH64
setVtable((pptr_t)arm_vector_table);
#endif
#ifdef CONFIG_HAVE_FPU
if (fpsimd_HWCapTest()) {
if (!fpsimd_init()) {
return false;
}
} else {
printf("Platform claims to have FP hardware, but does not!");
return false;
}
#endif
cpu_initLocalIRQController();
#ifdef CONFIG_ENABLE_BENCHMARKS
armv_init_ccnt();
#endif
armv_init_user_access();
initTimer();
return true;
}
BOOT_CODE static void
init_plat(void)
{
initIRQController();
initL2Cache();
}
#if CONFIG_MAX_NUM_NODES > 1
BOOT_CODE static bool_t
try_init_kernel_secondary_core(void)
{
while (!node_boot_lock);
init_cpu();
maskInterrupt(false, KERNEL_TIMER_IRQ);
NODE_LOCK_SYS;
ksNumCPUs++;
init_core_state(SchedulerAction_ResumeCurrentThread);
return true;
}
BOOT_CODE static void
release_secondary_cpus(void)
{
node_boot_lock = 1;
cleanInvalidateL1Caches();
plat_cleanInvalidateCache();
while (ksNumCPUs != CONFIG_MAX_NUM_NODES);
}
#endif
static BOOT_CODE bool_t
try_init_kernel(
paddr_t ui_p_reg_start,
paddr_t ui_p_reg_end,
sword_t pv_offset,
vptr_t v_entry
)
{
cap_t root_cnode_cap;
cap_t it_ap_cap;
cap_t it_pd_cap;
cap_t ipcbuf_cap;
region_t ui_reg = paddr_to_pptr_reg((p_region_t) {
ui_p_reg_start, ui_p_reg_end
});
pptr_t bi_frame_pptr;
vptr_t bi_frame_vptr;
vptr_t ipcbuf_vptr;
create_frames_of_region_ret_t create_frames_ret;
v_region_t ui_v_reg;
v_region_t it_v_reg;
ui_v_reg.start = ui_p_reg_start - pv_offset;
ui_v_reg.end = ui_p_reg_end - pv_offset;
ipcbuf_vptr = ui_v_reg.end;
bi_frame_vptr = ipcbuf_vptr + BIT(PAGE_BITS);
it_v_reg.start = ui_v_reg.start;
it_v_reg.end = bi_frame_vptr + BIT(PAGE_BITS);
if (it_v_reg.end > kernelBase) {
printf("Userland image virtual end address too high\n");
return false;
}
map_kernel_window();
if (!init_cpu()) {
return false;
}
printf("Bootstrapping kernel\n");
init_plat();
init_freemem(ui_reg);
root_cnode_cap = create_root_cnode();
if (cap_get_capType(root_cnode_cap) == cap_null_cap) {
return false;
}
create_domain_cap(root_cnode_cap);
if (!create_irq_cnode()) {
return false;
}
init_irqs(root_cnode_cap);
bi_frame_pptr = allocate_bi_frame(0, CONFIG_MAX_NUM_NODES, ipcbuf_vptr);
if (!bi_frame_pptr) {
return false;
}
if (config_set(CONFIG_ARM_SMMU)) {
ndks_boot.bi_frame->ioSpaceCaps = create_iospace_caps(root_cnode_cap);
if (ndks_boot.bi_frame->ioSpaceCaps.start == 0 &&
ndks_boot.bi_frame->ioSpaceCaps.end == 0) {
return false;
}
} else {
ndks_boot.bi_frame->ioSpaceCaps = S_REG_EMPTY;
}
it_pd_cap = create_it_address_space(root_cnode_cap, it_v_reg);
if (cap_get_capType(it_pd_cap) == cap_null_cap) {
return false;
}
create_bi_frame_cap(
root_cnode_cap,
it_pd_cap,
bi_frame_pptr,
bi_frame_vptr
);
ipcbuf_cap = create_ipcbuf_frame(root_cnode_cap, it_pd_cap, ipcbuf_vptr);
if (cap_get_capType(ipcbuf_cap) == cap_null_cap) {
return false;
}
create_frames_ret =
create_frames_of_region(
root_cnode_cap,
it_pd_cap,
ui_reg,
true,
pv_offset
);
if (!create_frames_ret.success) {
return false;
}
ndks_boot.bi_frame->userImageFrames = create_frames_ret.region;
it_ap_cap = create_it_asid_pool(root_cnode_cap);
if (cap_get_capType(it_ap_cap) == cap_null_cap) {
return false;
}
write_it_asid_pool(it_ap_cap, it_pd_cap);
if (!create_idle_thread()) {
return false;
}
cleanInvalidateL1Caches();
tcb_t *initial = create_initial_thread(
root_cnode_cap,
it_pd_cap,
v_entry,
bi_frame_vptr,
ipcbuf_vptr,
ipcbuf_cap
);
if (initial == NULL) {
return false;
}
init_core_state(initial);
if (!create_untypeds(
root_cnode_cap,
(region_t) {
kernelBase, (pptr_t)ki_boot_end
}
)) {
return false;
}
ndks_boot.bi_frame->sharedFrames = S_REG_EMPTY;
bi_finalise();
cleanInvalidateL1Caches();
invalidateTLB();
if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
invalidateHypTLB();
}
ksNumCPUs = 1;
SMP_COND_STATEMENT(clh_lock_init());
SMP_COND_STATEMENT(release_secondary_cpus());
NODE_LOCK_SYS;
printf("Booting all finished, dropped to user space\n");
return true;
}
BOOT_CODE VISIBLE void
init_kernel(
paddr_t ui_p_reg_start,
paddr_t ui_p_reg_end,
sword_t pv_offset,
vptr_t v_entry
)
{
bool_t result;
#if CONFIG_MAX_NUM_NODES > 1
if (getCurrentCPUIndex() == 0) {
result = try_init_kernel(ui_p_reg_start,
ui_p_reg_end,
pv_offset,
v_entry);
} else {
result = try_init_kernel_secondary_core();
}
printf("Core %lu is processing with the root task \n", getCurrentCPUIndex());
#else
result = try_init_kernel(ui_p_reg_start,
ui_p_reg_end,
pv_offset,
v_entry);
#endif
if (!result) {
fail ("Kernel init failed for some reason :(");
}
schedule();
activateThread();
}