Files
LithosAnanke/kernel/src/arch/aarch64/native_rpi5_entry.S
T
rajamesandClaude Opus 5.5 0e2c60dac3 fix(raspi): the Pi 5's native path turns the MMU and caches on before any C; bounded mailbox; framebuffer address from dma-ranges
boards/BRINGUP.md R2/R3, FABRIC-3 SXXXIX.  The entry stub cleans the cache
over the image, builds an identity map (the image's GiB Normal, the rest
Device) and enables MMU and caches at EL2 (or EL1); rpi5_mmu.c makes the
devicetree's RAM Normal and the framebuffer uncached.  Mailbox waits give
up after one second; the buffer is cleaned around the exchange; the
framebuffer address goes through dma-ranges, not a Pi 4 mask.

New rpi5-rehearsal target runs the native image on QEMU virt at EL2.  It
found three more that would each have stopped the board: the PL011 shift
(mine, c9519173; the host test asserted it), the GIC address not carried
through /soc's ranges (new fdt_reg_address(), checked against the real
Pi 5 DTB), and no timer IRQ at EL2 (HCR_EL2.IMO/FMO/AMO).

Rehearsal: POST 1052 passed, same parity hash as UEFI.  Standard acceptance
on three ISAs: POST 1052, hash 0x82f4ae7cb2e2d424.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:58:35 -04:00

243 lines
8.8 KiB
ArmAsm

/*
* 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