Currently the GICv5 cpu interface directly calls cpu_interrupt() and cpu_reset_interrupt() to raise and lower IRQ and FIQ. This is almost but not exactly what we do for GICv2/GICv3 external GPIO IRQ/FIQ line changes. Those are handled by arm_cpu_set_irq(), and as well as calling cpu_interrupt() or cpu_reset_interrupt() they also update env->irq_line_state.
For IRQ and FIQ it doesn't matter whether we set and clear the bits in irq_line_state, because nothing looks at them. But for VIRQ and VFIQ it will matter, because VIRQ and VFIQ are the logical OR of the line state and some bits in HCR_EL2. We're going to need to call arm_cpu_set_irq() for VIRQ and VFIQ; for consistency, handle IRQ and FIQ that way too. Since arm_cpu_set_irq() is now a function called from other parts of the CPU code, we make its first argument a properly typed one, with a wrapper taking void* for when we need to use it as a qemu_irq_handler. Signed-off-by: Peter Maydell <[email protected]> --- target/arm/cpu.c | 14 +++++++++++--- target/arm/internals.h | 14 ++++++++++++++ target/arm/tcg/gicv5-cpuif.c | 35 ++++------------------------------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 77aa78f00e2..2b1b87b0ed4 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -806,9 +806,8 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) #ifndef CONFIG_USER_ONLY -static void arm_cpu_set_irq(void *opaque, int irq, int level) +void arm_cpu_set_irq(ARMCPU *cpu, int irq, int level) { - ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); static const int mask[] = { @@ -860,6 +859,15 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) } } +/* + * Wrapper of arm_cpu_set_irq with the right argument types to be a + * qemu_irq_handler function for our inbound GPIO lines. + */ +static void arm_cpu_gpio_set_irq(void *opaque, int irq, int level) +{ + arm_cpu_set_irq(opaque, irq, level); +} + static bool arm_cpu_internal_is_big_endian(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -1263,7 +1271,7 @@ static void arm_cpu_initfn(Object *obj) */ qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 6); } else { - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 6); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_gpio_set_irq, 6); } qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, diff --git a/target/arm/internals.h b/target/arm/internals.h index f6b4e173a54..f6c9f984952 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1882,6 +1882,20 @@ void define_gcs_cpregs(ARMCPU *cpu); /* Add the cpreg definitions for OMAP CP15 regs */ void define_omap_cp_regs(ARMCPU *cpu); +#ifndef CONFIG_USER_ONLY +/** + * arm_cpu_set_irq: Assert or clear an IRQ/FIQ/etc interrupt line + * @cpu: CPU to set interrupt on + * @irq: one of the ARM_CPU_IRQ/ARM_CPU_FIQ/ARM_CPU_VIRQ/etc values + * @level: 0 to deassert, non-zero to assert + * + * This function is called by the qemu_irq_handler function + * when IRQ etc are real inbound GPIO lines; it is also called + * directly by the GICv5 CPU interface. + */ +void arm_cpu_set_irq(ARMCPU *cpu, int irq, int level); +#endif + /* Add the cpreg definitions for the GICv5 CPU interface */ void define_gicv5_cpuif_regs(ARMCPU *cpu); diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index a6e6c440849..517c780d1f7 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -173,33 +173,6 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain) return best; } -static void cpu_interrupt_update(CPUARMState *env, int irqtype, bool new_state) -{ - CPUState *cs = env_cpu(env); - - /* - * OPT: calling cpu_interrupt() and cpu_reset_interrupt() has the - * correct behaviour, but is not optimal for the case where we're - * setting the interrupt line to the same level it already has. - * - * Clearing an already clear interrupt is free (it's just doing an - * atomic AND operation). Signalling an already set interrupt is a - * bit less ideal (it might unnecessarily kick the CPU). - * - * We could potentially use cpu_test_interrupt(), like - * arm_cpu_update_{virq,vfiq,vinmi,vserr}, since we always hold - * the BQL here; or perhaps there is an abstraction we could - * provide in the core code that all these places could call. - * - * For now, this is simple and definitely correct. - */ - if (new_state) { - cpu_interrupt(cs, irqtype); - } else { - cpu_reset_interrupt(cs, irqtype); - } -} - static void gicv5_update_irq_fiq(CPUARMState *env) { /* @@ -243,12 +216,12 @@ static void gicv5_update_irq_fiq(CPUARMState *env) /* * Unlike a GICv3 or GICv2, there is no external IRQ or FIQ line * to the CPU. Instead we directly signal the interrupt via - * cpu_interrupt()/cpu_reset_interrupt(). + * arm_cpu_set_irq(). */ trace_gicv5_update_irq_fiq(irq, fiq, superpriority); - cpu_interrupt_update(env, CPU_INTERRUPT_HARD, irq); - cpu_interrupt_update(env, CPU_INTERRUPT_FIQ, fiq); - cpu_interrupt_update(env, CPU_INTERRUPT_NMI, superpriority); + arm_cpu_set_irq(env_archcpu(env), ARM_CPU_IRQ, irq); + arm_cpu_set_irq(env_archcpu(env), ARM_CPU_FIQ, fiq); + arm_cpu_set_irq(env_archcpu(env), ARM_CPU_NMI, superpriority); } static void gic_recalc_ppi_hppi(CPUARMState *env) -- 2.43.0
