target/arm: GICv5 cpuif: Signal IRQ or FIQ

The CPU interface must signal IRQ or FIQ (possibly with
superpriority) when there is a pending interrupt of sufficient
priority available.  Implement this logic.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Message-id: 20260327111700.795099-53-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2026-03-27 11:16:47 +00:00
parent 49f4c98648
commit 9bd90bddb7
2 changed files with 85 additions and 3 deletions

View file

@ -170,6 +170,84 @@ 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)
{
/*
* Update whether we are signalling IRQ or FIQ based on the
* current state of the CPU interface (and in particular on the
* HPPI information from the IRS and for the PPIs for each
* interrupt domain);
*
* The logic here for IRQ and FIQ is defined by rules R_QLGBG and
* R_ZGHMN; whether to signal with superpriority is defined by
* rule R_CSBDX.
*
* For the moment, we do not consider preemptive interrupts,
* because these only occur when there is a HPPI of sufficient
* priority for another interrupt domain, and we only support EL1
* and the NonSecure interrupt domain currently.
*
* NB: when we handle more than just EL1 we will need to arrange
* to call this function to re-evaluate the IRQ and FIQ state when
* we change EL.
*/
GICv5PendingIrq current_hppi;
bool irq, fiq, superpriority;
/*
* We will never signal FIQ because FIQ is for preemptive
* interrupts or for EL3 HPPIs.
*/
fiq = false;
/*
* We signal IRQ when we are not signalling FIQ and there is a
* HPPI of sufficient priority for the current domain. It has
* Superpriority if its priority is 0 (in which case it is
* CPU_INTERRUPT_NMI rather than CPU_INTERRUPT_HARD).
*/
current_hppi = gic_hppi(env, gicv5_current_phys_domain(env));
superpriority = current_hppi.prio == 0;
irq = current_hppi.prio != PRIO_IDLE && !superpriority;
/*
* 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().
*/
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);
}
static void gic_recalc_ppi_hppi(CPUARMState *env)
{
/*
@ -219,15 +297,16 @@ static void gic_recalc_ppi_hppi(CPUARMState *env)
env->gicv5_cpuif.ppi_hppi[i].intid,
env->gicv5_cpuif.ppi_hppi[i].prio);
}
gicv5_update_irq_fiq(env);
}
void gicv5_forward_interrupt(ARMCPU *cpu, GICv5Domain domain)
{
/*
* For now, we do nothing. Later we will recalculate the overall
* HPPI by combining the IRS HPPI with the PPI HPPI, and possibly
* signal IRQ/FIQ.
* IRS HPPI has changed: recalculate the IRQ/FIQ levels by
* combining the IRS HPPI with the PPI HPPI.
*/
gicv5_update_irq_fiq(&cpu->env);
}
static void gic_cddis_write(CPUARMState *env, const ARMCPRegInfo *ri,
@ -430,6 +509,7 @@ static void gic_icc_cr0_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
value |= R_ICC_CR0_LINK_MASK | R_ICC_CR0_LINK_IDLE_MASK;
env->gicv5_cpuif.icc_cr0[domain] = value;
gicv5_update_irq_fiq(env);
}
static void gic_icc_cr0_el1_reset(CPUARMState *env, const ARMCPRegInfo *ri)
@ -571,6 +651,7 @@ static void gic_cdeoi_write(CPUARMState *env, const ARMCPRegInfo *ri,
/* clear lowest bit, doing nothing if already zero */
*apr &= *apr - 1;
gicv5_update_irq_fiq(env);
}
static void gic_cddi_write(CPUARMState *env, const ARMCPRegInfo *ri,

View file

@ -7,3 +7,4 @@ gicv5_gicr_cdia_fail(int domain, const char *reason) "domain %d CDIA attempt fai
gicv5_gicr_cdia(int domain, uint32_t id) "domain %d CDIA acknowledge of interrupt 0x%x"
gicv5_cdeoi(int domain) "domain %d CDEOI performing priority drop"
gicv5_cddi(int domain, uint32_t id) "domain %d CDDI deactivating interrupt ID 0x%x"
gicv5_update_irq_fiq(bool irq, bool fiq, bool nmi) "now IRQ %d FIQ %d NMI %d"