@samitouri / QOSamiQemu / commits / 9bd90bddb7

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

Peter Maydell committed Mar 27, 2026 at 11:16 UTC 9bd90bddb79b6f531e451425d27f767ca8178b0b
2 files changed +85 -3
target/arm/tcg/gicv5-cpuif.c
+84 -3
@@ -170,6 +170,84 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain)
170 return best;
171 }
172
173 +static void cpu_interrupt_update(CPUARMState *env, int irqtype, bool new_state)
174 +{
175 + CPUState *cs = env_cpu(env);
176 +
177 + /*
178 + * OPT: calling cpu_interrupt() and cpu_reset_interrupt() has the
179 + * correct behaviour, but is not optimal for the case where we're
180 + * setting the interrupt line to the same level it already has.
181 + *
182 + * Clearing an already clear interrupt is free (it's just doing an
183 + * atomic AND operation). Signalling an already set interrupt is a
184 + * bit less ideal (it might unnecessarily kick the CPU).
185 + *
186 + * We could potentially use cpu_test_interrupt(), like
187 + * arm_cpu_update_{virq,vfiq,vinmi,vserr}, since we always hold
188 + * the BQL here; or perhaps there is an abstraction we could
189 + * provide in the core code that all these places could call.
190 + *
191 + * For now, this is simple and definitely correct.
192 + */
193 + if (new_state) {
194 + cpu_interrupt(cs, irqtype);
195 + } else {
196 + cpu_reset_interrupt(cs, irqtype);
197 + }
198 +}
199 +
200 +static void gicv5_update_irq_fiq(CPUARMState *env)
201 +{
202 + /*
203 + * Update whether we are signalling IRQ or FIQ based on the
204 + * current state of the CPU interface (and in particular on the
205 + * HPPI information from the IRS and for the PPIs for each
206 + * interrupt domain);
207 + *
208 + * The logic here for IRQ and FIQ is defined by rules R_QLGBG and
209 + * R_ZGHMN; whether to signal with superpriority is defined by
210 + * rule R_CSBDX.
211 + *
212 + * For the moment, we do not consider preemptive interrupts,
213 + * because these only occur when there is a HPPI of sufficient
214 + * priority for another interrupt domain, and we only support EL1
215 + * and the NonSecure interrupt domain currently.
216 + *
217 + * NB: when we handle more than just EL1 we will need to arrange
218 + * to call this function to re-evaluate the IRQ and FIQ state when
219 + * we change EL.
220 + */
221 + GICv5PendingIrq current_hppi;
222 + bool irq, fiq, superpriority;
223 +
224 + /*
225 + * We will never signal FIQ because FIQ is for preemptive
226 + * interrupts or for EL3 HPPIs.
227 + */
228 + fiq = false;
229 +
230 + /*
231 + * We signal IRQ when we are not signalling FIQ and there is a
232 + * HPPI of sufficient priority for the current domain. It has
233 + * Superpriority if its priority is 0 (in which case it is
234 + * CPU_INTERRUPT_NMI rather than CPU_INTERRUPT_HARD).
235 + */
236 + current_hppi = gic_hppi(env, gicv5_current_phys_domain(env));
237 + superpriority = current_hppi.prio == 0;
238 + irq = current_hppi.prio != PRIO_IDLE && !superpriority;
239 +
240 + /*
241 + * Unlike a GICv3 or GICv2, there is no external IRQ or FIQ line
242 + * to the CPU. Instead we directly signal the interrupt via
243 + * cpu_interrupt()/cpu_reset_interrupt().
244 + */
245 + trace_gicv5_update_irq_fiq(irq, fiq, superpriority);
246 + cpu_interrupt_update(env, CPU_INTERRUPT_HARD, irq);
247 + cpu_interrupt_update(env, CPU_INTERRUPT_FIQ, fiq);
248 + cpu_interrupt_update(env, CPU_INTERRUPT_NMI, superpriority);
249 +}
250 +
251 static void gic_recalc_ppi_hppi(CPUARMState *env)
252 {
253 /*
@@ -219,15 +297,16 @@ static void gic_recalc_ppi_hppi(CPUARMState *env)
297 env->gicv5_cpuif.ppi_hppi[i].intid,
298 env->gicv5_cpuif.ppi_hppi[i].prio);
299 }
300 + gicv5_update_irq_fiq(env);
301 }
302
303 void gicv5_forward_interrupt(ARMCPU *cpu, GICv5Domain domain)
304 {
305 /*
227 - * For now, we do nothing. Later we will recalculate the overall
228 - * HPPI by combining the IRS HPPI with the PPI HPPI, and possibly
229 - * signal IRQ/FIQ.
306 + * IRS HPPI has changed: recalculate the IRQ/FIQ levels by
307 + * combining the IRS HPPI with the PPI HPPI.
308 */
309 + gicv5_update_irq_fiq(&cpu->env);
310 }
311
312 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,
509 value |= R_ICC_CR0_LINK_MASK | R_ICC_CR0_LINK_IDLE_MASK;
510
511 env->gicv5_cpuif.icc_cr0[domain] = value;
512 + gicv5_update_irq_fiq(env);
513 }
514
515 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,
651
652 /* clear lowest bit, doing nothing if already zero */
653 *apr &= *apr - 1;
654 + gicv5_update_irq_fiq(env);
655 }
656
657 static void gic_cddi_write(CPUARMState *env, const ARMCPRegInfo *ri,
target/arm/tcg/trace-events
+1
@@ -7,3 +7,4 @@ gicv5_gicr_cdia_fail(int domain, const char *reason) "domain %d CDIA attempt fai
7 gicv5_gicr_cdia(int domain, uint32_t id) "domain %d CDIA acknowledge of interrupt 0x%x"
8 gicv5_cdeoi(int domain) "domain %d CDEOI performing priority drop"
9 gicv5_cddi(int domain, uint32_t id) "domain %d CDDI deactivating interrupt ID 0x%x"
10 +gicv5_update_irq_fiq(bool irq, bool fiq, bool nmi) "now IRQ %d FIQ %d NMI %d"