Commit 146696d9cd for qemu.org
commit 146696d9cdd170ee105ef2a7e16d95ea5bde806c
Author: Peter Maydell <peter.maydell@linaro.org>
Date: Tue Oct 13 09:38:14 2026 +0100
target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status
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.
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index c484c40a7e..0ae0d581be 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -812,9 +812,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[] = {
@@ -866,6 +865,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);
@@ -1265,7 +1273,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 509ed236a1..033d6d66da 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -1889,6 +1889,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 e847fc722b..e7b80b8375 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)