15.6"H" V: Traps — Virtual Instructions, Entry, htinst, Return

Part II Linux boot: optional Vol. II (Privileged) pp. 177–186 · ~4 min read

New cause codes

H adds to the base cause table: interrupts 2/6/10 (VS software/timer/external) and 12 (supervisor guest external); exceptions 10 (ecall from VS — separate from HS’s 9, so the two delegate independently), 20/21/23 (instruction/load/store guest-page faults, slotted into the priority order right beside their page-fault cousins), and 22 (virtual instruction).

The virtual-instruction rule

When V=1, an instruction that is HS-qualified — it would execute in HS-mode assuming TSR=TVM=0 — but is blocked under virtualization raises virtual-instruction (22), not illegal-instruction. The enumerated cases: counter CSRs blocked by hcounteren (or scounteren in VU) while mcounteren grants them; any hypervisor instruction or CSR touched from VS/VU; supervisor instructions/CSRs from VU; WFI under VTW (VS) or TW=0-in-VU; SRET under VTSR; SFENCE/SINVAL/satp under VTVM; high-half CSR partners on XLEN=32.

Why a separate code? Delegation speed: M-mode routes code 22 straight to the hypervisor (its emulation hook), while code 2 typically visits M-mode first. A hypervisor that chooses not to emulate converts it to an illegal-instruction for the guest. One codified exception: FP/vector instructions blocked by FS/VS=Off in either sstatus or vsstatus are always illegal-instruction — a legacy-preserving carve-out.

Trap entry, three destinations

What hardware writes on trap entry (delegation: M unless medeleg/mideleg → HS unless hedeleg/hideleg → VS)
State written
Into M-modeV←0; mstatus.MPV/MPP per Table 52 (VS-mode → MPV=1, MPP=1), GVA, MPIE/MIE; mepc, mcause, mtval, mtval2, mtinst.
Into HS-modeV←0; hstatus.SPV = old V, GVA; sstatus.SPP/SPIE/SIE; sepc, scause, stval, htval, htinst.
Into VS-modeV stays 1; vsstatus.SPP (VU→0, VS→1), SPIE/SIE; vsepc, vscause, vstval. hstatus and HS-sstatus untouched — indistinguishable from a native S-mode trap.
Dotted-underlined cells have explanations — click one.

Returns mirror entries: MRET picks the mode from MPP+MPV (MPP=3 forces V=0), then clears MPV/MPP, restores MIE from MPIE. SRET at V=0 uses hstatus.SPV + sstatus.SPP (clearing SPV); SRET at V=1 (VTSR permitting) is a purely-VS affair through vsstatus/vsepc. With Ssdbltrp, SRET entering VU (or executed in VS) clears vsstatus.SDT.

mtinst / htinst — the pre-decoded trap

On synchronous exceptions from explicit memory accesses, hardware may write a transformed instruction so the handler never fetches or decodes guest code:

00000003125000002420Addr. Offset1915funct31412rd117opcode60Transformed load (Figure 89)
Click a field for its role.

The value grammar keeps the register forward-compatible: bit 0 = 1 with “set bit 1” yielding a standard instruction → transform; a custom-space encoding → custom value (non-standard trapping instructions only); bits 1:0 = 00 → pseudoinstruction; anything else (and always on interrupts, fetch-side faults, ecalls) → zero. Atomics and HLV/HSV transform like loads/stores with all fields preserved except rs1 → Addr. Offset.

Table 56 — pseudoinstruction values (MANDATORY, not zero, when a guest-page fault comes from an implicit VS-stage page-table access and htval/mtval2 is nonzero)
Meaning
0x00002000 / 0x0000300032-bit (VSXLEN=32) / 64-bit (VSXLEN=64) READ for VS-stage address translation — the walker couldn’t read a guest PTE. ≈ lw/ld x0,0(x0) with bits 1:0 = 00.
0x00002020 / 0x00003020The WRITE case — hardware A/D update of a VS-level PTE faulted at G-stage. Never occurs if A/D updates are software’s job. (Atomicity of the real update is ignored here — the hypervisor just makes the page accessible and retries.)
Dotted-underlined cells have explanations — click one.

Hardware Designer Notes

The transform hardware is a latch of decode fields (funct3, rd, rs2, compressed?) carried to the commit stage, plus one subtractor for Addr. Offset — cheap, and it removes a guest-memory round trip from every MMIO emulation exit. Measure before omitting: trap-and-emulate latency is THE hypervisor performance metric.

Minimal Linux-boot hart MUST

  • Implement the virtual-vs-illegal selector: (V=1) ∧ HS-qualified ∧ blocked → 22, with the FS/VS-off carve-out to code 2
  • Write the pseudoinstruction values on implicit-access guest-page faults whenever htval/mtval2 is nonzero — zero is non-conformant exactly there
  • Keep the three-destination entry state machine exact, including SPVP’s only-if-V-was-1 update and MRET’s MPP=3-forces-V=0 rule

MAY simplify / trap-and-emulate

  • Write zero to mtinst/htinst for every transformable case — transforms are a pure emulation-speed optimization
  • Reduce htinst storage to the handful of supported values

Check yourself — H traps

1.A VS-mode guest reads the cycle CSR with hcounteren.CY=0 but mcounteren.CY=1. Which exception, and why does the distinction matter?

2.Which exception deliberately BREAKS the virtual-instruction rule?

3.A trap from VU-mode lands in VS-mode (fully delegated). What does hardware write?

4.htinst reads 0x00003000 after a guest-page fault. What happened?

5.In a transformed load written to htinst, what replaces the rs1 field?

5 questions