@samitouri / QOSamiQemu / commits / dd9641cb14

target/loongarch: Fix SWI interrupt delivery via CSR_ESTAT

In TCG mode, helper_csrwr_estat() updates CSR.ESTAT.IS[1:0] (SWI0/SWI1) when the guest writes CSR_ESTAT, but it did not update the CPU interrupt request state. As a result, software interrupts could be observed as pending in CSR.ESTAT while no interrupt exception was taken. Update CPU_INTERRUPT_HARD after modifying CSR_ESTAT, matching the behavior of loongarch_cpu_set_irq(). The helper runs without the Big QEMU Lock (BQL), so take the BQL while calling cpu_interrupt(). Fixes: 5b1dedfe848b ("target/loongarch: Add LoongArch CSR instruction") Reported-by: Andrew S. Rightenburg <andrew@rail5.org> Signed-off-by: Andrew S. Rightenburg <andrew@rail5.org> Signed-off-by: Bibo Mao <maobibo@loongson.cn> Reviewed-by: Song Gao <17746591750@163.com> Message-ID: <20260806120315.1059413-1-maobibo@loongson.cn> Signed-off-by: Song Gao <gaosong@loongson.cn>

Bibo Mao committed Aug 6, 2026 at 20:03 UTC dd9641cb140d6e2c297f8d87c7875409e42319b2
3 files changed +31 -6
target/loongarch/cpu.c
+20 -6
@@ -58,12 +58,29 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs)
58 #ifndef CONFIG_USER_ONLY
59 #include "hw/loongarch/virt.h"
60
61 +void loongarch_cpu_update_irq(LoongArchCPU *cpu, uint64_t old)
62 +{
63 + CPULoongArchState *env = &cpu->env;
64 + CPUState *cs = CPU(cpu);
65 + CPUSysState *sys = env_sys(env);
66 +
67 + if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
68 + if (!FIELD_EX64(old, CSR_ESTAT, IS)) {
69 + cpu_interrupt(cs, CPU_INTERRUPT_HARD);
70 + }
71 + } else {
72 + if (FIELD_EX64(old, CSR_ESTAT, IS)) {
73 + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
74 + }
75 + }
76 +}
77 +
78 void loongarch_cpu_set_irq(void *opaque, int irq, int level)
79 {
80 LoongArchCPU *cpu = opaque;
81 CPULoongArchState *env = &cpu->env;
65 - CPUState *cs = CPU(cpu);
82 CPUSysState *sys = env_sys(env);
83 + uint64_t old;
84
85 if (irq < 0 || irq >= N_IRQS) {
86 return;
@@ -72,12 +89,9 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level)
89 if (kvm_enabled()) {
90 kvm_loongarch_set_interrupt(cpu, irq, level);
91 } else if (tcg_enabled()) {
92 + old = sys->CSR_ESTAT;
93 sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0);
76 - if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
77 - cpu_interrupt(cs, CPU_INTERRUPT_HARD);
78 - } else {
79 - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
80 - }
94 + loongarch_cpu_update_irq(cpu, old);
95 }
96 }
97
target/loongarch/internals.h
+1
@@ -31,6 +31,7 @@ void restore_fp_status(CPULoongArchState *env);
31 #ifndef CONFIG_USER_ONLY
32 extern const VMStateDescription vmstate_loongarch_cpu;
33
34 +void loongarch_cpu_update_irq(LoongArchCPU *cpu, uint64_t old);
35 void loongarch_cpu_set_irq(void *opaque, int irq, int level);
36
37 void loongarch_constant_timer_cb(void *opaque);
target/loongarch/tcg/csr_helper.c
+10
@@ -106,6 +106,16 @@ target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val)
106
107 /* Only IS[1:0] can be written */
108 sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, 0, 2, val);
109 + /*
110 + * Software interrupts (SWI0/SWI1) are latched in CSR.ESTAT.IS[1:0].
111 + * Make sure the CPU interrupt request state tracks the pending bits,
112 + * matching the behavior of loongarch_cpu_set_irq().
113 + */
114 + if (sys->CSR_ESTAT != old_v) {
115 + bql_lock();
116 + loongarch_cpu_update_irq(env_archcpu(env), old_v);
117 + bql_unlock();
118 + }
119
120 return old_v;
121 }