arch/aarch64: fix VBAR_EL2 stale comment + hardcode HVC PSCI conduit
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

FABRIC-3.md §IV.3 item 7's code audit found two more findings; fixed both
per direct instruction naming them specifically.

Comments only, no behavior change:
interrupts.c's arch_interrupts_init() doc comment (and its matching
runtime EL2 console message) claimed VBAR_EL2 was "a known gap... not yet
wired." Checked against isr.S's aarch64_install_vectors() and found that
claim stale: the assembly already branches on aarch64_current_el() and
writes vbar_el2/vbar_el1 correctly, threading the same answer through
el2_mode_flag for the IRQ trampoline's ELR/SPSR selection too. There was
no gap to close -- only the comment was wrong. Also fixed a same-vintage
one-word staleness in arch.c's aarch64_install_vectors extern comment
("installs VBAR_EL1" -> EL-aware), caught while touching this.

Real behavior change, gated to preserve existing QEMU behavior:
arch_cold_reset() hardcoded the PSCI conduit to HVC, a documented
QEMU-specific workaround (QEMU's AAVMF has no genuine EL3 to answer SMC).
Real Pi 5 hardware's ATF means EL3 exists there; confirmed further this
pass that bcm2712.dtsi's own /psci node declares method="smc" directly,
not just inferred from ATF's presence.

New aarch64_psci_conduit_init(dtb) in arch.c: fdt_valid(dtb) ->
fdt_find_node_by_compatible(dtb, "arm,psci-1.0") ->
fdt_find_prop_in_node(..., "method", ...) -- sets a module-static
s_psci_use_smc flag to 1 only when method reads exactly "smc"; every
other outcome (no DTB, no PSCI node, method="hvc", property absent)
leaves it at its default 0/HVC, preserving this system's existing QEMU
behavior exactly. arch_cold_reset() now branches on that flag between
smc #0/hvc #0. Called once from apic_init() (arch/aarch64/apic.c), the
one point in boot with boot_info->dtb already in hand -- collocated with
its consumer in arch.c rather than defined in apic.c, since
arch_cold_reset() has no boot_info of its own by the time it runs (called
from deep in VM execution, via BYE).

Three-arch QEMU acceptance run exercised both new guard branches: the
aarch64 boot log shows "PSCI: no DTB -- using HVC (QEMU virt-machine
default)" immediately before the existing GIC discovery lines, then
boots clean to zuse)ok> -- confirming the QEMU/UEFI path is unchanged.
The SMC success branch itself stays unverified until real Pi 5 hardware
runs BYE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
Robert Allan James
2026-09-05 00:06:41 -04:00
co-authored by Claude Sonnet 5
parent dc9445abec
commit 350287850f
12 changed files with 27752 additions and 49 deletions
+46 -21
View File
@@ -541,9 +541,12 @@ 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 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
"identify, don't fix unless asked" rule — **all three findings below were fixed in code
across two follow-up rounds, each named specifically by direct instruction after this
audit landed**: the GIC base address (severe), the stale VBAR_EL2 doc comment (doc-only),
and the hardcoded HVC PSCI conduit. §V.3's own parallel audit found a fourth finding of
this same class (riscv64's `satp`-clear reasoning, lowest severity of the whole set) that
remains report-only, not asked for. See each finding's own
own text for what changed.
**Finding — severe, confirmed live, blocked reaching `ok>` on real hardware as the code
@@ -580,29 +583,51 @@ blocker for free.
`"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
**Finding — doc-only, not a functional gap — FIXED (comment corrected) 2026-09-04, per
direct instruction naming it specifically.** `arch_interrupts_init()`'s own doc comment
in `interrupts.c` claimed "`VBAR_EL1` is written unconditionally regardless of the
detected level... if EL2, this is a known gap" — checked against `isr.S`
(`aarch64_install_vectors`, lines 124146) and found **stale**: the actual implementation
already branches on `aarch64_current_el()` and writes `vbar_el2`/`vbar_el1` correctly,
with `el2_mode_flag` threading the same answer through the IRQ trampoline for
`ELR_EL1`/`SPSR_EL1` vs `ELR_EL2`/`SPSR_EL2` selection. The comment describes a gap the
code already closed; left as-is per report-only policy, but flagged so a future reader
doesn't take the stale comment at face value. Which EL the Pi 5's ATF hands the kernel
off at under the native path is itself unconfirmed (the `atf@0` reserved-memory region
found during item 2's work only establishes that EL3 firmware exists, not which EL the
kernel lands at) — moot for this specific gap now that both paths are actually wired, but
worth knowing for other EL-dependent code.
`ELR_EL1`/`SPSR_EL1` vs `ELR_EL2`/`SPSR_EL2` selection. Rewrote the doc comment and the
matching runtime console message (which made the identical stale claim — "not yet
wired, see item 0.5/0.7") to describe what `isr.S` actually does, and fixed a
same-vintage one-word staleness in `arch.c`'s own `aarch64_install_vectors` extern
comment ("installs `VBAR_EL1`" → EL-aware) caught while touching this. No behavior
changed anywhere — this was purely three comments correcting themselves to match code
that was already right. Which EL the Pi 5's ATF hands the kernel off at under the native
path is itself still unconfirmed (the `atf@0` reserved-memory region found during item
2's work only establishes that EL3 firmware exists, not which EL the kernel lands at) —
moot for this specific fix now that both paths are correctly wired regardless of which EL
is chosen, but worth knowing for other EL-dependent code.
**Finding — same defect class as amd64's `arch_cold_reset()` finding, on the QEMU side of
this arch specifically**: `arch/aarch64/arch.c`'s `arch_cold_reset()` issues PSCI
`SYSTEM_RESET` via `HVC` — a fix already documented in-file as a QEMU-specific
workaround, because QEMU's AAVMF firmware has no genuine EL3/TrustZone secure monitor to
answer an `SMC`. The Pi 5's real ATF (confirmed present via the `atf@0` reservation) is
exactly the kind of genuine EL3 firmware PSCI's `SMC` conduit assumes — the conventional
real-hardware call, not `HVC`. No runtime conduit detection exists; this is hardcoded to
the QEMU-only fix with no fallback. Riscv64's equivalent (`arch/riscv64/arch.c`, SBI SRST)
is the portable pattern this and amd64's i8042-pulse version both lack — worth naming as
the standard to bring the other two toward, not fixed here.
this arch specifically — FIXED 2026-09-04, per direct instruction naming it
specifically.** `arch/aarch64/arch.c`'s `arch_cold_reset()` issued PSCI `SYSTEM_RESET` via
`HVC` unconditionally — a fix already documented in-file as a QEMU-specific workaround,
because QEMU's AAVMF firmware has no genuine EL3/TrustZone secure monitor to answer an
`SMC`. The Pi 5's real ATF (confirmed present via the `atf@0` reservation) is exactly the
kind of genuine EL3 firmware PSCI's `SMC` conduit assumes — confirmed further this pass:
`bcm2712.dtsi`'s own `/psci` node declares `method = "smc"` directly, not inferred from
ATF's mere presence. **Fix**: new `aarch64_psci_conduit_init(dtb)` (`arch.c`) —
`fdt_valid(dtb)``fdt_find_node_by_compatible(dtb, "arm,psci-1.0")`
`fdt_find_prop_in_node(..., "method", ...)` — sets a module-static `s_psci_use_smc` flag
to 1 only when the DTB's own `method` property reads exactly `"smc"`; every other
outcome (no DTB, no PSCI node, `method="hvc"`, property absent) leaves it at its default
0, preserving the exact HVC behaviour this system's QEMU boot already relies on.
`arch_cold_reset()` now branches on that flag between the `smc #0`/`hvc #0` trap
instructions; the function ID and calling convention are identical either way, matching
the file's own existing comment about that. Called once from `apic_init()`
(`arch/aarch64/apic.c`), the one point in boot with `boot_info->dtb` already in hand —
collocated in `arch.c`, not `apic.c`, because `arch_cold_reset()` is the actual consumer
and has no `boot_info` of its own by the time it runs (called from deep in VM execution,
via `BYE`). Riscv64's equivalent (`arch/riscv64/arch.c`, SBI SRST) remains the one cold-
reset implementation across all three architectures that never needed this kind of fix —
still worth naming as the standard the other two moved toward, one now fixed, amd64's
i8042-pulse version still open. 3-arch acceptance exercised the guard (this system's QEMU
AAVMF forwards no DTB, so `aarch64_psci_conduit_init()` hits its own "no DTB" branch and
`arch_cold_reset()` keeps using HVC, unchanged) but not the `SMC` success branch itself —
that stays unverified until real Pi 5 hardware runs `BYE`.
**Hardware-dependent, after 2026-09-17 (not started until then):**
8. Build the boot media (SD card: `config.txt`, `bcm2712-rpi-5-b.dtb`, kernel image).