MTTCG lets interrupt inputs and timer callbacks update CP0_Cause,
CP0_Status, and CP0_Count while a vCPU checks interrupt state.

Use qatomic accessors for CP0 fields that participate in interrupt and
timer decisions, and kick remote CPUs explicitly after raising a hard
interrupt; MIPS interrupt line updates no longer depend on taking the
BQL.

Signed-off-by: Kirill A. Korinsky <[email protected]>
---
 hw/mips/mips_int.c                      | 17 +++++++------
 target/mips/internal.h                  | 13 ++++++----
 target/mips/system/cp0.c                | 27 +++++++++++++--------
 target/mips/system/cp0_timer.c          | 32 ++++++++++++++-----------
 target/mips/tcg/system/special_helper.c | 17 +++++++------
 target/mips/tcg/system/tlb_helper.c     | 21 +++++++++-------
 6 files changed, 75 insertions(+), 52 deletions(-)

diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c
index 26e7dd6d5f..537394c8fd 100644
--- a/hw/mips/mips_int.c
+++ b/hw/mips/mips_int.c
@@ -21,30 +21,33 @@
  */
 
 #include "qemu/osdep.h"
-#include "qemu/main-loop.h"
 #include "hw/core/irq.h"
 #include "target/mips/cpu.h"
+#include "target/mips/internal.h"
 
 static void cpu_mips_irq_request(void *opaque, int irq, int level)
 {
     MIPSCPU *cpu = opaque;
     CPUMIPSState *env = &cpu->env;
     CPUState *cs = CPU(cpu);
+    int32_t irq_bit;
 
     if (irq < 0 || irq > 7) {
         return;
     }
 
-    BQL_LOCK_GUARD();
-
+    irq_bit = 1 << (irq + CP0Ca_IP);
     if (level) {
-        env->CP0_Cause |= 1 << (irq + CP0Ca_IP);
+        qatomic_or(&env->CP0_Cause, irq_bit);
     } else {
-        env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP));
+        qatomic_and(&env->CP0_Cause, ~irq_bit);
     }
 
-    if (env->CP0_Cause & CP0Ca_IP_mask) {
-        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+    if (qatomic_read(&env->CP0_Cause) & CP0Ca_IP_mask) {
+        cpu_set_interrupt(cs, CPU_INTERRUPT_HARD);
+        if (!qemu_cpu_is_self(cs)) {
+            qemu_cpu_kick(cs);
+        }
     } else {
         cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
     }
diff --git a/target/mips/internal.h b/target/mips/internal.h
index c5c286872e..cd06d84140 100644
--- a/target/mips/internal.h
+++ b/target/mips/internal.h
@@ -8,6 +8,7 @@
 #ifndef MIPS_INTERNAL_H
 #define MIPS_INTERNAL_H
 
+#include "qemu/atomic.h"
 #include "exec/memattrs.h"
 #ifdef CONFIG_TCG
 #include "tcg/tcg-internal.h"
@@ -167,9 +168,11 @@ extern const VMStateDescription vmstate_mips_cpu;
 
 static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env)
 {
-    return (env->CP0_Status & (1 << CP0St_IE)) &&
-        !(env->CP0_Status & (1 << CP0St_EXL)) &&
-        !(env->CP0_Status & (1 << CP0St_ERL)) &&
+    int32_t status = qatomic_read(&env->CP0_Status);
+
+    return (status & (1 << CP0St_IE)) &&
+        !(status & (1 << CP0St_EXL)) &&
+        !(status & (1 << CP0St_ERL)) &&
         !(env->hflags & MIPS_HFLAG_DM) &&
         /*
          * Note that the TCStatus IXMT field is initialized to zero,
@@ -186,8 +189,8 @@ static inline bool 
cpu_mips_hw_interrupts_pending(CPUMIPSState *env)
     int32_t status;
     bool r;
 
-    pending = env->CP0_Cause & CP0Ca_IP_mask;
-    status = env->CP0_Status & CP0Ca_IP_mask;
+    pending = qatomic_read(&env->CP0_Cause) & CP0Ca_IP_mask;
+    status = qatomic_read(&env->CP0_Status) & CP0Ca_IP_mask;
 
     if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
         /*
diff --git a/target/mips/system/cp0.c b/target/mips/system/cp0.c
index ff7d3db00c..6f222ad586 100644
--- a/target/mips/system/cp0.c
+++ b/target/mips/system/cp0.c
@@ -22,6 +22,7 @@
 #include "cpu.h"
 #include "internal.h"
 #include "exec/cputlb.h"
+#include "qemu/atomic.h"
 
 /* Called for updates to CP0_Status.  */
 void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc)
@@ -61,7 +62,8 @@ void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int 
tc)
 void cpu_mips_store_status(CPUMIPSState *env, target_ulong val)
 {
     uint32_t mask = env->CP0_Status_rw_bitmask;
-    target_ulong old = env->CP0_Status;
+    target_ulong old;
+    target_ulong status;
 
     if (env->insn_flags & ISA_MIPS_R6) {
         bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3;
@@ -77,9 +79,11 @@ void cpu_mips_store_status(CPUMIPSState *env, target_ulong 
val)
         mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val);
     }
 
-    env->CP0_Status = (old & ~mask) | (val & mask);
+    old = qatomic_read(&env->CP0_Status);
+    status = (old & ~mask) | (val & mask);
+    qatomic_set(&env->CP0_Status, status);
 #if defined(TARGET_MIPS64)
-    if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) {
+    if ((status ^ old) & (old & (7 << CP0St_UX))) {
         /* Access to at least one of the 64-bit segments has been disabled */
         tlb_flush(env_cpu(env));
     }
@@ -93,8 +97,9 @@ void cpu_mips_store_status(CPUMIPSState *env, target_ulong 
val)
 
 void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val)
 {
-    uint32_t mask = 0x00C00300;
-    uint32_t old = env->CP0_Cause;
+    int32_t mask = 0x00C00300;
+    int32_t old;
+    int32_t cause;
     int i;
 
     if (env->insn_flags & ISA_MIPS_R2) {
@@ -104,10 +109,12 @@ void cpu_mips_store_cause(CPUMIPSState *env, target_ulong 
val)
         mask &= ~((1 << CP0Ca_WP) & val);
     }
 
-    env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask);
+    old = qatomic_fetch_and(&env->CP0_Cause, ~mask);
+    qatomic_or(&env->CP0_Cause, (int32_t)(val & mask));
+    cause = qatomic_read(&env->CP0_Cause);
 
-    if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) {
-        if (env->CP0_Cause & (1 << CP0Ca_DC)) {
+    if ((old ^ cause) & (1 << CP0Ca_DC)) {
+        if (cause & (1 << CP0Ca_DC)) {
             cpu_mips_stop_count(env);
         } else {
             cpu_mips_start_count(env);
@@ -116,8 +123,8 @@ void cpu_mips_store_cause(CPUMIPSState *env, target_ulong 
val)
 
     /* Set/reset software interrupts */
     for (i = 0 ; i < 2 ; i++) {
-        if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
-            cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+        if ((old ^ cause) & (1 << (CP0Ca_IP + i))) {
+            cpu_mips_soft_irq(env, i, cause & (1 << (CP0Ca_IP + i)));
         }
     }
 }
diff --git a/target/mips/system/cp0_timer.c b/target/mips/system/cp0_timer.c
index 6aed5d3af2..622fee357d 100644
--- a/target/mips/system/cp0_timer.c
+++ b/target/mips/system/cp0_timer.c
@@ -23,6 +23,7 @@
 #include "qemu/osdep.h"
 #include "hw/core/irq.h"
 #include "qemu/timer.h"
+#include "qemu/atomic.h"
 #include "internal.h"
 
 /* MIPS R4K timer */
@@ -31,7 +32,7 @@ static uint32_t cpu_mips_get_count_val(CPUMIPSState *env)
     MIPSCPU *cpu = env_archcpu(env);
     int64_t now_ns;
     now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    return env->CP0_Count +
+    return qatomic_read(&env->CP0_Count) +
             (uint32_t)clock_ns_to_ticks(cpu->count_clock, now_ns);
 }
 
@@ -56,15 +57,15 @@ static void cpu_mips_timer_expire(CPUMIPSState *env)
 {
     cpu_mips_timer_update(env);
     if (env->insn_flags & ISA_MIPS_R2) {
-        env->CP0_Cause |= 1 << CP0Ca_TI;
+        qatomic_or(&env->CP0_Cause, 1 << CP0Ca_TI);
     }
     qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
 }
 
 uint32_t cpu_mips_get_count(CPUMIPSState *env)
 {
-    if (env->CP0_Cause & (1 << CP0Ca_DC)) {
-        return env->CP0_Count;
+    if (qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC)) {
+        return qatomic_read(&env->CP0_Count);
     } else {
         uint64_t now_ns;
 
@@ -86,12 +87,13 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t count)
      * So env->timer may be NULL, so treat timer as disabled in that case.
      */
     MIPSCPU *cpu = env_archcpu(env);
-    if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer) {
-        env->CP0_Count = count;
+    if ((qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC)) || !env->timer) {
+        qatomic_set(&env->CP0_Count, count);
     } else {
         /* Store new count register */
-        env->CP0_Count = count - (uint32_t)clock_ns_to_ticks(cpu->count_clock,
-                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+        qatomic_set(&env->CP0_Count,
+                    count - (uint32_t)clock_ns_to_ticks(cpu->count_clock,
+                            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)));
         /* Update timer timer */
         cpu_mips_timer_update(env);
     }
@@ -100,26 +102,28 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t 
count)
 void cpu_mips_store_compare(CPUMIPSState *env, uint32_t value)
 {
     env->CP0_Compare = value;
-    if (!(env->CP0_Cause & (1 << CP0Ca_DC))) {
+    if (!(qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC))) {
         cpu_mips_timer_update(env);
     }
     if (env->insn_flags & ISA_MIPS_R2) {
-        env->CP0_Cause &= ~(1 << CP0Ca_TI);
+        qatomic_and(&env->CP0_Cause, ~(1 << CP0Ca_TI));
     }
     qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
 }
 
 void cpu_mips_start_count(CPUMIPSState *env)
 {
-    cpu_mips_store_count(env, env->CP0_Count);
+    cpu_mips_store_count(env, qatomic_read(&env->CP0_Count));
 }
 
 void cpu_mips_stop_count(CPUMIPSState *env)
 {
     /* Store the current value */
     MIPSCPU *cpu = env_archcpu(env);
-    env->CP0_Count += (uint32_t)clock_ns_to_ticks(cpu->count_clock,
-                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+
+    qatomic_add(&env->CP0_Count,
+                (uint32_t)clock_ns_to_ticks(cpu->count_clock,
+                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)));
 }
 
 static void mips_timer_cb(void *opaque)
@@ -128,7 +132,7 @@ static void mips_timer_cb(void *opaque)
 
     env = opaque;
 
-    if (env->CP0_Cause & (1 << CP0Ca_DC)) {
+    if (qatomic_read(&env->CP0_Cause) & (1 << CP0Ca_DC)) {
         return;
     }
 
diff --git a/target/mips/tcg/system/special_helper.c 
b/target/mips/tcg/system/special_helper.c
index b54cbe88a3..04ae0bc6c3 100644
--- a/target/mips/tcg/system/special_helper.c
+++ b/target/mips/tcg/system/special_helper.c
@@ -24,21 +24,22 @@
 #include "exec/helper-proto.h"
 #include "exec/translation-block.h"
 #include "internal.h"
+#include "qemu/atomic.h"
 
 /* Specials */
 target_ulong helper_di(CPUMIPSState *env)
 {
-    target_ulong t0 = env->CP0_Status;
+    target_ulong t0 = qatomic_read(&env->CP0_Status);
 
-    env->CP0_Status = t0 & ~(1 << CP0St_IE);
+    qatomic_set(&env->CP0_Status, t0 & ~(1 << CP0St_IE));
     return t0;
 }
 
 target_ulong helper_ei(CPUMIPSState *env)
 {
-    target_ulong t0 = env->CP0_Status;
+    target_ulong t0 = qatomic_read(&env->CP0_Status);
 
-    env->CP0_Status = t0 | (1 << CP0St_IE);
+    qatomic_set(&env->CP0_Status, t0 | (1 << CP0St_IE));
     return t0;
 }
 
@@ -103,13 +104,15 @@ bool mips_io_recompile_replay_branch(CPUState *cs, const 
TranslationBlock *tb)
 
 static inline void exception_return(CPUMIPSState *env)
 {
+    target_ulong status = qatomic_read(&env->CP0_Status);
+
     debug_pre_eret(env);
-    if (env->CP0_Status & (1 << CP0St_ERL)) {
+    if (status & (1 << CP0St_ERL)) {
         mips_env_set_pc(env, env->CP0_ErrorEPC);
-        env->CP0_Status &= ~(1 << CP0St_ERL);
+        qatomic_set(&env->CP0_Status, status & ~(1 << CP0St_ERL));
     } else {
         mips_env_set_pc(env, env->CP0_EPC);
-        env->CP0_Status &= ~(1 << CP0St_EXL);
+        qatomic_set(&env->CP0_Status, status & ~(1 << CP0St_EXL));
     }
     compute_hflags(env);
     debug_post_eret(env);
diff --git a/target/mips/tcg/system/tlb_helper.c 
b/target/mips/tcg/system/tlb_helper.c
index 4398c6f80b..9772df5d6c 100644
--- a/target/mips/tcg/system/tlb_helper.c
+++ b/target/mips/tcg/system/tlb_helper.c
@@ -1106,7 +1106,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
         env->hflags &= ~(MIPS_HFLAG_KSU);
         /* EJTAG probe trap enable is not implemented... */
         if (!(env->CP0_Status & (1 << CP0St_EXL))) {
-            env->CP0_Cause &= ~(1U << CP0Ca_BD);
+            qatomic_and(&env->CP0_Cause, (int32_t)~(1U << CP0Ca_BD));
         }
         env->active_tc.PC = env->exception_base + 0x480;
         set_hflags_for_handler(env);
@@ -1134,7 +1134,7 @@ void mips_cpu_do_interrupt(CPUState *cs)
         env->hflags |= MIPS_HFLAG_CP0;
         env->hflags &= ~(MIPS_HFLAG_KSU);
         if (!(env->CP0_Status & (1 << CP0St_EXL))) {
-            env->CP0_Cause &= ~(1U << CP0Ca_BD);
+            qatomic_and(&env->CP0_Cause, (int32_t)~(1U << CP0Ca_BD));
         }
         env->active_tc.PC = env->exception_base;
         set_hflags_for_handler(env);
@@ -1148,7 +1148,9 @@ void mips_cpu_do_interrupt(CPUState *cs)
                 offset = 0x200;
             } else {
                 uint32_t vector = 0;
-                uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 
CP0Ca_IP;
+                uint32_t pending =
+                    (qatomic_read(&env->CP0_Cause) & CP0Ca_IP_mask) >>
+                    CP0Ca_IP;
 
                 if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
                     /*
@@ -1246,8 +1248,9 @@ void mips_cpu_do_interrupt(CPUState *cs)
     case EXCP_CpU:
         cause = 11;
         update_badinstr = 1;
-        env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) |
-                         (env->error_code << CP0Ca_CE);
+        qatomic_and(&env->CP0_Cause, (int32_t)~(0x3U << CP0Ca_CE));
+        qatomic_or(&env->CP0_Cause,
+                   (int32_t)(env->error_code << CP0Ca_CE));
         goto set_EPC;
     case EXCP_OVERFLOW:
         cause = 12;
@@ -1305,9 +1308,9 @@ void mips_cpu_do_interrupt(CPUState *cs)
                 set_badinstr_registers(env);
             }
             if (env->hflags & MIPS_HFLAG_BMASK) {
-                env->CP0_Cause |= (1U << CP0Ca_BD);
+                qatomic_or(&env->CP0_Cause, (int32_t)(1U << CP0Ca_BD));
             } else {
-                env->CP0_Cause &= ~(1U << CP0Ca_BD);
+                qatomic_and(&env->CP0_Cause, (int32_t)~(1U << CP0Ca_BD));
             }
             env->CP0_Status |= (1 << CP0St_EXL);
             if (env->insn_flags & ISA_MIPS3) {
@@ -1333,8 +1336,8 @@ void mips_cpu_do_interrupt(CPUState *cs)
 
         env->active_tc.PC += offset;
         set_hflags_for_handler(env);
-        env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) |
-                         (cause << CP0Ca_EC);
+        qatomic_and(&env->CP0_Cause, (int32_t)~(0x1f << CP0Ca_EC));
+        qatomic_or(&env->CP0_Cause, cause << CP0Ca_EC);
         break;
     default:
         abort();
-- 
2.54.0


Reply via email to