apic.c (aarch64): fix GIC base address for real Pi 5 hardware
FABRIC-3.md §IV.3 item 7's code audit found GICD_BASE_PA/GICC_BASE_PA hardcoded to QEMU virt-machine constants (0x08000000/0x08010000), self-documented as a deliberate exception because QEMU's aarch64 UEFI firmware never forwards a DTB. That premise doesn't hold on the native Pi 5 boot path (rpi5_native_boot.c), which does have a real DTB and calls this same, unmodified apic_init() -- which ignored boot_info entirely and always programmed the QEMU addresses. Real BCM2712 GIC-400 is at 0x10_7fff9000 (confirmed against bcm2712.dtsi's axi/gicv2 nodes), a completely different region of the address space. Fixed per direct instruction, naming this finding specifically. apic_init() now calls gic_bases_from_dtb() first: fdt_valid(dtb) -> fdt_find_node_by_compatible(dtb, "arm,gic-400") -> fdt_find_prop_in_node(..., "reg", ...), reading the first two 2-address-cell/2-size-cell entries (GICD, then GICC -- the standard arm,gic-400 binding order). Falls back to the QEMU constants on any failure (no DTB, no matching node), so the existing QEMU/UEFI path is unchanged. GICD_BASE_PA/GICC_BASE_PA became s_gicd_base/s_gicc_base (module-static uintptr_t, no longer compile-time constants on this path) -- every MMIO call site (apic_init, apic_spi_enable, apic_read_iar, apic_eoi_intid) now reads through them. Three-arch QEMU acceptance run exercised the guard itself, not just compiled it: the aarch64 boot log shows "GICv2: no DTB GIC node -- using QEMU virt-machine defaults" followed by the unchanged "distributor+CPU interface enabled, PPI 30" line, then boots clean to zuse)ok>. The success branch (a real DTB with a matching node) stays unverified until real Pi 5 hardware -- this build never has a devicetree to exercise it against. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0f798256a0
commit
dc9445abec
+39
-23
@@ -540,30 +540,46 @@ blocker for free.
|
||||
|
||||
7. **Code audit pass — DONE, 2026-09-04.** Reviewed `arch/aarch64/apic.c`, `arch.c`,
|
||||
`timer.c`, `interrupts.c` for the same class of QEMU-virt-vs-real-hardware assumption
|
||||
§III item 6's amd64 audit looked for. Report only, per this project's own "identify,
|
||||
don't fix unless asked" rule — nothing below was changed.
|
||||
§III item 6's amd64 audit looked for. Report-only by default, per this project's own
|
||||
"identify, don't fix unless asked" rule — three of the four findings below are report
|
||||
only, nothing changed for them. **The GIC base address finding was fixed in code**, per
|
||||
direct instruction naming it specifically after this audit landed — see that finding's
|
||||
own text for what changed.
|
||||
|
||||
**Finding — severe, confirmed live, blocks reaching `ok>` on real hardware as the code
|
||||
stands today**: `apic.c`'s own file header already self-documents `GICD_BASE_PA`/
|
||||
`GICC_BASE_PA` (`0x08000000`/`0x08010000`) as "QEMU-virt-machine constants... a
|
||||
deliberate, recorded exception," reasoned correctly at the time it was written: no DTB
|
||||
was ever available to discover them from, because QEMU's own aarch64 UEFI firmware
|
||||
doesn't forward one. That premise no longer holds — the native boot path (item 2, DONE)
|
||||
receives a real DTB directly and calls the same, unmodified `kernel_main()` M4 sequence,
|
||||
which calls `apic_init(boot_info)` unconditionally (confirmed: `kernel_main.c:413`, no
|
||||
arch- or boot-path gating) — `apic_init()` itself still ignores `boot_info` entirely
|
||||
(`(void)boot_info;`) and always programs the QEMU addresses. The real BCM2712 GIC-400 is
|
||||
at `0x10_7fff9000` (distributor) / `0x10_7fffa000` (CPU interface 0) — confirmed directly
|
||||
against `bcm2712.dtsi`'s own `gicv2` node (`compatible = "arm,gic-400"`), not recalled —
|
||||
a completely different region of the physical address space from QEMU's. With the MMU off
|
||||
at this point in boot (per the standard aarch64 boot protocol and this kernel's own
|
||||
`arch_mmu_init()` stub), a 32-bit MMIO write to an address BCM2712 doesn't decode as GIC
|
||||
registers is a data abort, not a silent wrong-value write — this blocks item 10 below
|
||||
(`ok>`) as the code stands, on the same critical path as item 1's still-missing `0x80000`
|
||||
build target. Not fixed this pass — `fdt_find_node_by_compatible(dtb, "arm,gic-400")` →
|
||||
`fdt_find_prop_in_node(..., "reg", ...)` is the existing primitive that would supply the
|
||||
real base once `apic_init()` is updated to use it when a DTB is present, falling back to
|
||||
the QEMU constants when it (still correctly) isn't.
|
||||
**Finding — severe, confirmed live, blocked reaching `ok>` on real hardware as the code
|
||||
stood — FIXED 2026-09-04, per direct instruction (this item's own findings are normally
|
||||
report-only; this one was explicitly asked for by name).** `apic.c`'s own file header
|
||||
already self-documented `GICD_BASE_PA`/`GICC_BASE_PA` (`0x08000000`/`0x08010000`) as
|
||||
"QEMU-virt-machine constants... a deliberate, recorded exception," reasoned correctly at
|
||||
the time it was written: no DTB was ever available to discover them from, because QEMU's
|
||||
own aarch64 UEFI firmware doesn't forward one. That premise no longer held — the native
|
||||
boot path (item 2, DONE) receives a real DTB directly and calls the same, unmodified
|
||||
`kernel_main()` M4 sequence, which calls `apic_init(boot_info)` unconditionally
|
||||
(confirmed: `kernel_main.c:413`, no arch- or boot-path gating) — `apic_init()` itself
|
||||
ignored `boot_info` entirely and always programmed the QEMU addresses. The real BCM2712
|
||||
GIC-400 is at `0x10_7fff9000` (distributor) / `0x10_7fffa000` (CPU interface 0) —
|
||||
confirmed directly against `bcm2712.dtsi`'s own `axi`/`gicv2` nodes
|
||||
(`compatible = "arm,gic-400"`, `#address-cells = <2>`/`#size-cells = <2>`, `axi`'s own
|
||||
`ranges` identity-mapping this region — no offset needed, unlike `rpi5_dtb.c`'s
|
||||
1-cell/offset `soc` peripherals), not recalled.
|
||||
**Fix**: `apic.c` now tries `gic_bases_from_dtb()` first — `fdt_valid(dtb)` →
|
||||
`fdt_find_node_by_compatible(dtb, "arm,gic-400")` → `fdt_find_prop_in_node(..., "reg",
|
||||
...)`, reading the first two 2-address-cell/2-size-cell entries (GICD, then GICC — the
|
||||
standard arm,gic-400 binding order) — and falls back to the QEMU constants only when no
|
||||
DTB or no matching node is found, so the existing QEMU/UEFI path's behaviour (verified via
|
||||
the mandatory 3-arch boot) is unchanged. `GICD_BASE_PA`/`GICC_BASE_PA` became
|
||||
`s_gicd_base`/`s_gicc_base` (module-static `uintptr_t`, not `#define`s, since they're no
|
||||
longer compile-time constants on this path) — every MMIO call site (`apic_init()`,
|
||||
`apic_spi_enable()`, `apic_read_iar()`, `apic_eoi_intid()`) now reads through them.
|
||||
The 3-arch acceptance run exercised the *guard*, not just compiled it: `apic_init()`'s
|
||||
`gic_bases_from_dtb()` call runs unconditionally on every aarch64 boot, including this
|
||||
one, and its `fdt_valid()` check correctly returned 0 on this system's QEMU/UEFI firmware
|
||||
— confirmed via the boot log's own `GICv2: no DTB GIC node -- using QEMU virt-machine
|
||||
defaults` line, followed by the unchanged `distributor+CPU interface enabled, PPI 30`.
|
||||
What remains genuinely unexercised is the *success* branch (a real DTB with a matching
|
||||
`"arm,gic-400"` node) — that needs a devicetree this build never has, so it stays
|
||||
unverified until real Pi 5 hardware, same caveat as every other native-path item in this
|
||||
list.
|
||||
**Finding — doc-only, not a functional gap**: `arch_interrupts_init()`'s own doc comment
|
||||
in `interrupts.c` claims "`VBAR_EL1` is written unconditionally regardless of the
|
||||
detected level... if EL2, this is a known gap" — checked against `isr.S`
|
||||
|
||||
Reference in New Issue
Block a user