riscv64: skip satp Bare-mode switch when already Bare (§V.3 item 7 audit fix)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

arch_early_init() unconditionally cleared satp on every boot, justified
only by behavior observed under QEMU/EDK2 firmware (satp.MODE=10/Sv57,
kernel identity-mapped within it). That reasoning never applied to the
native U-Boot+OpenSBI boot path, where satp is conventionally already 0
at S-mode handoff -- the unconditional clear was likely a harmless no-op
there, but on an unverified assumption.

Fix: read satp.MODE first and return early when it's already 0 (nothing
to switch away from, no safety argument needed). The unconditional
csrw/sfence pair still runs unchanged for the confirmed QEMU/EDK2 case.
No Sv39/Sv48/Sv57 page-table walker built -- out of proportion to this
finding's severity.

3-arch acceptance: amd64/aarch64 don't compile this file, so their runs
are non-regression on untouched files only. riscv64's own boot log
confirms satp.MODE = 0xa at entry, so the mode != 0 branch ran and
"satp cleared -- Bare mode, explicit" printed exactly as before the fix.

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:17:35 -04:00
co-authored by Claude Sonnet 5
parent 350287850f
commit 90ee8deb6d
10 changed files with 27648 additions and 21 deletions
+29 -12
View File
@@ -747,8 +747,11 @@ it doesn't. This is a real punch-list item, not a hypothetical.
7. **Code audit pass — DONE, 2026-09-04.** Reviewed `arch/riscv64/apic.c`, `plic.c`,
`arch.c`, `interrupts.c`, `timer.c` for the same class of QEMU-virt-vs-real-hardware
assumption §III item 6's amd64 audit and item 7 above's aarch64 audit looked for. Report
only — nothing below was changed.
assumption §III item 6's amd64 audit and item 7 above's aarch64 audit looked for.
Report-only by default — the `satp`-clear finding below was fixed in code, per direct
instruction naming it specifically after this audit landed. The other findings (PLIC
base, already tracked as item 3 above; the clean SBI/SBI-SRST findings) needed no code
change either way.
**Clean, and the most portable pattern of all three architectures**: the SBI timer path
(`apic.c`) is genuinely hardware-independent — it probes for the TIME extension at
@@ -766,16 +769,30 @@ it doesn't. This is a real punch-list item, not a hypothetical.
already flagged as "a real punch-list item, not a hypothetical" before this audit ran.
Nothing new to add beyond confirming the file's own header comment is accurate and the
risk is real, not overstated.
**Finding — minor, reasoning doesn't transfer to the native path, action likely still
safe**: `arch_early_init()`'s explicit `satp` clear (Bare-mode switch) is justified in
its own comment entirely by behavior *observed under QEMU's EDK2 RISC-V firmware*
(confirmed `satp.MODE=10`/Sv57 live, kernel identity-mapped within it). Under the native
boot path (U-Boot+OpenSBI, no UEFI/EDK2 at all per §V.1) that specific observation cannot
apply — OpenSBI's S-mode handoff conventionally already leaves `satp=0` (Bare mode), most
likely making this a no-op there rather than a hazard, but the *stated justification* for
the switch (identity-mapped, safe to clear) was derived from a firmware stack the native
path doesn't use, and hasn't been re-confirmed for OpenSBI's actual handoff state. Lowest
severity of any finding across both audits — flagged for completeness, not urgency.
**Finding — minor, reasoning didn't transfer to the native path — FIXED 2026-09-04, per
direct instruction naming it specifically.** `arch_early_init()`'s explicit `satp` clear
(Bare-mode switch) was justified in its own comment entirely by behavior *observed under
QEMU's EDK2 RISC-V firmware* (confirmed `satp.MODE=10`/Sv57 live, kernel identity-mapped
within it). Under the native boot path (U-Boot+OpenSBI, no UEFI/EDK2 at all per §V.1)
that specific observation cannot apply — OpenSBI's S-mode handoff conventionally already
leaves `satp=0` (Bare mode), most likely making the unconditional switch a harmless no-op
there, but on an unverified assumption rather than a checked one.
**Fix**: rather than build a general Sv39/Sv48/Sv57 page-table walker just to *prove* the
running address is identity-mapped before switching (out of proportion to this finding's
own severity), `arch_early_init()` now reads `satp.MODE` first and **skips the switch
entirely when it's already 0** — the expected case on a from-Bare native boot, needing no
safety argument at all since there's nothing to switch away from. The unconditional
`csrw satp, x0`/`sfence.vma` pair still runs, unchanged, for the confirmed QEMU/EDK2 case
(`mode != 0`), where the identity-mapping argument remains the actual justification it
always was. If the native path somehow reaches the non-zero branch anyway (OpenSBI
leaving a non-Bare `satp`, not confirmed either way), the code says so explicitly in its
own comment now rather than silently relying on the QEMU-only observation. Verified against
the riscv64 boot log's own `satp.MODE` print, not assumed: this system's QEMU/EDK2 boot
shows `satp.MODE = 0x000000000000000a` (Sv57) at kernel entry, so the `mode != 0` branch
ran and printed `satp cleared -- Bare mode, explicit` exactly as before the fix — the
unconditional-switch path is unchanged on the one boot path that exercises it. amd64 and
aarch64 don't compile this file at all, so their part of the 3-arch acceptance run is pure
non-regression on files untouched by this fix, not evidence about the guard itself.
**Hardware-dependent, after 2026-09-17:**
8. Build and flash the boot image to QSPI flash (or attempt UART XMODEM recovery boot if QSPI