Moving from Exclusive to Open Access should generate event stream events. Technically a non-exclusive store to any address range covered by the global monitor should also trigger such an event but we can only detect that after the event by seeing if memory doesn't match cpu_exclusive_val when processing the eventual store exclusive. The CLREX instruction has the same effect as do other operations clearing the exclusive state (such as eret). We special case STLR/STL (Store Release) instructions to generate events because their use is a suggested pattern for clearing locks that might be sleeping. We only trigger the event if we detect an exclusive instruction is running. Signed-off-by: Alex Bennée --- v2 - add gen_global_event_reg() and use that - add handling for STL/STLR --- target/arm/internals.h | 1 + target/arm/tcg/translate.h | 14 ++++++++++++++ target/arm/tcg/translate-a64.c | 23 +++++++++++++++++++++++ target/arm/tcg/translate.c | 20 ++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/target/arm/internals.h b/target/arm/internals.h index 089f679ac0a..7045b4a56bd 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -709,6 +709,7 @@ static inline void arm_broadcast_event(void) static inline void arm_clear_exclusive(CPUARMState *env) { env->exclusive_addr = -1; + arm_broadcast_event(); } /** diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 9bf2701a56b..01053060af4 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -882,6 +882,20 @@ static inline void gen_event_reg(void) #endif } +/* + * Some events affect all PEs in the same shareability domain. In + * practice as we currently model SMP systems as single SoC devices so + * we signal them all. + */ +static inline void gen_global_event_reg(void) +{ +#ifndef CONFIG_USER_ONLY + /* re-use the SEV helper */ + gen_helper_sev(tcg_env); +#endif +} + + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index f30df5dbfed..a1b0cc9508e 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -2253,6 +2253,7 @@ static bool trans_CHKFEAT(DisasContext *s, arg_CHKFEAT *a) static bool trans_CLREX(DisasContext *s, arg_CLREX *a) { tcg_gen_movi_i64(cpu_exclusive_addr, -1); + gen_global_event_reg(); return true; } @@ -3407,6 +3408,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); } tcg_gen_mov_i64(cpu_reg(s, rd), tmp); + + /* + * On a successful StoreExcl the global monitor transitions from + * Exclusive to Open Access and at that point generate an Event + * for PEs in the same memory sharing domain. + */ + gen_global_event_reg(); + tcg_gen_br(done_label); gen_set_label(fail_label); @@ -3544,6 +3553,7 @@ static bool trans_STLR(DisasContext *s, arg_stlr *a) TCGv_i64 clean_addr; MemOp memop; bool iss_sf = ldst_iss_sf(a->sz, false, false); + TCGLabel *skip_monitor_event = gen_new_label(); /* * StoreLORelease is the same as Store-Release for QEMU, but @@ -3562,6 +3572,19 @@ static bool trans_STLR(DisasContext *s, arg_stlr *a) true, a->rn != 31, memop); do_gpr_st(s, cpu_reg(s, a->rt), clean_addr, memop, true, a->rt, iss_sf, a->lasr); + + /* + * We don't fully model the global monitor as it would be very + * expensive for every memory access. However in the Arm ARM "Use + * of Wait for Event (WFE) and Send Event (SEV) with lock" it does + * give the example of using STLR to clear a lock. So if a lock is + * active trigger the global event register so we don't deadlock + * while sleeping. + */ + tcg_gen_brcondi_i64(TCG_COND_EQ, cpu_exclusive_addr, -1, skip_monitor_event); + gen_global_event_reg(); + gen_set_label(skip_monitor_event); + return true; } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 59925151fc3..18d64620e40 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -2116,6 +2116,12 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t2); } tcg_gen_mov_i32(cpu_R[rd], t0); + /* + * On a successful StoreExcl the global monitor transitions from + * Exclusive to Open Access and at that point generate an Event + * for PEs in the same memory sharing domain. + */ + gen_global_event_reg(); tcg_gen_br(done_label); gen_set_label(fail_label); @@ -4218,6 +4224,7 @@ static bool trans_STLEXH(DisasContext *s, arg_STREX *a) static bool op_stl(DisasContext *s, arg_STL *a, MemOp mop) { TCGv_i32 addr, tmp; + TCGLabel *skip_monitor_event; if (!ENABLE_ARCH_8) { return false; @@ -4230,10 +4237,23 @@ static bool op_stl(DisasContext *s, arg_STL *a, MemOp mop) addr = load_reg(s, a->rn); tmp = load_reg(s, a->rt); + skip_monitor_event = gen_new_label(); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), mop | MO_ALIGN); disas_set_da_iss(s, mop, a->rt | ISSIsAcqRel | ISSIsWrite); + /* + * We don't fully model the global monitor as it would be very + * expensive for every memory access. However in the Arm ARM "Use + * of Wait for Event (WFE) and Send Event (SEV) with lock" it does + * give the example of using STL to clear a lock. So if a lock is + * active trigger the global event register so we don't deadlock + * while sleeping. + */ + tcg_gen_brcondi_i64(TCG_COND_EQ, cpu_exclusive_addr, -1, skip_monitor_event); + gen_global_event_reg(); + gen_set_label(skip_monitor_event); + return true; } -- 2.47.3