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 | 35 +++++++++++++++++++-------- target/loongarch/cpu.h | 3 +++ target/loongarch/internals.h | 4 ++-- target/loongarch/tcg/csr_helper.c | 2 +- target/loongarch/tcg/tcg_cpu.c | 39 ++++++++++++++++++++++--------- 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 664763137d..5cef63a778 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -58,16 +58,20 @@ static vaddr loongarch_cpu_get_pc(CPUState *cs) #ifndef CONFIG_USER_ONLY #include "hw/loongarch/virt.h" -void loongarch_cpu_update_irq(LoongArchCPU *cpu) +void loongarch_cpu_update_irq(LoongArchCPU *cpu, int vm_level) { CPULoongArchState *env = &cpu->env; CPUState *cs = CPU(cpu); - CPUSysState *sys = env_sys(env); + CPUSysState *sys = get_sys(env, vm_level); + int interrupt = vm_level ? CPU_INTERRUPT_GUEST : CPU_INTERRUPT_HARD; + if (vm_level == VM_LEVEL1 && env_vm_level(env) != VM_LEVEL1) { + return; + } if (FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS)) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, interrupt); } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, interrupt); } } @@ -75,21 +79,28 @@ static void loongarch_cpu_self_set_irq(CPUState *cs, run_on_cpu_data data) { LoongArchCPU *cpu = LOONGARCH_CPU(cs); CPULoongArchState *env = cpu_env(cs); - CPUSysState *sys = env_sys(env); + CPUSysState *sys; + int vm_level = 0; int irq, level; irq = data.host_int & ~BIT(31); level = (data.host_int >> 31) & 1; + if (irq >= N_IRQS) { + irq -= N_IRQS; + vm_level = 1; + } + sys = get_sys(env, vm_level); sys->CSR_ESTAT = deposit64(sys->CSR_ESTAT, irq, 1, level != 0); - loongarch_cpu_update_irq(cpu); + loongarch_cpu_update_irq(cpu, vm_level); } void loongarch_cpu_set_irq(void *opaque, int irq, int level) { LoongArchCPU *cpu = opaque; CPUState *cs = CPU(cpu); + CPULoongArchState *env = cpu_env(cs); - if (irq < 0 || irq >= N_IRQS) { + if (irq < 0 || irq >= (cpu_has_lvz(env) ? N_VIRQS : N_IRQS)) { return; } @@ -103,11 +114,11 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level) } /* 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, int vm_level) { uint32_t pending; uint32_t status; - CPUSysState *sys = env_sys(env); + CPUSysState *sys = get_sys(env, vm_level); pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS); status = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE); @@ -122,7 +133,11 @@ 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), VM_LEVEL0)) { + has_work = true; + } + if (cpu_test_interrupt(cs, CPU_INTERRUPT_GUEST) && + cpu_loongarch_hw_interrupts_pending(cpu_env(cs), VM_LEVEL1)) { has_work = true; } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9eea1badaf..8939dbc748 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -20,6 +20,8 @@ #include "cpu-csr.h" #include "cpu-qom.h" +#define CPU_INTERRUPT_GUEST CPU_INTERRUPT_TGT_EXT_0 + #define FCSR0_M1 0x1f /* FCSR1 mask, Enables */ #define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */ #define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */ @@ -242,6 +244,7 @@ extern const char * const fregnames[32]; #define IRQ_TIMER 11 #define IRQ_IPI 12 #define INT_DMSI 14 +#define N_VIRQS (N_IRQS * 2) #define MAX_PERF_EVENTS 16 #define LOONGARCH_STLB 2048 /* 2048 STLB */ diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index ff956823fd..c11cefc4ee 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -31,7 +31,7 @@ void restore_fp_status(CPULoongArchState *env); #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_loongarch_cpu; -void loongarch_cpu_update_irq(LoongArchCPU *cpu); +void loongarch_cpu_update_irq(LoongArchCPU *cpu, int vm_level); void loongarch_cpu_set_irq(void *opaque, int irq, int level); void cpu_loongarch_timer_cb(void *opaque); @@ -39,7 +39,7 @@ uint64_t cpu_loongarch_get_timer_counter(CPUTimerState *timer); uint64_t cpu_loongarch_get_timer_ticks(CPUTimerState *timer); void cpu_loongarch_set_timer_config(CPUTimerState *timer, 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, int vm_level); #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 1a6919417f..ecfba63228 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -111,7 +111,7 @@ target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val) */ if (sys->CSR_ESTAT != old_v) { bql_lock(); - loongarch_cpu_update_irq(env_archcpu(env)); + loongarch_cpu_update_irq(env_archcpu(env), false); bql_unlock(); } diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c index b60d9e7cf8..101fe6fc7a 100644 --- a/target/loongarch/tcg/tcg_cpu.c +++ b/target/loongarch/tcg/tcg_cpu.c @@ -238,30 +238,47 @@ 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, int vm_level) { bool ret = 0; - CPUSysState *sys = env_sys(env); + CPUSysState *sys = get_sys(env, vm_level); + CPUSysState *host = get_sys(env, VM_LEVEL0); 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, VM_LEVEL0) && + cpu_loongarch_hw_interrupts_pending(env, VM_LEVEL0)) { + if (env_vm_level(env) != VM_LEVEL0) { + trigger_vm_exit(env); + } + has_interrupt = true; } + } else if (interrupt_request & CPU_INTERRUPT_GUEST) { + if (cpu_loongarch_hw_interrupts_enabled(env, VM_LEVEL1) && + cpu_loongarch_hw_interrupts_pending(env, VM_LEVEL1) && + env_vm_level(env) == VM_LEVEL1) { + 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
