target/riscv: an HLV/HSV fault taken into M-mode leaves mstatus.GVA = 0
Host environment
- Operating system: AlmaLinux 9.7, Linux 5.14.0-611.45.1.el9_7.x86_64
- Architecture: x86_64
- QEMU flavor: qemu-system-riscv64 (qemu-system-riscv32 fails the same tests)
- QEMU version: 11.1.1 (release tarball). The code is unchanged on master 5dfdb16f (2026-09-25).
- QEMU command line:
qemu-system-riscv64 -nographic -semihosting -icount shift=1 -machine virt -cpu max,smpmpmt=false,pmu-mask=0xfffffff8,smdbltrp=true,ssdbltrp=true -bios HSm_trap-00.elf
Emulated/Virtualized environment
- Bare-metal, self-checking riscv-arch-test (ACT4) tests from the hypervisor suites in development. Expected values come from the Sail reference model (sail-riscv 0.14.1).
Description of problem
For a trap into M-mode, riscv_cpu_do_interrupt sets mstatus.GVA only if env->virt_enabled && tval. It ignores write_gva, which it computes from two_stage_lookup and already uses for hstatus.GVA. An HLV/HSV executed in M-mode, or in HS/U-mode with the trap not delegated, runs with V=0. So a fault on its explicit access leaves GVA=0, even though mtval holds a guest virtual address:
- HSm_trap-00 executes
hlv.win M-mode with vsatp = Bare and a G-stage table that does not map GPA 0x1000. It gets a load guest-page fault withmtval = 0x1000,mtval2 = 0x400and GVA = 0. - PMPH_hlv_m-00 executes
hsv.bto a region that PMP denies to VS/VU. It gets a store access fault withmtval = 0x80009008and GVA = 0.
Sail sets GVA = 1 in both cases. The HS-mode path is correct (ACT H_trap-00 passes).
We also found two more cases by inspection. The same code never clears GVA on a trap into M-mode, and it sets GVA = 1 for any V=1 trap whose nonzero tval is not an address (for example, an illegal-instruction trap whose mtval holds the instruction). Since commit 18645f19 software cannot write GVA, so once set it stays 1. Our tests cannot reach these cases on 11.1.x, because MRET cannot enter VS-mode (#4588: mstatus.MPV is not writable).
Spec, hypervisor.adoc:
- norm:mstatus_gva_op: "Field GVA (Guest Virtual Address) is written by the implementation whenever a trap is taken into M-mode. For any trap (breakpoint, address misaligned, access fault, page fault, or guest-page fault) that writes a guest virtual address to
mtval, GVA is set to 1. For any other trap into M-mode, GVA is set to 0." - The note after norm:hstatus_gva_op says GVA is redundant with SPV "except when the explicit memory access of an HLV, HLVX, or HSV instruction causes a fault. In that case, SPV=0 but GVA=1."
Steps to reproduce
- Unpack 14-qemu-mstatus-gva-hlv.tar.gz and
cd 14-qemu-mstatus-gva-hlv. - Run the command above, and again with
-bios PMPH_hlv_m-00.elf. Each printsRVCP-SUMMARY: TEST FAILED, "Mismatch in trap signature",Expected value: 0x...0b000673,Actual value: 0x...03000673. Bit 27 of the trap-signature word is mstatus.GVA. ./run.shruns both with-d in_asm,int,cpu,nochain -accel tcg,one-insn-per-tb=onand prints the excerpts below, showing register values after each instruction. Ifsail_riscv_simis on PATH, it also prints Sail's trace.
Additional information
HSm_trap-00 (M-mode, hgatp = Sv39x4, vsatp = Bare):
M 0x80000088: csrrw x0,hgatp,x14 x14=800000000008000c mstatus=8000000a00007e00 mcause=0000000000000000 mtval=0000000000000000 mtval2=0000000000000000
M 0x800000a0: hlv.w x13,(x14) x14=0000000000001000 mstatus=8000040a00007e00 mcause=0000000000000015 mtval=0000000000001000 mtval2=0000000000000400
riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000015, epc:0x800000a0, tval:0x0000000000001000, desc=guest_load_page_faultPMPH_hlv_m-00 (M-mode, PMP entry 5 grants no permissions to VS/VU):
M 0x80000160: hsv.b x9,(x13) x13=0000000080009008 mstatus=8000040a00007e00 mcause=0000000000000007 mtval=0000000080009008
riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000007, epc:0x80000160, tval:0x0000000080009008, desc=fault_storeSail at both traps: CSR mstatus (0x300) <- 0x8000004A00007E00, with GVA (bit 38) = 1. In QEMU's 0x8000040a00007e00, bit 38 is 0. Bit 42 is MDT, from Smdbltrp, which the Sail configuration does not enable.
Source (11.1.1): the M-mode path in target/riscv/tcg/cpu_helper.c:2288-2294 sets GVA only if (env->virt_enabled && tval), while the HS-mode path at cpu_helper.c:2244 uses hstatus.GVA = write_gva. Fix: in the M-mode path, use env->mstatus = set_field(env->mstatus, MSTATUS_GVA, write_gva);.