gen_sc() uses a host cmpxchg against load_val, and AMO store events are never propagated to other harts' load_res reservations. An AMO by another hart that stores the same numeric value (amoadd +0, same-value amoswap, amoor 0, amoxor 0, amoand -1) therefore leaves the reservation intact and a later sc.w/sc.d succeeds, while native RISC-V hardware always fails the SC. The RISC-V A-extension requires SC to fail when a store to the reservation set from another hart can be observed between the LR and SC; a preserve-value AMO is still a real atomic store event.
Add a helper that walks all CPUs under RCU after a successful AMO store and clears overlapping reservations, and call it from gen_amo(). This makes the word/doubleword preserve-value screens match native behavior; a complete upstream fix should linearize reservation-set invalidation with the store (the patch is provided as a starting point). Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4149 Signed-off-by: wangyang <[email protected]> --- target/riscv/helper.h | 3 +++ target/riscv/tcg/op_helper.c | 25 +++++++++++++++++++++++++ target/riscv/tcg/translate.c | 2 ++ 3 files changed, 30 insertions(+) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 542b7c264fc..ed5b972e1b5 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1358,3 +1358,6 @@ DEF_HELPER_1(ssamoswap_disabled, void, env) /* Zalrsc SC write probe */ DEF_HELPER_FLAGS_3(sc_probe_write, TCG_CALL_NO_WG, void, env, tl, tl) + +/* Invalidate other harts' reservations after a successful AMO store. */ +DEF_HELPER_3(amo_invalidate_reservations, void, env, tl, tl) diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c index ba3c7da375a..11cac77e3fe 100644 --- a/target/riscv/tcg/op_helper.c +++ b/target/riscv/tcg/op_helper.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/rcu.h" #include "cpu.h" #include "target/riscv/tcg/csr.h" #include "internals.h" @@ -283,6 +284,30 @@ void helper_sc_probe_write(CPURISCVState *env, target_ulong addr, probe_write(env, addr, size, mmu_idx, ra); } +void helper_amo_invalidate_reservations(CPURISCVState *env, + target_ulong addr, + target_ulong size) +{ + CPUState *cpu; + + WITH_RCU_READ_LOCK_GUARD() { + CPU_FOREACH(cpu) { + CPURISCVState *other_env = cpu_env(cpu); + target_ulong reservation; + + if (other_env == env) { + continue; + } + + reservation = qatomic_read(&other_env->load_res); + if (reservation != (target_ulong)-1 && + reservation >= addr && reservation - addr < size) { + qatomic_set(&other_env->load_res, (target_ulong)-1); + } + } + } +} + #ifndef CONFIG_USER_ONLY target_ulong helper_sret(CPURISCVState *env) diff --git a/target/riscv/tcg/translate.c b/target/riscv/tcg/translate.c index 9684dbe7528..007a90f1e62 100644 --- a/target/riscv/tcg/translate.c +++ b/target/riscv/tcg/translate.c @@ -1159,6 +1159,8 @@ static bool gen_amo(DisasContext *ctx, arg_atomic *a, decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); src1 = get_address(ctx, a->rs1, 0); func(dest, src1, src2, ctx->mem_idx, mop); + gen_helper_amo_invalidate_reservations(tcg_env, src1, + tcg_constant_tl(memop_size(mop))); gen_set_gpr(ctx, a->rd, dest); return true; -- 2.43.0
