helper_cbo_zero() aligns the address down to the cache-block size before probe_write(), and accel/tcg/user-exec.c:probe_access_internal() reports that aligned address to cpu_loop_exit_sigsegv(), so a store fault from a legal cbo.zero (e.g. on a read-only page) reports the aligned block base as si_addr instead of the rs1 value supplied by the program. Native RISC-V Linux reports the exact rs1 address. Keep the original address and use it when the fault is reported.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4151 Signed-off-by: wangyang <[email protected]> --- accel/tcg/user-exec.c | 7 ++++++- include/hw/core/cpu.h | 2 ++ target/riscv/tcg/cpu_helper.c | 21 ++++++++++++++++++--- target/riscv/tcg/op_helper.c | 5 +++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index a35aca78893..13c38458111 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -787,7 +787,12 @@ static int probe_access_internal(CPUArchState *env, vaddr addr, return TLB_INVALID_MASK; } - cpu_loop_exit_sigsegv(env_cpu(env), addr, access_type, maperr, ra); + CPUState *cpu = env_cpu(env); + if (cpu->exception_addr_valid) { + addr = cpu->exception_addr; + cpu->exception_addr_valid = false; + } + cpu_loop_exit_sigsegv(cpu, addr, access_type, maperr, ra); } int probe_access_flags(CPUArchState *env, vaddr addr, int size, diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index b54035fb13b..2799d3d6701 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -560,6 +560,8 @@ struct CPUState { uint32_t tcg_cflags; uint32_t halted; int32_t exception_index; + vaddr exception_addr; + bool exception_addr_valid; bool vcpu_dirty; AccelCPUState *accel; diff --git a/target/riscv/tcg/cpu_helper.c b/target/riscv/tcg/cpu_helper.c index 07d92226527..73db04545e1 100644 --- a/target/riscv/tcg/cpu_helper.c +++ b/target/riscv/tcg/cpu_helper.c @@ -1537,7 +1537,12 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, default: g_assert_not_reached(); } - env->badaddr = address; + if (cs->exception_addr_valid) { + env->badaddr = cs->exception_addr; + cs->exception_addr_valid = false; + } else { + env->badaddr = address; + } env->two_stage_lookup = two_stage; env->two_stage_indirect_lookup = two_stage_indirect; } @@ -1588,7 +1593,12 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT; } - env->badaddr = addr; + if (cs->exception_addr_valid) { + env->badaddr = cs->exception_addr; + cs->exception_addr_valid = false; + } else { + env->badaddr = addr; + } env->two_stage_lookup = mmuidx_2stage(mmu_idx); env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); @@ -1621,7 +1631,12 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, default: g_assert_not_reached(); } - env->badaddr = addr; + if (cs->exception_addr_valid) { + env->badaddr = cs->exception_addr; + cs->exception_addr_valid = false; + } else { + env->badaddr = addr; + } env->two_stage_lookup = mmuidx_2stage(mmu_idx); env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c index 11cac77e3fe..7f1107905f5 100644 --- a/target/riscv/tcg/op_helper.c +++ b/target/riscv/tcg/op_helper.c @@ -171,9 +171,13 @@ void helper_cbo_zero(CPURISCVState *env, target_ulong address) int mmu_idx = riscv_env_mmu_index(env, false); uintptr_t ra = GETPC(); void *mem; + CPUState *cs = env_cpu(env); check_zicbo_envcfg(env, MENVCFG_CBZE, ra); + cs->exception_addr = address; + cs->exception_addr_valid = true; + /* Mask off low-bits to align-down to the cache-block. */ address &= ~(cbozlen - 1); @@ -201,6 +205,7 @@ void helper_cbo_zero(CPURISCVState *env, target_ulong address) cpu_stb_mmuidx_ra(env, address + i, 0, mmu_idx, ra); } } + cs->exception_addr_valid = false; } /* -- 2.43.0
