@samitouri / QOSamiQemu / commits / c2f85a1641

target/arm: Connect internal interrupt sources up as GICv5 PPIs

The CPU has several interrupt sources which are exposed as GICv5 PPIs. For QEMU, this means the generic timers and the PMU. In GICv3, we implemented these as qemu_irq lines which connect up to the external interrupt controller device. In a GICv5, the PPIs are handled entirely inside the CPU interface, so there are no external signals. Instead we provide a gicv5_update_ppi_state() function which the emulated timer and PMU code uses to tell the CPU interface about the new state of the PPI source. We make the GICv5 function a no-op if there is no GICv5 present, so that calling code can do both "update the old irq lines" and "update the GICv5 PPI" without having to add conditionals. (In a GICv5 system the old irq lines won't be connected to anything, so the qemu_set_irq() will be a no-op.) Updating PPIs via either mechanism is unnecessary in user-only mode; we got away with not ifdeffing this away before because qemu_set_irq() is built for user-only mode, but since the GICv5 cpuif code is system-emulation only, we do need an ifdef now. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Message-id: 20260327111700.795099-54-peter.maydell@linaro.org

Peter Maydell committed May 7, 2026 at 15:14 UTC c2f85a1641a88fbbf971c106aa02ae04fc0d8530
6 files changed +66 -2
target/arm/cpregs-pmu.c
+7 -2
@@ -428,9 +428,14 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
428
429 static void pmu_update_irq(CPUARMState *env)
430 {
431 +#ifndef CONFIG_USER_ONLY
432 ARMCPU *cpu = env_archcpu(env);
432 - qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) &&
433 - (env->cp15.c9_pminten & env->cp15.c9_pmovsr));
433 + bool level = (env->cp15.c9_pmcr & PMCRE) &&
434 + (env->cp15.c9_pminten & env->cp15.c9_pmovsr);
435 +
436 + gicv5_update_ppi_state(env, GICV5_PPI_PMUIRQ, level);
437 + qemu_set_irq(cpu->pmu_interrupt, level);
438 +#endif
439 }
440
441 static bool pmccntr_clockdiv_enabled(CPUARMState *env)
target/arm/helper.c
+20
@@ -1343,6 +1343,21 @@ uint64_t gt_get_countervalue(CPUARMState *env)
1343 return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(cpu);
1344 }
1345
1346 +static void gt_update_gicv5_ppi(CPUARMState *env, int timeridx, bool level)
1347 +{
1348 + static int timeridx_to_ppi[] = {
1349 + [GTIMER_PHYS] = GICV5_PPI_CNTP,
1350 + [GTIMER_VIRT] = GICV5_PPI_CNTV,
1351 + [GTIMER_HYP] = GICV5_PPI_CNTHP,
1352 + [GTIMER_SEC] = GICV5_PPI_CNTPS,
1353 + [GTIMER_HYPVIRT] = GICV5_PPI_CNTHV,
1354 + [GTIMER_S_EL2_PHYS] = GICV5_PPI_CNTHPS,
1355 + [GTIMER_S_EL2_VIRT] = GICV5_PPI_CNTHVS,
1356 + };
1357 +
1358 + gicv5_update_ppi_state(env, timeridx_to_ppi[timeridx], level);
1359 +}
1360 +
1361 static void gt_update_irq(ARMCPU *cpu, int timeridx)
1362 {
1363 CPUARMState *env = &cpu->env;
@@ -1361,6 +1376,11 @@ static void gt_update_irq(ARMCPU *cpu, int timeridx)
1376 irqstate = 0;
1377 }
1378
1379 + /*
1380 + * We update both the GICv5 PPI and the external-GIC irq line
1381 + * (whichever of the two mechanisms is unused will do nothing)
1382 + */
1383 + gt_update_gicv5_ppi(env, timeridx, irqstate);
1384 qemu_set_irq(cpu->gt_timer_outputs[timeridx], irqstate);
1385 trace_arm_gt_update_irq(timeridx, irqstate);
1386 }
target/arm/internals.h
+6
@@ -1813,6 +1813,12 @@ void define_omap_cp_regs(ARMCPU *cpu);
1813 /* Add the cpreg definitions for the GICv5 CPU interface */
1814 void define_gicv5_cpuif_regs(ARMCPU *cpu);
1815
1816 +/*
1817 + * Update the state of the given GICv5 PPI for this CPU. Does nothing
1818 + * if the GICv5 is not present.
1819 + */
1820 +void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level);
1821 +
1822 /* Effective value of MDCR_EL2 */
1823 static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env)
1824 {
target/arm/tcg-stubs.c
+4
@@ -43,3 +43,7 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env)
43 void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask)
44 {
45 }
46 +
47 +void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level)
48 +{
49 +}
target/arm/tcg/gicv5-cpuif.c
+28
@@ -309,6 +309,34 @@ void gicv5_forward_interrupt(ARMCPU *cpu, GICv5Domain domain)
309 gicv5_update_irq_fiq(&cpu->env);
310 }
311
312 +void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level)
313 +{
314 + /*
315 + * Update the state of the given PPI (which is connected to some
316 + * CPU-internal source of interrupts, like the timers). We can
317 + * assume that the PPI is fixed as level-triggered, which means
318 + * that its pending state exactly tracks the input (and the guest
319 + * cannot separately change the pending state, because the pending
320 + * bits are RO).
321 + */
322 + int oldlevel;
323 +
324 + if (!cpu_isar_feature(aa64_gcie, env_archcpu(env))) {
325 + return;
326 + }
327 +
328 + /* The architected PPIs are 0..63, so in the first PPI register. */
329 + assert(ppi >= 0 && ppi < 64);
330 + oldlevel = extract64(env->gicv5_cpuif.ppi_pend[0], ppi, 1);
331 + if (oldlevel != level) {
332 + trace_gicv5_update_ppi_state(ppi, level);
333 +
334 + env->gicv5_cpuif.ppi_pend[0] =
335 + deposit64(env->gicv5_cpuif.ppi_pend[0], ppi, 1, level);
336 + gic_recalc_ppi_hppi(env);
337 + }
338 +}
339 +
340 static void gic_cddis_write(CPUARMState *env, const ARMCPRegInfo *ri,
341 uint64_t value)
342 {
target/arm/tcg/trace-events
+1
@@ -8,3 +8,4 @@ gicv5_gicr_cdia(int domain, uint32_t id) "domain %d CDIA acknowledge of interrup
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"
11 +gicv5_update_ppi_state(int ppi, bool level) "PPI %d source level now %d"