Bug Description
In H-enabled Rocket, the first VS-mode csrr sstatus after an HS sret can return the HS-level host FS/SD view instead of the guest-visible vsstatus view.
The PoC deliberately makes the two architectural views different before entering VS mode:
- HS-level host state is forced to
FS=Dirty, so host sstatus would report FS=Dirty and SD=1.
- Guest-visible
vsstatus is forced to FS=Initial, so VS-visible sstatus should report FS=Initial and SD=0 before any VS floating-point instruction executes.
- As a control, HS
sscratch is set to 0x1111 while guest vsscratch is set to 0x2222.
After HS sets hstatus.SPV=1 and executes sret, the first VS-side control read:
returns the guest-visible vsscratch value 0x2222, showing that the hart is not simply stuck on the HS sscratch view.
However, the very next read:
still returns a value whose FS/SD fields match the host view rather than the programmed guest-visible vsstatus view.
The replay shows a specific split: sscratch -> vsscratch substitution succeeds, but sstatus -> vsstatus substitution does not.
Upstream version confirmation: this issue was validated against the official Rocket Chip master line at revision v1.6-819-g55bcad0f5, confirmed at commit 55bcad0f59436de98ea510334121de8546b9e9d7, using freechips.rocketchip.system.HypervisorConfig. No fix revision has been identified yet.
RISC-V Specification Requirement
The RISC-V Privileged ISA hypervisor extension defines two requirements that matter here:
- When
V=1, vsstatus substitutes for the usual sstatus, so instructions that normally access sstatus are required to access the guest-visible vsstatus view instead.
- When HS software executes
SRET with hstatus.SPV=1, the hart returns into a virtualized supervisor context, after which supervisor CSR accesses should follow the VS substitution rules.
Reference: RISC-V Privileged ISA v20260120, Hypervisor Extension
Under those rules, the first VS-side csrr sstatus in this PoC should observe the guest-programmed vsstatus state, not the HS host sstatus state.
Steps to Reproduce
- Use the attached minimal PoC (
program.S / program.elf).
- Run the official Verilator emulator with fixed seed
1:
emulator --seed=1 program.elf
- Optionally use the replay harness to capture the value-level mismatch on the first VS-mode
sstatus read:
replay_one_source.py --core rocket
The PoC builds the smallest state split needed to expose the problem:
- Start in M-mode on an H-enabled Rocket configuration and open a permissive PMP region so HS/VS software can execute from the loaded image.
- Force the HS host view to be obviously dirty:
li t0, STATUS_FS_MASK
csrc mstatus, t0
li t0, STATUS_FS_DIRTY
csrs mstatus, t0
- Program a control pair where host and guest supervisor scratch CSRs differ:
li t0, HOST_SSCRATCH_VAL
csrw sscratch, t0
li t0, GUEST_SSCRATCH_VAL
csrw CSR_VSSCRATCH, t0
- Program guest-visible
vsstatus to a clean initial state:
csrw CSR_VSSTATUS, zero
li t0, STATUS_FS_INITIAL
csrs CSR_VSSTATUS, t0
- Enter HS with
mret, then set hstatus.SPV=1, sstatus.SPP=1, and sepc=vs_entry, and execute sret.
- In
vs_entry, first read sscratch as the control probe and then read sstatus as the target signal:
csrr s4, sscratch
li t0, GUEST_SSCRATCH_VAL
bne s4, t0, fail_sscratch_alias
csrr s0, sstatus
Expected Result
After the HS sret, both supervisor CSR reads should use the guest-visible VS substitutions:
csrr sscratch should return 0x2222
csrr sstatus should report FS=Initial and SD=0
Actual Result
The PoC reaches its failure path because the first VS-mode sstatus read still exposes the host FS/SD view:
[27] commit pc 00000000800000e0 ... csrr s4, sscratch
W[r20=0000000000002222]
[31] commit pc 00000000800000fc ... csrr s0, sstatus
W[r 8=8000000200006000]
REF s0 = 0x0000000200002000
So the control read already switches sscratch to the guest-visible vsscratch value 0x2222, but the following csrr sstatus still returns the host view (FS=Dirty, SD=1) instead of the guest-visible VS state (FS=Initial, SD=0).
The official emulator run exits through the fail path:
*** FAILED *** (tohost = 31)
*** FAILED *** via dtm (code = 31, seed 1) after 34094 cycles
RK-3.zip
Bug Description
In H-enabled Rocket, the first VS-mode
csrr sstatusafter an HSsretcan return the HS-level host FS/SD view instead of the guest-visiblevsstatusview.The PoC deliberately makes the two architectural views different before entering VS mode:
FS=Dirty, so hostsstatuswould reportFS=DirtyandSD=1.vsstatusis forced toFS=Initial, so VS-visiblesstatusshould reportFS=InitialandSD=0before any VS floating-point instruction executes.sscratchis set to0x1111while guestvsscratchis set to0x2222.After HS sets
hstatus.SPV=1and executessret, the first VS-side control read:returns the guest-visible
vsscratchvalue0x2222, showing that the hart is not simply stuck on the HSsscratchview.However, the very next read:
still returns a value whose FS/SD fields match the host view rather than the programmed guest-visible
vsstatusview.The replay shows a specific split:
sscratch -> vsscratchsubstitution succeeds, butsstatus -> vsstatussubstitution does not.Upstream version confirmation: this issue was validated against the official Rocket Chip
masterline at revisionv1.6-819-g55bcad0f5, confirmed at commit55bcad0f59436de98ea510334121de8546b9e9d7, usingfreechips.rocketchip.system.HypervisorConfig. No fix revision has been identified yet.RISC-V Specification Requirement
The RISC-V Privileged ISA hypervisor extension defines two requirements that matter here:
V=1,vsstatussubstitutes for the usualsstatus, so instructions that normally accesssstatusare required to access the guest-visiblevsstatusview instead.SRETwithhstatus.SPV=1, the hart returns into a virtualized supervisor context, after which supervisor CSR accesses should follow the VS substitution rules.Reference: RISC-V Privileged ISA v20260120, Hypervisor Extension
Under those rules, the first VS-side
csrr sstatusin this PoC should observe the guest-programmedvsstatusstate, not the HS hostsstatusstate.Steps to Reproduce
program.S/program.elf).1:sstatusread:The PoC builds the smallest state split needed to expose the problem:
vsstatusto a clean initial state:mret, then sethstatus.SPV=1,sstatus.SPP=1, andsepc=vs_entry, and executesret.vs_entry, first readsscratchas the control probe and then readsstatusas the target signal:Expected Result
After the HS
sret, both supervisor CSR reads should use the guest-visible VS substitutions:csrr sscratchshould return0x2222csrr sstatusshould reportFS=InitialandSD=0Actual Result
The PoC reaches its failure path because the first VS-mode
sstatusread still exposes the host FS/SD view:So the control read already switches
sscratchto the guest-visiblevsscratchvalue0x2222, but the followingcsrr sstatusstill returns the host view (FS=Dirty,SD=1) instead of the guest-visible VS state (FS=Initial,SD=0).The official emulator run exits through the fail path:
RK-3.zip