Autonomous-mode rung, 6th past P45. After P49 made priv mode real, P50 makes priv mode actually shape where traps go.
What landed
enter_trap now decides between M-mode entry and S-mode entry
based on the trap’s cause and the delegation registers:
delegate_to_s = (cur_priv != M)
&& (!is_interrupt && medeleg[cause_idx])
|| (cur_priv != M)
&& ( is_interrupt && mideleg[cause_idx]);
When delegating, the chip writes sepc/scause/stval instead
of the M-mode equivalents, captures cur_priv into mstatus.SPP,
shifts SIE → SPIE, sets cur_priv = S, and jumps to stvec.
sret (0x10200073) is the dual of mret: restore cur_priv
from SPP, restore SIE from SPIE, set SPP back to U
(per-spec least-priv-supported on sret), pc ← sepc.
sstatus is now a real subset view of mstatus — the P47
standalone storage went away. Reads return mstatus & SSTATUS_MASK,
writes only update those bits in mstatus. Single architectural
register, two projections.
The probe
trap_delegation_probe walks the chip through the full priv
crossing:
- Set
medeleg[ECALL_FROM_S]=1, install probe-local M and S handlers in mtvec/stvec. mretwith MPP=S to a label.ecallfrom S → delegated, S handler runs, captures scause/sstatus, sret keeps us in S.- Clear
medelegfrom S,ecallagain → not delegated, M handler runs, captures mcause/mstatus, mret back to M.
Verified end to end. PASS at 5,102,703 clocks.
Wart count
Zero new. The gcc-zbb-auto-emit-sext.b hang from P46 is still
outstanding; march held at rv32ima_zba_zicsr_zifencei.
What’s still missing for Linux
Interrupt delegation (sip/sie need to be subset views of
mip/mie and the timer/external fast paths need to look at
cur_priv). M-only CSR privilege check on S-mode access. Then
the big one: Sv32 paging behind satp.