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
