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"