mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-08-26 22:23:12 -04:00
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:
parent
49f4c98648
commit
9bd90bddb7
2 changed files with 85 additions and 3 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue