On Sun, 2026-07-05 at 16:02 +0200, imaginos wrote: > During a two-stage (VS-stage + G-stage) page-table walk with hardware > A/D updating enabled (Svadu / menvcfg.ADUE), a store that reaches a > VS-stage leaf PTE whose accessed or dirty bit is clear triggers a > hardware write-back of those bits into the PTE. That write-back is an > implicit store to the PTE's guest-physical address, so it must be > permitted by G-stage. > > Fix this by re-running the G-stage translation of the guest PTE's > address with store semantics. get_physical_address() then also checks > that G-stage permits the guest PTE to be written, and raises a > guest-page store fault when it does not. > > Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3426 > Signed-off-by: imaginos <[email protected]>
Reviewed-by: Alistair Francis <[email protected]> Alistair > --- > target/riscv/cpu_helper.c | 28 ++++++++++++++++++++++++++++ > 1 file changed, 28 insertions(+) > > v2: > Reword an incomplete sentence in the code comment. > > Verified against Spike, with this patch QEMU's > reported values match Spike. > > Spike (reference): QEMU, before this patch: > mcause 0x17 mcause 0x17 > mtval 0x80000000 mtval 0x80000000 > mtval2 0x20001004 mtval2 0x20000000 > mtinst 0x3020 mtinst 0x6a704073 > > If I've misread any of the A/D-update semantics here, I'd appreciate > the > correction. > > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c > index 2db07f5dfb..eb34ba19e1 100644 > --- a/target/riscv/cpu_helper.c > +++ b/target/riscv/cpu_helper.c > @@ -1370,6 +1370,7 @@ static int get_physical_address(CPURISCVState > *env, hwaddr *physical, > int ptshift; > target_ulong pte; > hwaddr pte_addr; > + hwaddr pte_gpa = 0; > const hwaddr base_root = base; > const bool be = mo_endian_env(env) == MO_BE; > int i; > @@ -1407,6 +1408,7 @@ static int get_physical_address(CPURISCVState > *env, hwaddr *physical, > } > > pte_addr = vbase + idx * ptesize; > + pte_gpa = base + idx * ptesize; > } else { > pte_addr = base + idx * ptesize; > } > @@ -1661,6 +1663,28 @@ static int get_physical_address(CPURISCVState > *env, hwaddr *physical, > return TRANSLATE_FAIL; > } > > + /* > + * The implicit store that writes updated A/D bits back to a > VS-stage > + * (first-stage) PTE must itself be permitted by G-stage. > Re-run the > + * second-stage translation of the PTE's guest-physical > address with > + * store semantics. If G-stage denies write, raise G-stage > store fault > + * against the PTE address. > + */ > + if (two_stage && first_stage) { > + int gpa_prot; > + hwaddr gpa_paddr; > + int gpa_ret = get_physical_address(env, &gpa_paddr, > &gpa_prot, > + pte_gpa, NULL, > MMU_DATA_STORE, > + MMUIdx_U, false, > true, > + is_debug, false); > + if (gpa_ret != TRANSLATE_SUCCESS) { > + if (fault_pte_addr) { > + *fault_pte_addr = pte_gpa >> 2; > + } > + return TRANSLATE_G_STAGE_FAIL; > + } > + } > + > pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, > sxlen_bytes, > MMU_DATA_STORE, PRV_S); > if (pmp_ret != TRANSLATE_SUCCESS) { > @@ -2345,6 +2369,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) > * doing VS-stage page table walk. > */ > tinst = (riscv_cpu_xlen(env) == 32) ? 0x00002000 : > 0x00003000; > + > + if (cause == > RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT) { > + tinst |= 0x20; > + } > } else { > /* > * The "Addr. Offset" field in transformed > instruction is
