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.
---
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