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>
243 lines
8.8 KiB
ArmAsm
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
|