Route configured hardware interrupts into the guest CSR bank, track guest
interrupt requests separately, and expose guest pending and enable checks
to the TCG runtime.

Signed-off-by: SignKirigami <[email protected]>
Signed-off-by: Hengyu Yu <[email protected]>
---
 target/loongarch/cpu.c         | 43 +++++++++++++++++++++++++---------
 target/loongarch/internals.h   |  4 +++-
 target/loongarch/tcg/tcg_cpu.c | 36 +++++++++++++++++++---------
 3 files changed, 60 insertions(+), 23 deletions(-)

diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index c5c7f3e0e3..b12e8944de 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -57,41 +57,57 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs)
 #ifndef CONFIG_USER_ONLY
 #include "hw/loongarch/virt.h"
 
-void loongarch_cpu_set_irq(void *opaque, int irq, int level)
+void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
+                                bool guest)
 {
     LoongArchCPU *cpu = opaque;
     CPULoongArchState *env = &cpu->env;
     CPUState *cs = CPU(cpu);
-    CPUSysState *sys = env_sys(env);
+    CPUSysState *sys = get_sys(env, guest);
+    int interrupt = guest ? CPU_INTERRUPT_GUEST : CPU_INTERRUPT_HARD;
 
     if (irq < 0 || irq >= N_IRQS) {
         return;
     }
 
-    if (kvm_enabled()) {
+    if (!guest && kvm_enabled()) {
         kvm_loongarch_set_interrupt(cpu, irq, level);
     } else if (tcg_enabled()) {
+        if (!guest && irq >= 2 && irq < 10 &&
+            (FIELD_EX64(sys->CSR_GINTC, CSR_GINTC, HWIP) &
+             BIT(irq - 2))) {
+            loongarch_cpu_set_irq_impl(opaque, irq, level, true);
+            return;
+        }
         sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0);
-        if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
-            cpu_interrupt(cs, CPU_INTERRUPT_HARD);
-        } else {
-            cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+        if (!guest || env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+            if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) {
+                cpu_interrupt(cs, interrupt);
+            } else {
+                cpu_reset_interrupt(cs, interrupt);
+            }
         }
     }
 }
 
+void loongarch_cpu_set_irq(void *opaque, int irq, int level)
+{
+    loongarch_cpu_set_irq_impl(opaque, irq, level, false);
+}
+
 /* Check if there is pending and not masked out interrupt */
-bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
+bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest)
 {
     uint32_t pending;
     uint32_t status;
-    CPUSysState *sys = env_sys(env);
+    CPUSysState *sys = get_sys(env, guest);
 
     pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS);
-    status  = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
+    status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
 
     return (pending & status) != 0;
 }
+
 #endif
 
 #ifndef CONFIG_USER_ONLY
@@ -100,7 +116,12 @@ bool loongarch_cpu_has_work(CPUState *cs)
     bool has_work = false;
 
     if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) &&
-        cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) {
+        cpu_loongarch_hw_interrupts_pending(cpu_env(cs), false)) {
+        has_work = true;
+    }
+
+    if (cpu_test_interrupt(cs, CPU_INTERRUPT_GUEST) &&
+        cpu_loongarch_hw_interrupts_pending(cpu_env(cs), true)) {
         has_work = true;
     }
 
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index e01dbed40f..a23b0d3d58 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -32,6 +32,8 @@ void restore_fp_status(CPULoongArchState *env);
 extern const VMStateDescription vmstate_loongarch_cpu;
 
 void loongarch_cpu_set_irq(void *opaque, int irq, int level);
+void loongarch_cpu_set_irq_impl(void *opaque, int irq, int level,
+                                bool guest);
 
 void loongarch_constant_timer_cb(void *opaque);
 uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
@@ -39,7 +41,7 @@ uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU 
*cpu);
 void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
                                                uint64_t value);
 bool loongarch_cpu_has_work(CPUState *cs);
-bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env);
+bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env, bool guest);
 #endif /* !CONFIG_USER_ONLY */
 
 uint64_t read_fcc(CPULoongArchState *env);
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 4b1d44a164..e84ef5f305 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -238,29 +238,43 @@ static void loongarch_cpu_do_transaction_failed(CPUState 
*cs, hwaddr physaddr,
     }
 }
 
-static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
+static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env, 
bool guest)
 {
     bool ret = 0;
-    CPUSysState *sys = env_sys(env);
+    CPUSysState *sys = get_sys(env, guest);
+    CPUSysState *host = get_sys(env, LOONGARCH_VM_LEVEL_HOST);
 
     ret = (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE) &&
-          !(FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)));
+          !(FIELD_EX64(host->CSR_DBG, CSR_DBG, DST)));
 
     return ret;
 }
 
 static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
+    CPULoongArchState *env = cpu_env(cs);
+    bool has_interrupt = false;
+
     if (interrupt_request & CPU_INTERRUPT_HARD) {
-        CPULoongArchState *env = cpu_env(cs);
-
-        if (cpu_loongarch_hw_interrupts_enabled(env) &&
-            cpu_loongarch_hw_interrupts_pending(env)) {
-            /* Raise it */
-            cs->exception_index = EXCCODE_INT;
-            loongarch_cpu_do_interrupt(cs);
-            return true;
+        if (cpu_loongarch_hw_interrupts_enabled(env, false) &&
+            cpu_loongarch_hw_interrupts_pending(env, false)) {
+            if (env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+                trigger_vm_exit(env);
+            }
+            has_interrupt = true;
         }
+    } else if (interrupt_request & CPU_INTERRUPT_GUEST) {
+        if (cpu_loongarch_hw_interrupts_enabled(env, true) &&
+            cpu_loongarch_hw_interrupts_pending(env, true) &&
+            env_vm_level(env) == LOONGARCH_VM_LEVEL_GUEST) {
+            has_interrupt = true;
+        }
+    }
+    if (has_interrupt) {
+        /* Raise it */
+        cs->exception_index = EXCCODE_INT;
+        loongarch_cpu_do_interrupt(cs);
+        return true;
     }
     return false;
 }
-- 
2.55.0


Reply via email to