FABRIC.md §25: GAP-B2 verified and fixed, GAP-B3 fixed — FP state and EL scope

B2, verified against Makefile.starkernel: no architecture restricts FP register
use. amd64 has no -mno-sse (:124) -- works with live interrupts today, save-set
adequacy unverified. aarch64 lacks -mgeneral-regs-only (:146). riscv64 builds
-march=rv64gc -mabi=lp64d (:162) -- hard-float ABI, and kernel code genuinely
uses doubles (hotwords_stats_print). Items 0.2 and 0.5 now require saving the
ABI caller-saved FP set plus control/status registers, with the exact lists
verified against the psABI/AAPCS64 rather than this document, and explicitly
forbid "fixing" it via soft-float, which would break existing code.

B3: item 0.4's EL detection now governs everything EL-dependent -- vector base
register (today's isr.S writes VBAR_EL1 unconditionally, never consulted for
exceptions taken at EL2), saved-state pair, eret target, and timer registers.
Item 0.5's hardcoded ELR_EL1/SPSR_EL1 wording replaced accordingly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-03 11:31:26 -04:00
co-authored by Claude Fable 5
parent 09d0067b65
commit 8ac284f8c1
+24 -6
View File
@@ -2009,6 +2009,12 @@ on until there is a tick on all three architectures (§16.1, §16.5).*
Replace the one-way `riscv64_trap_entry` in `arch/riscv64/isr.S` with save / dispatch / Replace the one-way `riscv64_trap_entry` in `arch/riscv64/isr.S` with save / dispatch /
restore / `sret`. Route `scause` bit 63 + cause 5 to the timer path; everything else keeps restore / `sret`. Route `scause` bit 63 + cause 5 to the timer path; everything else keeps
falling through to the existing fatal handler. falling through to the existing fatal handler.
**FP state is not optional (B2 verified):** the kernel builds `-march=rv64gc -mabi=lp64d`
(`Makefile.starkernel:162`) — hard-float ABI, and kernel code genuinely uses doubles
(`hotwords_stats_print`). The trap entry must save the ABI's caller-saved FP registers
plus `fcsr` alongside the integer set; verify the exact register list against the RISC-V
psABI, not this document. Do not "fix" this by switching to soft-float — that breaks
existing code and is a build-system decision nobody has made.
*Done when:* riscv64 boots to the prompt unchanged, having taken and returned from at *Done when:* riscv64 boots to the prompt unchanged, having taken and returned from at
least one trap. Exceptions still halt with the same diagnostic as before. least one trap. Exceptions still halt with the same diagnostic as before.
@@ -2025,15 +2031,27 @@ on until there is a tick on all three architectures (§16.1, §16.5).*
relying on it; if it is absent, stop and report rather than falling back silently. relying on it; if it is absent, stop and report rather than falling back silently.
- [ ] **0.4 — aarch64: determine the exception level at runtime.** - [ ] **0.4 — aarch64: determine the exception level at runtime.**
Read `CurrentEL` in `timer_init()` and select the `CNTP_*_EL0` or `CNTHP_*_EL2` register Read `CurrentEL` once, early, and let it govern **everything EL-dependent**, not just the
set accordingly. Do not hardcode either. timer (B3): the vector base register (`VBAR_EL1` vs `VBAR_EL2` — today's `isr.S` writes
`VBAR_EL1` unconditionally, which is never consulted for exceptions taken at EL2), the
saved-state pair (`ELR_ELx`/`SPSR_ELx`), and the timer register set (`CNTP_*_EL0` vs
`CNTHP_*_EL2`). Do not hardcode either level anywhere.
*Done when:* the boot log states which EL was detected, on real QEMU output. *Done when:* the boot log states which EL was detected, on real QEMU output.
- [ ] **0.5 — aarch64: IRQ vector split.** - [ ] **0.5 — aarch64: IRQ vector split.**
Split `irq_spx` out of the shared fatal handler in `arch/aarch64/isr.S`: save `x0``x30`, Split `irq_spx` out of the shared fatal handler in `arch/aarch64/isr.S`: save `x0``x30`
`ELR_EL1`, `SPSR_EL1`, call a C handler, restore, `eret`. The other fifteen vectors are plus the saved-state registers (see B3 note below), call a C handler, restore, `eret`.
unchanged. Note the 128-byte slot limit — the save sequence will not fit inline and must The other fifteen vectors are unchanged. Note the 128-byte slot limit — the save sequence
branch to a trampoline. will not fit inline and must branch to a trampoline.
**FP state is not optional (B2 verified):** the kernel builds without
`-mgeneral-regs-only` (`Makefile.starkernel:146`), so the compiler may use SIMD registers
anywhere. Save the ABI's caller-saved SIMD set plus `FPSR`/`FPCR` alongside the integer
set; verify the exact list against the AAPCS64, not this document.
**EL governs the whole path (B3):** this item previously hardcoded `ELR_EL1`/`SPSR_EL1`,
while 0.4 refuses to hardcode the EL — and today's `isr.S` installs `VBAR_EL1`, which is
never consulted for exceptions taken at EL2. The EL detected in 0.4 must select the
vector base register (`VBAR_ELx`), the saved-state pair (`ELR_ELx`/`SPSR_ELx`), and the
`eret` target state, not just the timer registers.
*Done when:* aarch64 boots to the prompt having taken and returned from at least one IRQ. *Done when:* aarch64 boots to the prompt having taken and returned from at least one IRQ.
- [ ] **0.6 — aarch64: minimal GICv2.** - [ ] **0.6 — aarch64: minimal GICv2.**