/* * native_rpi5_entry.S (aarch64) - Raspberry Pi 5 native (non-UEFI) entry * stub (FABRIC-3.md §IV.3 item 1, 2026-09-04). * * Never run on the board yet. Rehearsed on QEMU's virt machine at EL2 * since 2026-10-14 (`make ... rpi5-rehearsal`, FABRIC-3.md §XXXIX): POST * passes there, same parity hash as the UEFI build. * * Entered directly by Pi 5 firmware at the classic bare-metal load address * 0x80000 (item 6, `config.txt`'s `os_check=0`, is still open -- that's * what actually gets a real image loaded there; this file is just the * code that would run once it is). No UEFI, no ACPI -- none of * `boot/uefi_loader.c`'s PE-loader shape applies on this path. Per * FABRIC-3.md §IV.1's own researched entry protocol: * * x0 = 32-bit DTB pointer, upper 32 bits of the 64-bit register * UNSPECIFIED -- must mask before use. * x1-x3 = reserved / zero, unused here. * * This stub's own job: mask and capture that DTB pointer into * `g_rpi5_dtb_ptr` (declared in `include/starkernel/rpi5_native_entry.h`), * establish a dedicated stack -- this path has no EDK2 boot stack to * inherit, there is no EDK2 at all here -- then tail-call * `rpi5_native_boot()` (`rpi5_native_boot.c`, §IV.3 item 2), which builds * `BootInfo` from the devicetree and calls the existing `kernel_main()`. * `rpi5_native_boot()` never returns; the `wfe`/`b` loop after the `bl` * exists only so this file's own control flow has somewhere defined to * go if that contract is ever violated. * * Build-system note: `kernel/Makefile`'s `KERNEL_ASM` wildcards every * `*.S` file in this directory into the kernel ELF build (not the PE/COFF * loader build -- that list is explicit, boot.S/isr.S/switch.S only), so * this file links into the ARCH=aarch64 QEMU/UEFI kernel ELF as dead * code, and is the entry point of the separately-linked Pi 5 native * image (`make boot_image TARGET=RASPI`): * `kernel/linker/starkernel-native-rpi5.ld` places `.text.entry` first * at 0x80000 and the image is flattened to `kernel_2712.img`. * * Pi 5 firmware loads only the file's bytes; `.bss` (NOLOAD) is whatever * was in RAM. It is zeroed here, before anything in it -- including * `g_rpi5_dtb_ptr` and this stub's own stack -- is used. x0 (the DTB * pointer) is preserved across the loop; only x4-x6 are touched. */ .section .bss .align 8 .global g_rpi5_dtb_ptr g_rpi5_dtb_ptr: .space 8 /* uint64_t -- masked DTB pointer */ /* The early identity map (2026-10-14): one level-1 table of 512 1 GiB * entries (39-bit VA, 4 KiB granule), and one level-2 table that splits * the GiB the image is in into 2 MiB blocks, so that a framebuffer * inside it can be made uncached alone (rpi5_mmu.c). */ .align 12 .global g_rpi5_l1 g_rpi5_l1: .space 4096 .global g_rpi5_l2 g_rpi5_l2: .space 4096 .align 4 g_rpi5_native_stack: .space 0x200000 /* 2 MiB, same convention as the * amd64/riscv64 kernel_entry.S * BSS stacks */ g_rpi5_native_stack_top: .section .text.entry, "ax" .global rpi5_native_start rpi5_native_start: /* Mask x0 to its documented 32-bit DTB-pointer range (§IV.1: the * upper 32 bits of the 64-bit register are unspecified by the * firmware's own entry protocol). */ mov x1, #0xffffffff and x0, x0, x1 /* Clean and invalidate the data cache over the whole image, .bss and * stack included, FIRST: whatever the firmware may have left in the * cache for these addresses goes to RAM now, before anything below * writes them with the cache off -- a dirty line cleaned later would * land on top of that work. Line size from CTR_EL0.DminLine. */ mrs x6, ctr_el0 ubfx x6, x6, #16, #4 mov x7, #4 lsl x7, x7, x6 /* x7 = line size in bytes */ sub x8, x7, #1 adrp x4, __kernel_start add x4, x4, :lo12:__kernel_start bic x4, x4, x8 adrp x5, __kernel_end add x5, x5, :lo12:__kernel_end .Lcache_clean: cmp x4, x5 b.hs .Lcache_done dc civac, x4 add x4, x4, x7 b .Lcache_clean .Lcache_done: dsb sy /* Zero .bss: [__bss_start, __bss_end), both 16-byte aligned by the * native linker script. */ adrp x4, __bss_start add x4, x4, :lo12:__bss_start adrp x5, __bss_end add x5, x5, :lo12:__bss_end .Lbss_zero: cmp x4, x5 b.hs .Lbss_done stp xzr, xzr, [x4], #16 b .Lbss_zero .Lbss_done: adrp x2, g_rpi5_dtb_ptr add x2, x2, :lo12:g_rpi5_dtb_ptr str x0, [x2] /* Establish this path's own dedicated stack. */ adrp x0, g_rpi5_native_stack_top add x0, x0, :lo12:g_rpi5_native_stack_top mov sp, x0 /* MMU and caches on, before any C (2026-10-14). The firmware enters * with the MMU off, where every access is Device memory: an unaligned * access or an exclusive faults, and nothing is cached. Identity map: * the first GiB Normal write-back (every Pi 5 has at least that much * RAM; rpi5_mmu.c makes the rest of the devicetree's RAM Normal once C * runs), everything else Device-nGnRE, never executable. At * whichever EL the firmware chose -- EL2 on the Pi 5, EL1 handled too. * "The first GiB" is precisely the GiB the image is in: 0 on the Pi 5 * (0x80000), 1 on QEMU's virt machine, where it is rehearsed. * MAIR: attr0 Normal WB RA/WA (0xFF), attr1 Device-nGnRE (0x04), * attr2 Normal non-cacheable (0x44). * block: AF (bit 10), SH inner (bits 9:8 = 3) for Normal, AttrIndx * (bits 4:2), bit 54 XN/UXN for Device. */ adrp x1, g_rpi5_l1 add x1, x1, :lo12:g_rpi5_l1 adrp x2, g_rpi5_l2 add x2, x2, :lo12:g_rpi5_l2 adrp x10, __kernel_start add x10, x10, :lo12:__kernel_start lsr x10, x10, #30 /* x10 = the GiB the image is in */ lsl x11, x10, #30 /* level 2: that GiB, 512 x 2 MiB Normal */ mov x3, #0 mov x4, #0x701 .Ll2_fill: lsl x5, x3, #21 add x5, x5, x11 orr x5, x5, x4 str x5, [x2, x3, lsl #3] add x3, x3, #1 cmp x3, #512 b.lo .Ll2_fill mov x3, #0 /* level 1: 1 GiB Device, but the image's GiB */ mov x4, #0x405 orr x4, x4, #(1 << 54) .Ll1_fill: lsl x5, x3, #30 orr x5, x5, x4 cmp x3, x10 b.ne .Ll1_store orr x5, x2, #3 /* -> the level 2 table */ .Ll1_store: str x5, [x1, x3, lsl #3] add x3, x3, #1 cmp x3, #512 b.lo .Ll1_fill dsb sy movz x6, #0x04FF movk x6, #0x0044, lsl #16 /* MAIR = 0x4404FF */ mov x7, #(1 << 0) | (1 << 2) | (1 << 12) /* SCTLR: M, C, I */ mrs x8, CurrentEL ubfx x8, x8, #2, #2 cmp x8, #2 b.ne .Lmmu_el1 msr mair_el2, x6 movz x9, #0x3519 /* T0SZ 25, IRGN0/ORGN0 WB, SH0 inner, TG0 4K */ movk x9, #0x8082, lsl #16 /* PS 40-bit, RES1 bits 23 and 31 */ msr tcr_el2, x9 msr ttbr0_el2, x1 isb tlbi alle2 dsb ish isb ic iallu dsb ish mrs x9, sctlr_el2 orr x9, x9, x7 bic x9, x9, #(1 << 1) /* A: no alignment checking */ bic x9, x9, #(1 << 19) /* WXN: the image is writable Normal memory */ msr sctlr_el2, x9 isb /* Interrupts to EL2, where the kernel runs: with HCR_EL2.IMO/FMO/AMO * clear (as a stub that hands Linux EL1 leaves them, and QEMU resets * them) an IRQ targets EL1 and is never taken at EL2 -- the timer * never ticked (FABRIC-3.md SXXXIX). */ mrs x9, hcr_el2 orr x9, x9, #(1 << 3) /* FMO */ orr x9, x9, #(1 << 4) /* IMO */ orr x9, x9, #(1 << 5) /* AMO */ msr hcr_el2, x9 isb b .Lmmu_done .Lmmu_el1: msr mair_el1, x6 movz x9, #0x3519 /* T0SZ 25, IRGN0/ORGN0 WB, SH0 inner, TG0 4K */ movk x9, #0x0080, lsl #16 /* EPD1: no TTBR1 walks */ movk x9, #0x0002, lsl #32 /* IPS 40-bit */ msr tcr_el1, x9 msr ttbr0_el1, x1 isb tlbi vmalle1 dsb ish isb ic iallu dsb ish mrs x9, sctlr_el1 orr x9, x9, x7 bic x9, x9, #(1 << 1) bic x9, x9, #(1 << 19) msr sctlr_el1, x9 isb .Lmmu_done: /* x0 was clobbered above for the stack-top address -- reload the * masked DTB pointer from g_rpi5_dtb_ptr before calling in. */ adrp x0, g_rpi5_dtb_ptr add x0, x0, :lo12:g_rpi5_dtb_ptr ldr x0, [x0] bl rpi5_native_boot .Lhalt: wfe b .Lhalt