//! The entry point. QEMU loads our ELF file (see link.ld) and starts CPU 0 //! at `_start`, with the MMU off, at EL1 (or EL2 with `-M virt,virtualization=on`, //! the way real boards and firmware usually hand over). //! //! What `_start` does, in assembly because there's no stack yet: //! //! 1. Any CPU but CPU 0 parks (QEMU's virt keeps the others off anyway; //! the Pi 3's firmware spins them, so this matters there). //! 2. At EL2: make EL1 a 64-bit level (`HCR_EL2.RW`), let it use the //! timer (`CNTHCTL_EL2`), give it a safe `SCTLR_EL1`, then `eret` down //! to EL1 with interrupts masked. //! 3. Set the stack pointer (`__stack_top` from link.ld), clear `.bss` //! (Rust expects zero-initialised statics), allow FP/SIMD //! (`CPACR_EL1.FPEN`; our code doesn't use it, but compiler_builtins //! could), and call `kmain(dtb, boot_el)`. core::arch::global_asm!( r#" .section .text.boot, "ax" .global _start _start: mrs x1, mpidr_el1 and x1, x1, #0xff cbnz x1, 9f // not CPU 0: park mov x19, x0 // a DTB pointer, if the loader passed one mrs x20, CurrentEL lsr x20, x20, #2 // x20 = the EL we started at cmp x20, #2 b.ne 1f // --- EL2 -> EL1 --- mov x1, #(1 << 31) // HCR_EL2.RW: EL1 is AArch64 msr hcr_el2, x1 mrs x1, cnthctl_el2 orr x1, x1, #3 // EL1PCTEN | EL1PCEN: EL1 may use the timers msr cnthctl_el2, x1 msr cntvoff_el2, xzr // virtual time = physical time movz x1, #0x0800 movk x1, #0x30d0, lsl #16 // SCTLR_EL1: RES1 bits only (MMU, caches off) msr sctlr_el1, x1 mov x1, #0x3c5 // SPSR: EL1h, D A I F masked msr spsr_el2, x1 adr x1, 1f msr elr_el2, x1 eret 1: msr spsel, #1 // use SP_EL1 adrp x1, __stack_top add x1, x1, :lo12:__stack_top mov sp, x1 adrp x1, __bss_start add x1, x1, :lo12:__bss_start adrp x2, __bss_end add x2, x2, :lo12:__bss_end 2: cmp x1, x2 b.hs 3f str xzr, [x1], #8 b 2b 3: mov x1, #(3 << 20) // CPACR_EL1.FPEN = 0b11 msr cpacr_el1, x1 isb mov x0, x19 mov x1, x20 bl kmain 9: wfe b 9b "# );