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

Reply via email to