629 lines
19 KiB
C
629 lines
19 KiB
C
/*
|
|
* Copyright 2014, General Dynamics C4 Systems
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*/
|
|
|
|
#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 <linker.h>
|
|
#include <plat/machine/hardware.h>
|
|
#include <machine.h>
|
|
#include <arch/machine/timer.h>
|
|
#include <arch/machine/fpu.h>
|
|
#include <arch/machine/tlb.h>
|
|
|
|
#ifdef CONFIG_ARM_SMMU
|
|
#include <drivers/smmu/smmuv2.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
/* sync variable to prevent other nodes from booting
|
|
* until kernel data structures initialized */
|
|
BOOT_BSS static volatile int node_boot_lock;
|
|
#endif /* ENABLE_SMP_SUPPORT */
|
|
|
|
#define ARCH_RESERVED 3 // kernel + user image + dtb
|
|
#define MAX_RESERVED (ARCH_RESERVED + MODE_RESERVED)
|
|
BOOT_BSS static region_t reserved[MAX_RESERVED];
|
|
|
|
BOOT_CODE static bool_t arch_init_freemem(p_region_t ui_p_reg,
|
|
p_region_t dtb_p_reg,
|
|
v_region_t it_v_reg,
|
|
word_t extra_bi_size_bits)
|
|
{
|
|
reserved[0].start = KERNEL_ELF_BASE;
|
|
reserved[0].end = (pptr_t)ki_end;
|
|
|
|
int index = 1;
|
|
if (dtb_p_reg.start) {
|
|
/* the dtb region could be empty */
|
|
reserved[index].start = (pptr_t) paddr_to_pptr(dtb_p_reg.start);
|
|
reserved[index].end = (pptr_t) paddr_to_pptr(dtb_p_reg.end);
|
|
index++;
|
|
}
|
|
|
|
if (MODE_RESERVED > 1) {
|
|
printf("ERROR: MODE_RESERVED > 1 unsupported!\n");
|
|
return false;
|
|
}
|
|
|
|
if (ui_p_reg.start < PADDR_TOP) {
|
|
region_t ui_reg = paddr_to_pptr_reg(ui_p_reg);
|
|
if (MODE_RESERVED == 1) {
|
|
if (ui_reg.end > mode_reserved_region[0].start) {
|
|
reserved[index] = mode_reserved_region[0];
|
|
index++;
|
|
reserved[index].start = ui_reg.start;
|
|
reserved[index].end = ui_reg.end;
|
|
} else {
|
|
reserved[index].start = ui_reg.start;
|
|
reserved[index].end = ui_reg.end;
|
|
index++;
|
|
reserved[index] = mode_reserved_region[0];
|
|
}
|
|
index++;
|
|
} else {
|
|
reserved[index].start = ui_reg.start;
|
|
reserved[index].end = ui_reg.end;
|
|
index++;
|
|
}
|
|
} else if (MODE_RESERVED == 1) {
|
|
reserved[index] = mode_reserved_region[0];
|
|
index++;
|
|
}
|
|
|
|
return init_freemem(get_num_avail_p_regs(), get_avail_p_regs(), index,
|
|
reserved, it_v_reg, extra_bi_size_bits);
|
|
}
|
|
|
|
|
|
BOOT_CODE static void init_irqs(cap_t root_cnode_cap)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i <= maxIRQ ; i++) {
|
|
setIRQState(IRQInactive, CORE_IRQ_TO_IRQT(0, i));
|
|
}
|
|
setIRQState(IRQTimer, CORE_IRQ_TO_IRQT(0, KERNEL_TIMER_IRQ));
|
|
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
|
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, INTERRUPT_VGIC_MAINTENANCE));
|
|
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, INTERRUPT_VTIMER_EVENT));
|
|
#endif
|
|
#ifdef CONFIG_TK1_SMMU
|
|
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, INTERRUPT_SMMU));
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARM_ENABLE_PMU_OVERFLOW_INTERRUPT
|
|
#ifdef KERNEL_PMU_IRQ
|
|
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(0, KERNEL_PMU_IRQ));
|
|
#if (defined CONFIG_PLAT_TX1 && defined ENABLE_SMP_SUPPORT)
|
|
//SELFOUR-1252
|
|
#error "This platform doesn't support tracking CPU utilisation on multicore"
|
|
#endif /* CONFIG_PLAT_TX1 && ENABLE_SMP_SUPPORT */
|
|
#else
|
|
#error "This platform doesn't support tracking CPU utilisation feature"
|
|
#endif /* KERNEL_TIMER_IRQ */
|
|
#endif /* CONFIG_ARM_ENABLE_PMU_OVERFLOW_INTERRUPT */
|
|
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
setIRQState(IRQIPI, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), irq_remote_call_ipi));
|
|
setIRQState(IRQIPI, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), irq_reschedule_ipi));
|
|
#endif
|
|
|
|
/* provide the IRQ control cap */
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapIRQControl), cap_irq_control_cap_new());
|
|
}
|
|
|
|
#ifdef CONFIG_ARM_SMMU
|
|
BOOT_CODE static void init_smmu(cap_t root_cnode_cap)
|
|
{
|
|
plat_smmu_init();
|
|
/*provide the SID and CB control cap*/
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapSMMUSIDControl), cap_sid_control_cap_new());
|
|
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapSMMUCBControl), cap_cb_control_cap_new());
|
|
}
|
|
|
|
#endif
|
|
|
|
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;
|
|
|
|
slot_pos_before = ndks_boot.slot_pos_cur;
|
|
create_device_untypeds(root_cnode_cap, slot_pos_before);
|
|
create_kernel_untypeds(root_cnode_cap, boot_mem_reuse_reg, slot_pos_before);
|
|
|
|
slot_pos_after = ndks_boot.slot_pos_cur;
|
|
ndks_boot.bi_frame->untyped = (seL4_SlotRegion) {
|
|
slot_pos_before, slot_pos_after
|
|
};
|
|
return true;
|
|
|
|
}
|
|
|
|
/** This and only this function initialises the CPU.
|
|
*
|
|
* It does NOT initialise any kernel state.
|
|
* @return For the verification build, this currently returns true always.
|
|
*/
|
|
BOOT_CODE static bool_t init_cpu(void)
|
|
{
|
|
bool_t haveHWFPU;
|
|
|
|
#ifdef CONFIG_ARCH_AARCH64
|
|
if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
|
|
if (!checkTCR_EL2()) {
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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
|
|
|
|
/* Setup kernel stack pointer.
|
|
* On ARM SMP, the array index here is the CPU ID
|
|
*/
|
|
#ifndef CONFIG_ARCH_ARM_V6
|
|
word_t stack_top = ((word_t) kernel_stack_alloc[SMP_TERNARY(getCurrentCPUIndex(), 0)]) + BIT(CONFIG_KERNEL_STACK_BITS);
|
|
#if defined(ENABLE_SMP_SUPPORT) && defined(CONFIG_ARCH_AARCH64)
|
|
/* the least 12 bits are used to store logical core ID */
|
|
stack_top |= getCurrentCPUIndex();
|
|
#endif
|
|
setKernelStack(stack_top);
|
|
#endif /* CONFIG_ARCH_ARM_V6 */
|
|
|
|
#ifdef CONFIG_ARCH_AARCH64
|
|
/* initialise CPU's exception vector table */
|
|
setVtable((pptr_t)arm_vector_table);
|
|
#endif /* CONFIG_ARCH_AARCH64 */
|
|
|
|
haveHWFPU = fpsimd_HWCapTest();
|
|
|
|
/* Disable FPU to avoid channels where a platform has an FPU but doesn't make use of it */
|
|
if (haveHWFPU) {
|
|
disableFpu();
|
|
}
|
|
|
|
#ifdef CONFIG_HAVE_FPU
|
|
if (haveHWFPU) {
|
|
if (!fpsimd_init()) {
|
|
return false;
|
|
}
|
|
} else {
|
|
printf("Platform claims to have FP hardware, but does not!");
|
|
return false;
|
|
}
|
|
#endif /* CONFIG_HAVE_FPU */
|
|
|
|
cpu_initLocalIRQController();
|
|
|
|
#ifdef CONFIG_ENABLE_BENCHMARKS
|
|
arm_init_ccnt();
|
|
#endif /* CONFIG_ENABLE_BENCHMARKS */
|
|
|
|
/* Export selected CPU features for access by PL0 */
|
|
armv_init_user_access();
|
|
|
|
initTimer();
|
|
|
|
return true;
|
|
}
|
|
|
|
/* This and only this function initialises the platform. It does NOT initialise any kernel state. */
|
|
|
|
BOOT_CODE static void init_plat(void)
|
|
{
|
|
initIRQController();
|
|
initL2Cache();
|
|
#ifdef CONFIG_ARM_SMMU
|
|
plat_smmu_init();
|
|
#endif
|
|
}
|
|
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
BOOT_CODE static bool_t try_init_kernel_secondary_core(void)
|
|
{
|
|
unsigned i;
|
|
|
|
/* need to first wait until some kernel init has been done */
|
|
while (!node_boot_lock);
|
|
|
|
/* Perform cpu init */
|
|
init_cpu();
|
|
|
|
for (i = 0; i < NUM_PPI; i++) {
|
|
maskInterrupt(true, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), i));
|
|
}
|
|
setIRQState(IRQIPI, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), irq_remote_call_ipi));
|
|
setIRQState(IRQIPI, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), irq_reschedule_ipi));
|
|
/* Enable per-CPU timer interrupts */
|
|
setIRQState(IRQTimer, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), KERNEL_TIMER_IRQ));
|
|
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
|
|
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), INTERRUPT_VGIC_MAINTENANCE));
|
|
setIRQState(IRQReserved, CORE_IRQ_TO_IRQT(getCurrentCPUIndex(), INTERRUPT_VTIMER_EVENT));
|
|
#endif /* CONFIG_ARM_HYPERVISOR_SUPPORT */
|
|
NODE_LOCK_SYS;
|
|
|
|
ksNumCPUs++;
|
|
|
|
init_core_state(SchedulerAction_ResumeCurrentThread);
|
|
|
|
return true;
|
|
}
|
|
|
|
BOOT_CODE static void release_secondary_cpus(void)
|
|
{
|
|
|
|
/* release the cpus at the same time */
|
|
node_boot_lock = 1;
|
|
|
|
#ifndef CONFIG_ARCH_AARCH64
|
|
/* At this point in time the other CPUs do *not* have the seL4 global pd set.
|
|
* However, they still have a PD from the elfloader (which is mapping mmemory
|
|
* as strongly ordered uncached, as a result we need to explicitly clean
|
|
* the cache for it to see the update of node_boot_lock
|
|
*
|
|
* For ARMv8, the elfloader sets the page table entries as inner shareable
|
|
* (so is the attribute of the seL4 global PD) when SMP is enabled, and
|
|
* turns on the cache. Thus, we do not need to clean and invaliate the cache.
|
|
*/
|
|
cleanInvalidateL1Caches();
|
|
plat_cleanInvalidateL2Cache();
|
|
#endif
|
|
|
|
/* Wait until all the secondary cores are done initialising */
|
|
while (ksNumCPUs != CONFIG_MAX_NUM_NODES) {
|
|
/* perform a memory release+acquire to get new values of ksNumCPUs */
|
|
__atomic_signal_fence(__ATOMIC_ACQ_REL);
|
|
}
|
|
}
|
|
#endif /* ENABLE_SMP_SUPPORT */
|
|
|
|
/* Main kernel initialisation function. */
|
|
|
|
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,
|
|
paddr_t dtb_addr_start,
|
|
paddr_t dtb_addr_end
|
|
)
|
|
{
|
|
cap_t root_cnode_cap;
|
|
cap_t it_ap_cap;
|
|
cap_t it_pd_cap;
|
|
cap_t ipcbuf_cap;
|
|
p_region_t ui_p_reg = (p_region_t) {
|
|
ui_p_reg_start, ui_p_reg_end
|
|
};
|
|
region_t ui_reg = paddr_to_pptr_reg(ui_p_reg);
|
|
region_t dtb_reg;
|
|
word_t extra_bi_size;
|
|
pptr_t extra_bi_offset = 0;
|
|
vptr_t extra_bi_frame_vptr;
|
|
vptr_t bi_frame_vptr;
|
|
vptr_t ipcbuf_vptr;
|
|
create_frames_of_region_ret_t create_frames_ret;
|
|
create_frames_of_region_ret_t extra_bi_ret;
|
|
|
|
/* convert from physical addresses to userland vptrs */
|
|
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);
|
|
extra_bi_frame_vptr = bi_frame_vptr + BIT(PAGE_BITS);
|
|
|
|
/* If no DTB was provided, skip allocating extra bootinfo */
|
|
p_region_t dtb_p_reg = {
|
|
dtb_addr_start, ROUND_UP(dtb_addr_end, PAGE_BITS)
|
|
};
|
|
if (dtb_addr_start == 0) {
|
|
extra_bi_size = 0;
|
|
dtb_reg = (region_t) {
|
|
0, 0
|
|
};
|
|
} else {
|
|
dtb_reg = paddr_to_pptr_reg(dtb_p_reg);
|
|
extra_bi_size = sizeof(seL4_BootInfoHeader) + (dtb_reg.end - dtb_reg.start);
|
|
}
|
|
word_t extra_bi_size_bits = calculate_extra_bi_size_bits(extra_bi_size);
|
|
|
|
/* The region of the initial thread is the user image + ipcbuf and boot info */
|
|
it_v_reg.start = ui_v_reg.start;
|
|
it_v_reg.end = extra_bi_frame_vptr + BIT(extra_bi_size_bits);
|
|
|
|
if (it_v_reg.end >= USER_TOP) {
|
|
printf("ERROR: userland image virtual end address too high\n");
|
|
return false;
|
|
}
|
|
|
|
/* setup virtual memory for the kernel */
|
|
map_kernel_window();
|
|
|
|
/* initialise the CPU */
|
|
if (!init_cpu()) {
|
|
printf("ERROR: CPU init failed\n");
|
|
return false;
|
|
}
|
|
|
|
/* debug output via serial port is only available from here */
|
|
printf("Bootstrapping kernel\n");
|
|
|
|
/* initialise the platform */
|
|
init_plat();
|
|
|
|
if (!arch_init_freemem(ui_p_reg, dtb_p_reg, it_v_reg, extra_bi_size_bits)) {
|
|
printf("ERROR: free memory management initialization failed\n");
|
|
return false;
|
|
}
|
|
|
|
/* create the root cnode */
|
|
root_cnode_cap = create_root_cnode();
|
|
if (cap_get_capType(root_cnode_cap) == cap_null_cap) {
|
|
printf("ERROR: root c-node creation failed\n");
|
|
return false;
|
|
}
|
|
|
|
/* create the cap for managing thread domains */
|
|
create_domain_cap(root_cnode_cap);
|
|
|
|
/* initialise the IRQ states and provide the IRQ control cap */
|
|
init_irqs(root_cnode_cap);
|
|
|
|
#ifdef CONFIG_ARM_SMMU
|
|
/* initialise the SMMU and provide the SMMU control caps*/
|
|
init_smmu(root_cnode_cap);
|
|
#endif
|
|
populate_bi_frame(0, CONFIG_MAX_NUM_NODES, ipcbuf_vptr, extra_bi_size);
|
|
|
|
/* put DTB in the bootinfo block, if present. */
|
|
seL4_BootInfoHeader header;
|
|
if (dtb_reg.start) {
|
|
header.id = SEL4_BOOTINFO_HEADER_FDT;
|
|
header.len = sizeof(header) + dtb_reg.end - dtb_reg.start;
|
|
*(seL4_BootInfoHeader *)(rootserver.extra_bi + extra_bi_offset) = header;
|
|
extra_bi_offset += sizeof(header);
|
|
memcpy((void *)(rootserver.extra_bi + extra_bi_offset), (void *)dtb_reg.start,
|
|
dtb_reg.end - dtb_reg.start);
|
|
extra_bi_offset += (dtb_reg.end - dtb_reg.start);
|
|
}
|
|
|
|
if (extra_bi_size > extra_bi_offset) {
|
|
/* provde a chunk for any leftover padding in the extended boot info */
|
|
header.id = SEL4_BOOTINFO_HEADER_PADDING;
|
|
header.len = (extra_bi_size - extra_bi_offset);
|
|
*(seL4_BootInfoHeader *)(rootserver.extra_bi + extra_bi_offset) = header;
|
|
}
|
|
|
|
if (config_set(CONFIG_TK1_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) {
|
|
printf("ERROR: SMMU I/O space creation failed\n");
|
|
return false;
|
|
}
|
|
} else {
|
|
ndks_boot.bi_frame->ioSpaceCaps = S_REG_EMPTY;
|
|
}
|
|
|
|
/* Construct an initial address space with enough virtual addresses
|
|
* to cover the user image + ipc buffer and bootinfo frames */
|
|
it_pd_cap = create_it_address_space(root_cnode_cap, it_v_reg);
|
|
if (cap_get_capType(it_pd_cap) == cap_null_cap) {
|
|
printf("ERROR: address space creation for initial thread failed\n");
|
|
return false;
|
|
}
|
|
|
|
/* Create and map bootinfo frame cap */
|
|
create_bi_frame_cap(
|
|
root_cnode_cap,
|
|
it_pd_cap,
|
|
bi_frame_vptr
|
|
);
|
|
|
|
/* create and map extra bootinfo region */
|
|
if (extra_bi_size > 0) {
|
|
region_t extra_bi_region = {
|
|
.start = rootserver.extra_bi,
|
|
.end = rootserver.extra_bi + extra_bi_size
|
|
};
|
|
extra_bi_ret =
|
|
create_frames_of_region(
|
|
root_cnode_cap,
|
|
it_pd_cap,
|
|
extra_bi_region,
|
|
true,
|
|
pptr_to_paddr((void *)extra_bi_region.start) - extra_bi_frame_vptr
|
|
);
|
|
if (!extra_bi_ret.success) {
|
|
printf("ERROR: mapping extra boot info to initial thread failed\n");
|
|
return false;
|
|
}
|
|
ndks_boot.bi_frame->extraBIPages = extra_bi_ret.region;
|
|
}
|
|
|
|
#ifdef CONFIG_KERNEL_MCS
|
|
init_sched_control(root_cnode_cap, CONFIG_MAX_NUM_NODES);
|
|
#endif
|
|
|
|
/* create the initial thread's IPC buffer */
|
|
ipcbuf_cap = create_ipcbuf_frame_cap(root_cnode_cap, it_pd_cap, ipcbuf_vptr);
|
|
if (cap_get_capType(ipcbuf_cap) == cap_null_cap) {
|
|
printf("ERROR: could not create IPC buffer for initial thread\n");
|
|
return false;
|
|
}
|
|
|
|
/* create all userland image frames */
|
|
create_frames_ret =
|
|
create_frames_of_region(
|
|
root_cnode_cap,
|
|
it_pd_cap,
|
|
ui_reg,
|
|
true,
|
|
pv_offset
|
|
);
|
|
if (!create_frames_ret.success) {
|
|
printf("ERROR: could not create all userland image frames\n");
|
|
return false;
|
|
}
|
|
ndks_boot.bi_frame->userImageFrames = create_frames_ret.region;
|
|
|
|
/* create/initialise the initial thread's ASID pool */
|
|
it_ap_cap = create_it_asid_pool(root_cnode_cap);
|
|
if (cap_get_capType(it_ap_cap) == cap_null_cap) {
|
|
printf("ERROR: could not create ASID pool for initial thread\n");
|
|
return false;
|
|
}
|
|
write_it_asid_pool(it_ap_cap, it_pd_cap);
|
|
|
|
#ifdef CONFIG_KERNEL_MCS
|
|
NODE_STATE(ksCurTime) = getCurrentTime();
|
|
#endif
|
|
|
|
/* create the idle thread */
|
|
if (!create_idle_thread()) {
|
|
printf("ERROR: could not create idle thread\n");
|
|
return false;
|
|
}
|
|
|
|
/* Before creating the initial thread (which also switches to it)
|
|
* we clean the cache so that any page table information written
|
|
* as a result of calling create_frames_of_region will be correctly
|
|
* read by the hardware page table walker */
|
|
cleanInvalidateL1Caches();
|
|
|
|
/* create the initial thread */
|
|
tcb_t *initial = create_initial_thread(
|
|
root_cnode_cap,
|
|
it_pd_cap,
|
|
v_entry,
|
|
bi_frame_vptr,
|
|
ipcbuf_vptr,
|
|
ipcbuf_cap
|
|
);
|
|
|
|
if (initial == NULL) {
|
|
printf("ERROR: could not create initial thread\n");
|
|
return false;
|
|
}
|
|
|
|
init_core_state(initial);
|
|
|
|
/* create all of the untypeds. Both devices and kernel window memory */
|
|
if (!create_untypeds(
|
|
root_cnode_cap,
|
|
(region_t) {
|
|
KERNEL_ELF_BASE, (pptr_t)ki_boot_end
|
|
} /* reusable boot code/data */
|
|
)) {
|
|
printf("ERROR: could not create untypteds for kernel image boot memory\n");
|
|
return false;
|
|
}
|
|
|
|
/* no shared-frame caps (ARM has no multikernel support) */
|
|
ndks_boot.bi_frame->sharedFrames = S_REG_EMPTY;
|
|
|
|
/* finalise the bootinfo frame */
|
|
bi_finalise();
|
|
|
|
/* make everything written by the kernel visible to userland. Cleaning to PoC is not
|
|
* strictly neccessary, but performance is not critical here so clean and invalidate
|
|
* everything to PoC */
|
|
cleanInvalidateL1Caches();
|
|
invalidateLocalTLB();
|
|
if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
|
|
invalidateHypTLB();
|
|
}
|
|
|
|
|
|
ksNumCPUs = 1;
|
|
|
|
/* initialize BKL before booting up other cores */
|
|
SMP_COND_STATEMENT(clh_lock_init());
|
|
SMP_COND_STATEMENT(release_secondary_cpus());
|
|
|
|
/* grab BKL before leaving the kernel */
|
|
NODE_LOCK_SYS;
|
|
|
|
printf("Booting all finished, dropped to user space\n");
|
|
|
|
/* kernel successfully initialized */
|
|
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,
|
|
paddr_t dtb_addr_p,
|
|
uint32_t dtb_size
|
|
)
|
|
{
|
|
bool_t result;
|
|
paddr_t dtb_end_p = 0;
|
|
|
|
if (dtb_addr_p) {
|
|
dtb_end_p = dtb_addr_p + dtb_size;
|
|
}
|
|
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
/* we assume there exists a cpu with id 0 and will use it for bootstrapping */
|
|
if (getCurrentCPUIndex() == 0) {
|
|
result = try_init_kernel(ui_p_reg_start,
|
|
ui_p_reg_end,
|
|
pv_offset,
|
|
v_entry,
|
|
dtb_addr_p, dtb_end_p);
|
|
} else {
|
|
result = try_init_kernel_secondary_core();
|
|
}
|
|
|
|
#else
|
|
result = try_init_kernel(ui_p_reg_start,
|
|
ui_p_reg_end,
|
|
pv_offset,
|
|
v_entry,
|
|
dtb_addr_p, dtb_end_p);
|
|
|
|
#endif /* ENABLE_SMP_SUPPORT */
|
|
|
|
if (!result) {
|
|
fail("ERROR: kernel init failed");
|
|
}
|
|
|
|
#ifdef CONFIG_KERNEL_MCS
|
|
NODE_STATE(ksCurTime) = getCurrentTime();
|
|
NODE_STATE(ksConsumed) = 0;
|
|
#endif
|
|
schedule();
|
|
activateThread();
|
|
}
|
|
|