+++ /dev/null
-/*
- * Copyright (c) 2016, 2017, ETH Zurich.
- * Copyright (c) 2016, Hewlett Packard Enterprise Development LP.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
- */
-
-/* CPU driver VM initialisation.
-
- This is the entry point on booting the first core, and needs to deal with
- the state left by UEFI. The CPU is mostly configured, in particular
- translation is enabled, and all RAM is mapped 1-1. We'll also be in either
- EL3 or EL2. We need to map the EL1 kernel window (TTBR1), drop to EL1, and
- jump to the next routine, which has already been relocated for us.
- */
-
-#include <stdio.h>
-#include <stdbool.h>
-
-#include <init.h>
-#include <offsets.h>
-#include <sysreg.h>
-#include <dev/armv8_dev.h>
-
-#include <barrelfish_kpi/arm_core_data.h>
-
-
-void eret(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3);
-
-void boot_bsp_init(uint32_t magic, lpaddr_t pointer, lpaddr_t stack)
- __attribute__((noreturn));
-void boot_app_init(lpaddr_t context)
- __attribute__((noreturn));
-
-void *jump_target= &arch_init;
-
-static inline
-void configure_tcr(void) {
- armv8_TCR_EL1_t tcr_el1 = armv8_TCR_EL1_rd(NULL);
- // disable top byte ignored, EL1
- tcr_el1 = armv8_TCR_EL1_TBI1_insert(tcr_el1, 0);
- // disable top byte ignored, EL0
- tcr_el1 = armv8_TCR_EL1_TBI0_insert(tcr_el1, 0);
- // 48b IPA
- tcr_el1 = armv8_TCR_EL1_IPS_insert(tcr_el1, 5);
- // 4kB granule
- tcr_el1 = armv8_TCR_EL1_TG1_insert(tcr_el1, armv8_KB_4);
- // Walks inner shareable
- tcr_el1 = armv8_TCR_EL1_SH1_insert(tcr_el1, armv8_inner_shareable);
- // Walks outer WB WA
- tcr_el1 = armv8_TCR_EL1_ORGN1_insert(tcr_el1, armv8_WbRaWa_cache);
- // Walks inner WB WA
- tcr_el1 = armv8_TCR_EL1_IRGN1_insert(tcr_el1, armv8_WbRaWa_cache);
- // enable EL1 translation
- tcr_el1 = armv8_TCR_EL1_EPD1_insert(tcr_el1, 0);
- // 48b kernel VA
- tcr_el1 = armv8_TCR_EL1_T1SZ_insert(tcr_el1, 16);
- // 4kB granule
- tcr_el1 = armv8_TCR_EL1_TG0_insert(tcr_el1, armv8_KB_4);
- // Walks inner shareable
- tcr_el1 = armv8_TCR_EL1_SH0_insert(tcr_el1, armv8_inner_shareable);
- // Walks outer WB WA
- tcr_el1 = armv8_TCR_EL1_ORGN0_insert(tcr_el1, armv8_WbRaWa_cache);
- // Walks inner WB WA
- tcr_el1 = armv8_TCR_EL1_IRGN0_insert(tcr_el1, armv8_WbRaWa_cache);
- // enable EL0 translation
- tcr_el1 = armv8_TCR_EL1_EPD0_insert(tcr_el1, 0);
- // 48b user VA
- tcr_el1 = armv8_TCR_EL1_T0SZ_insert(tcr_el1, 16);
- armv8_TCR_EL1_wr(NULL, tcr_el1);
-}
-
-
-#define DAIF_FIQ_BIT (1 << 0)
-#define DAIF_IRQ_BIT (1 << 1)
-
-
-/*
- * XXX: the following functions should go somewhere else, leaving them
- * here for the moment... -RA
- */
-static void armv8_disable_interrupts(void)
-{
- //__asm volatile("msr DAIFSet, #(DAIF_FIQ_BIT | DAIF_IRQ_BIT)\n");
- __asm volatile("msr DAIFSet, #3\n");
-}
-
-static void armv8_enable_interrupts(void)
-{
- // __asm volatile("msr DAIFClr, #(DAIF_FIQ_BIT | DAIF_IRQ_BIT)\n");
- __asm volatile("msr DAIFClr, #3\n");
-}
-
-static void armv8_set_tcr(uint8_t el)
-{
- switch(el) {
- case 3:
- //sysreg_write_ttbr0_el2(addr);
- case 2:
- {
- armv8_TCR_EL2_t reg = 0;
- reg = armv8_TCR_EL2_PS_insert(reg, 5);
- reg = armv8_TCR_EL2_T0SZ_insert(reg, (64 - 48));
- armv8_TCR_EL2_wr(NULL, reg);
- break;
- }
- case 1:
- {
- armv8_TCR_EL1_t reg = 0;
- // TODO: figure out what to set reg = armv8_TCR_EL1_PS_insert(reg, 5);
- reg = armv8_TCR_EL1_T0SZ_insert(reg, (64 - 48));
- armv8_TCR_EL1_wr(NULL, reg);
- break;
- }
- default:
- assert("should not happen");
- return;
- }
-}
-
-static void armv8_set_ttbr0(uint8_t el, lpaddr_t addr)
-{
- switch(el) {
- case 3:
- //sysreg_write_ttbr0_el2(addr);
- case 2:
- armv8_TTBR0_EL2_baddr_wrf(NULL, addr);
- armv8_TTBR0_EL1_baddr_wrf(NULL, addr);
- break;
- case 1:
- armv8_TTBR0_EL1_baddr_wrf(NULL, addr);
- break;
- default:
- assert("should not happen");
- return;
- }
- __asm volatile("isb");
-}
-
-static void armv8_enable_mmu(uint8_t el)
-{
- switch(el) {
- case 3:
- armv8_SCTLR_EL3_M_wrf(NULL, 0x1);
- __asm volatile("tlbi alle3\n isb");
- break;
- case 2:
- armv8_SCTLR_EL2_M_wrf(NULL, 0x1);
- __asm volatile("tlbi alle2\n isb");
- break;
- case 1:
- armv8_SCTLR_EL1_M_wrf(NULL, 0x1);
- __asm volatile("tlbi vmalle1\n isb");
- break;
- default:
- assert("should not happen");
- return;
- }
- __asm volatile("dsb sy\n isb");
-}
-
-static void armv8_invalidate_tlb(uint8_t el)
-{
- switch(el) {
- case 3:
- armv8_SCTLR_EL3_M_wrf(NULL, 0x1);
- __asm volatile("tlbi alle3");
- break;
- case 2:
- armv8_SCTLR_EL2_M_wrf(NULL, 0x1);
- __asm volatile("tlbi alle2");
- break;
- case 1:
- armv8_SCTLR_EL1_M_wrf(NULL, 0x1);
- __asm volatile("tlbi vmalle1");
- break;
- default:
- assert("should not happen");
- return;
- }
- __asm volatile("dsb sy\n isb");
-}
-
-static void armv8_invalidate_icache(void)
-{
- __asm volatile(
- "ic iallu \n"
- "dsb sy \n"
- "isb \n"
- );
-}
-
-static void armv8_instruction_synchronization_barrier(void)
-{
- __asm volatile("isb");
-}
-
-static void configure_spsr(uint8_t el) {
- uint32_t spsr = 0;
- /* mask the exceptions */
- spsr = armv8_SPSR_EL2_D_insert(spsr, 1);
- spsr = armv8_SPSR_EL2_A_insert(spsr, 1);
- spsr = armv8_SPSR_EL2_I_insert(spsr, 1);
- spsr = armv8_SPSR_EL2_F_insert(spsr, 1);
-
- /* set el1 */
- spsr = armv8_SPSR_EL2_EL_insert(spsr, 1);
-
- /* use the SP_ELx stack */
- spsr = armv8_SPSR_EL2_SP_insert(spsr, 1);
-
- switch(el) {
- case 3:
- armv8_SPSR_EL3_wr(NULL, spsr);
- return;
- case 2:
- armv8_SPSR_EL2_wr(NULL, spsr);
- break;
- case 1:
- armv8_SPSR_EL1_wr(NULL, spsr);
- return;
- default:
- return;
- }
-}
-
-static void configure_ttbr1(uint8_t el)
-{
- lpaddr_t ttbr1_el1;
- switch(el) {
- case 3:
- ttbr1_el1= armv8_TTBR0_EL3_rawrd(NULL);
- break;
- case 2:
- ttbr1_el1= armv8_TTBR0_EL2_rawrd(NULL);
- break;
- case 1:
- ttbr1_el1= armv8_TTBR0_EL1_rawrd(NULL);
- break;
- default:
- return;
- }
- armv8_TTBR1_EL1_rawwr(NULL, ttbr1_el1);
-}
-
-static void configure_mair(void)
-{
- /* Set memory type 0, for kernel use. */
- // attr0 = Normal Memory, Inner Write-back non transient
- // attr1 = Device-nGnRnE memory
- armv8_MAIR_EL1_wr(NULL, 0x00ff);
-}
-
-static void configure_sctlr(void)
-/* Enable EL0/1 translation. */
-{
-
- armv8_SCTLR_EL1_t val = 0;
-
- /* Traps EL0 execution of cache maintenance instructions to EL1 */
- val = armv8_SCTLR_EL1_UCI_insert(val, 0x1);
-
- /* write permissions implies execute never */
- //val = armv8_SCTLR_EL1_WXN_insert(val, 0x1);
-
- /* don't trap WFI/WFE instructions to EL1 */
- val = armv8_SCTLR_EL1_nTWE_insert(val, 0x1);
- val = armv8_SCTLR_EL1_nTWI_insert(val, 0x1);
-
- /* disable Traps EL0 accesses to the CTR_EL0 to EL1*/
- val = armv8_SCTLR_EL1_UCT_insert(val, 0x1);
-
- /* Allow EL0 to do DC ZVA */
- val = armv8_SCTLR_EL1_DZE_insert(val, 0x1);
-
- /* enable instruction cache */
- val = armv8_SCTLR_EL1_I_insert(val, 0x1);
-
- /*
- * EL0 execution of MRS , MSR(register) , or MSR(immediate) instructions
- * that access the DAIF is not trapped to EL1.
- */
- //val = armv8_SCTLR_EL1_UMA_insert(val, 0x1);
-
- /*
- * Enables accesses to the DMB, DSB, and ISB System
- * instructions in the (coproc== 1111 ) encoding space from EL0
- */
- val = armv8_SCTLR_EL1_CP15BEN_insert(val, 0x1);
-
- /* Enable SP alignment checks */
- val = armv8_SCTLR_EL1_SA0_insert(val, 0x1);
- val = armv8_SCTLR_EL1_SA_insert(val, 0x1);
-
- /* enable data cachable */
- val = armv8_SCTLR_EL1_C_insert(val, 0x1);
-
- /* enable alignment checks */
- val = armv8_SCTLR_EL1_A_insert(val, 0x1);
-
- /* enable mmu */
- val = armv8_SCTLR_EL1_M_insert(val, 0x1);
-
- armv8_SCTLR_EL1_wr(NULL, val);
-}
-
-static void configure_el3_traps(void)
-{
-
- /* If we've started in EL3, that most likely means we're in the
- * simulator. We don't use it at all, so just disable all traps to
- * EL3, and drop to non-secure EL2 (if it exists). */
-
- armv8_SCR_EL3_t val = 0;
-
- /* Don't trap secure timer access. */
- val = armv8_SCR_EL3_ST_insert(val, 0x1);
-
- /* Next EL is AArch64. */
- val = armv8_SCR_EL3_RW_insert(val, 0x1);
-
- /* HVC is enabled. */
- val = armv8_SCR_EL3_HCE_insert(val, 0x1);
-
- /* SMC is disabled. */
- val = armv8_SCR_EL3_SMD_insert(val, 0x1);
-
- /* External aborts don't trap to EL3. */
- val = armv8_SCR_EL3_EA_insert(val, 0x1);
-
- /* FIQs don't trap to EL3. */
- val = armv8_SCR_EL3_FIQ_insert(val, 0x1);
-
- /* IRQs don't trap to EL3. */
- val = armv8_SCR_EL3_IRQ_insert(val, 0x1);
-
- /* EL0 and EL1 are non-secure. */
- val = armv8_SCR_EL3_NS_insert(val, 0x1);
-
- armv8_SCR_EL3_wr(NULL, val);
-
- /* We don't need to set SCTLR_EL3, as we're not using it. */
-
- armv8_MDCR_EL3_t mdcr = 0;
- /* Allow event counting in secure state. */
- armv8_MDCR_EL3_SPME_insert(mdcr, 0x1);
- armv8_MDCR_EL3_wr(NULL, mdcr);
-}
-
-static void configure_el2_traps(void)
-{
- /* check if EL2 is implemented */
- if (armv8_ID_AA64PFR0_EL1_EL2_rdf(NULL) == armv8_ID_EL_NOT_IMPLEMENTED) {
- return;
- }
-
- /* configure EL2 traps & mmu */
-
- armv8_HCR_EL2_t val = 0;
-
- /* For the Non-secure EL1&0 translation regime, for permitted accesses to a
- * memory location that use a common definition of the Shareability and
- * Cacheability of the location, there might be a loss of coherency if the
- * Inner Cacheability attribute for those accesses differs from the Outer
- * Cacheability attribute.*/
- val = armv8_HCR_EL2_MIOCNC_insert(val, 1);
-
- /* Set the mode to be AARCH64 */
- val = armv8_HCR_EL2_RW_insert(val, 1);
-
- /* HVC instructions are UNDEFINED at EL2 and Non-secure EL1. Any resulting
- * exception is taken to the Exception level at which the HVC instruction
- * is executed.
- *
- * XXX: this will disable Hypervisor calls entirely, revisit for ARRAKIS
- */
- val = armv8_HCR_EL2_HCD_insert(val, 1);
-
- armv8_HCR_EL2_wr(NULL, val);
-
-
- /* disable traps to EL2 for timer accesses */
- uint32_t cnthctl = sysreg_read_cnthctl_el2();
- sysreg_write_cnthctl_el2(cnthctl | 0x3);
-
-}
-
-static void configure_el1_traps(void)
-{
- /* disable traps for FP/SIMD access */
- armv8_CPACR_EL1_FPEN_wrf(NULL, armv8_fpen_trap_none);
-}
-
-static void drop_to_el2(uint32_t magic, lpaddr_t pointer, lpaddr_t stack)
-{
- /* write the stack pointer for EL1 */
- armv8_SP_EL1_wr(NULL, stack + KERNEL_OFFSET);
-
- /* Set the jump target */
- armv8_ELR_EL3_wr(NULL, (uint64_t)jump_target);
-
- /* call exception return */
- eret(magic, pointer + KERNEL_OFFSET, stack + KERNEL_OFFSET, 0);
-}
-
-static void drop_to_el1(uint32_t magic, lpaddr_t pointer, lpaddr_t stack)
-{
- /* write the stack pointer for EL1 */
- armv8_SP_EL1_wr(NULL, stack + KERNEL_OFFSET);
-
- /* Set the jump target */
- armv8_ELR_EL2_wr(NULL, (uint64_t)jump_target);
-
- /* call exception return */
- eret(magic, pointer + KERNEL_OFFSET, stack + KERNEL_OFFSET, 0);
-}
-
-static void jump_to_cpudriver(uint32_t magic, lpaddr_t pointer, lpaddr_t stack)
-{
- // We are in EL1, so call arch_init directly.
- void (*relocated_arch_init)(uint32_t magic, lpaddr_t pointer, lpaddr_t stack);
- if ((lvaddr_t)arch_init < KERNEL_OFFSET) {
- relocated_arch_init = (void *)((lvaddr_t)arch_init + KERNEL_OFFSET);
- } else {
- relocated_arch_init = (void *)((lvaddr_t)arch_init);
- }
-
- // we may need to re set the stack pointer
- uint64_t sp = sysreg_read_sp();
- if (sp < KERNEL_OFFSET) {
- sysreg_write_sp(sp + KERNEL_OFFSET);
- }
- relocated_arch_init(magic, pointer + KERNEL_OFFSET, stack + KERNEL_OFFSET);
-}
-
-/**
- * @brief initializes an application core
- *
- * @param state pointer to the armv8_core_data structure
- *
- * This function is intended to bring the core to the same state as if it
- * has been booted by the UEFI boot loader.
- */
-void boot_app_init(lpaddr_t state)
-{
-
- struct armv8_core_data *cd = (struct armv8_core_data *)state;
-
- uint8_t current_el = armv8_CurrentEL_EL_rdf(NULL);
-
- if (current_el == 2) {
- uint64_t zero = 0;
- __asm volatile("MSR CPTR_EL2, %[zero]" : : [zero] "r" (zero));
- }
-
- /* disable interrupts */
- armv8_disable_interrupts();
-
- /* set the ttbr0/1 */
- armv8_set_ttbr0(current_el, cd->page_table_root);
-
- /* set the TCR */
- armv8_set_tcr(current_el);
-
- /* enable MMU */
- armv8_enable_mmu(current_el);
-
- /* invalidate TLB */
- armv8_invalidate_tlb(current_el);
-
- /* invalidate icache */
- armv8_invalidate_icache();
- armv8_instruction_synchronization_barrier();
-
- /* enable interrupts */
- armv8_enable_interrupts();
-
- boot_bsp_init(cd->boot_magic, state, cd->cpu_driver_stack);
-}
-
-
-/* On entry:
-
- Execution is starting in LOW addresses
- Pointers to stack and multiboot are LOW addresses
- Single core running (not guaranteed to be core 0)
- CPU is in highest implemented exception level
- MMU enabled, 4k translation granule, 1:1 mapping of all RAM
- Little-endian mode
- Core caches (L1&L2) and TLB enabled
- Non-architectural caches disabled (e.g. L3)
- Interrupts enabled
- Generic timer initialized and enabled
- >= 128KiB stack
- ACPI tables available
- Register x0 contains the multiboot magic value
- Register x1 contains a pointer to multiboot image
- Register x1 contains a pointer to top entry in the kernel stack
- */
-void
-boot_bsp_init(uint32_t magic, lpaddr_t pointer, lpaddr_t stack) {
-
- uint8_t el = armv8_CurrentEL_EL_rdf(NULL);
-
- /* Configure the EL1 translation regime. */
- configure_tcr();
-
- /* Copy the current TTBR for EL1. */
- configure_ttbr1(el);
-
- /* configure memory attributes */
- configure_mair();
-
- /* Enable EL0/1 translation. */
- configure_sctlr();
-
- /* configure spsr */
- configure_spsr(el);
-
- /* configure EL 1 traps*/
- configure_el1_traps();
-
- switch(el) {
- case 3:
- configure_el3_traps();
- configure_el2_traps();
- drop_to_el2(magic, pointer, stack);
- break;
- case 2:
- configure_el2_traps();
- drop_to_el1(magic, pointer, stack);
- break;
- case 1:
- jump_to_cpudriver(magic, pointer, stack);
- break;
- default:
- break;
- }
-
- while(1) {
- __asm volatile("wfi \n");
- }
-}