On 2026/8/26 下午6:30, SignKirigami wrote:
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;
Is it ok to keep function loongarch_cpu_set_irq() unchanged? using irq
number to differentiate guest HW, value of irq of guest timer is defined
as IRQ_VTIMER, such as
#define IRQ_VTIMER (IRQ_TIMER + N_IRQS)
if (irq >= N_IRQS) {
irq -= N_IRQS;
guest = true;
}
The same for guest PMU/MSGINT HW in future.
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;
+ }
It is not necessary to check CSR_GINTC.
Regards
Bibo Mao
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;
}