target/arm: GICv5 cpuif: Implement GICR CDIA command
The GICR CDIA system instruction is what the guest uses to acknowledge the highest priority pending interrupt. It returns a value corresponding to the HPPI for the current physical interrupt domain, if any, and moves that interrupt to being Active. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Message-id: 20260327111700.795099-49-peter.maydell@linaro.org
Peter Maydell committed
Mar 27, 2026 at 11:16 UTC
3f79212abae8950eca66360c07f23451ced026a5
2 files changed
+103
target/arm/tcg/gicv5-cpuif.c
+101
@@ -39,6 +39,10 @@ FIELD(GIC_CDHM, HM, 32, 1)
39
FIELD(GIC_CDRCFG, ID, 0, 24)
40
FIELD(GIC_CDRCFG, TYPE, 29, 3)
41
42
+FIELD(GICR_CDIA, ID, 0, 24)
43
+FIELD(GICR_CDIA, TYPE, 29, 3)
44
+FIELD(GICR_CDIA, VALID, 32, 1)
45
+
46
FIELD(ICC_IDR0_EL1, ID_BITS, 0, 4)
47
FIELD(ICC_IDR0_EL1, PRI_BITS, 4, 4)
48
FIELD(ICC_IDR0_EL1, GCIE_LEGACY, 8, 4)
@@ -463,6 +467,93 @@ static uint64_t gic_icc_hppir_el1_read(CPUARMState *env, const ARMCPRegInfo *ri)
467
return hppi.intid;
468
}
469
470
+static bool gic_hppi_is_nmi(CPUARMState *env, GICv5PendingIrq hppi,
471
+ GICv5Domain domain)
472
+{
473
+ /*
474
+ * For GICv5 an interrupt is an NMI if it is signaled with
475
+ * Superpriority and SCTLR_ELx.NMI for the current EL is 1. GICR
476
+ * CDIA/CDNMIA always work on the current interrupt domain, so we
477
+ * do not need to consider preemptive interrupts. This means that
478
+ * the interrupt has Superpriority if and only if it has priority 0.
479
+ */
480
+ return hppi.prio == 0 && arm_sctlr(env, arm_current_el(env)) & SCTLR_NMI;
481
+}
482
+
483
+static uint64_t gicr_cdia_read(CPUARMState *env, const ARMCPRegInfo *ri)
484
+{
485
+ /* Acknowledge HPPI in the current interrupt domain */
486
+ GICv5Common *gic = gicv5_get_gic(env);
487
+ GICv5Domain domain = gicv5_current_phys_domain(env);
488
+ GICv5PendingIrq hppi = gic_hppi(env, domain);
489
+ GICv5IntType type = FIELD_EX64(hppi.intid, INTID, TYPE);
490
+ uint32_t id = FIELD_EX64(hppi.intid, INTID, ID);
491
+
492
+ bool cdnmia = ri->opc2 == 1;
493
+
494
+ if (!hppi.intid) {
495
+ /* No interrupt available to acknowledge */
496
+ trace_gicv5_gicr_cdia_fail(domain,
497
+ "no available interrupt to acknowledge");
498
+ return 0;
499
+ }
500
+ assert(hppi.prio != PRIO_IDLE);
501
+
502
+ if (gic_hppi_is_nmi(env, hppi, domain) != cdnmia) {
503
+ /* GICR CDIA only acknowledges non-NMI; GICR CDNMIA only NMI */
504
+ trace_gicv5_gicr_cdia_fail(domain,
505
+ cdnmia ? "CDNMIA but HPPI is not NMI" :
506
+ "CDIA but HPPI is NMI");
507
+ return 0;
508
+ }
509
+
510
+ trace_gicv5_gicr_cdia(domain, hppi.intid);
511
+
512
+ /*
513
+ * The interrupt becomes Active. If the handling mode of the
514
+ * interrupt is Edge then we also clear the pending state.
515
+ */
516
+
517
+ /*
518
+ * Set the appropriate bit in the APR to track active priorities.
519
+ * We do this now so that when gic_recalc_ppi_hppi() or
520
+ * gicv5_activate() cause a re-evaluation of HPPIs they use the
521
+ * right (new) running priority.
522
+ */
523
+ env->gicv5_cpuif.icc_apr[domain] |= (1 << hppi.prio);
524
+ switch (type) {
525
+ case GICV5_PPI:
526
+ {
527
+ uint32_t ppireg, ppibit;
528
+
529
+ assert(id < GICV5_NUM_PPIS);
530
+ ppireg = id / 64;
531
+ ppibit = 1 << (id % 64);
532
+
533
+ env->gicv5_cpuif.ppi_active[ppireg] |= ppibit;
534
+ if (!(env->gicv5_cpuif.ppi_hm[ppireg] & ppibit)) {
535
+ /* handling mode is Edge: clear pending */
536
+ env->gicv5_cpuif.ppi_pend[ppireg] &= ~ppibit;
537
+ }
538
+ gic_recalc_ppi_hppi(env);
539
+ break;
540
+ }
541
+ case GICV5_LPI:
542
+ case GICV5_SPI:
543
+ /*
544
+ * Send an Activate command to the IRS, which, despite the
545
+ * name of the stream command, does both "set Active" and
546
+ * "maybe set not Pending" as a single atomic action.
547
+ */
548
+ gicv5_activate(gic, id, domain, type, false);
549
+ break;
550
+ default:
551
+ g_assert_not_reached();
552
+ }
553
+
554
+ return hppi.intid | R_GICR_CDIA_VALID_MASK;
555
+}
556
+
557
static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
558
/*
559
* Barrier: wait until the effects of a cpuif system register
@@ -520,6 +611,16 @@ static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
611
.access = PL1_W, .type = ARM_CP_IO | ARM_CP_NO_RAW,
612
.writefn = gic_cdhm_write,
613
},
614
+ { .name = "GICR_CDIA", .state = ARM_CP_STATE_AA64,
615
+ .opc0 = 1, .opc1 = 0, .crn = 12, .crm = 3, .opc2 = 0,
616
+ .access = PL1_R, .type = ARM_CP_IO | ARM_CP_NO_RAW,
617
+ .readfn = gicr_cdia_read,
618
+ },
619
+ { .name = "GICR_CDNMIA", .state = ARM_CP_STATE_AA64,
620
+ .opc0 = 1, .opc1 = 0, .crn = 12, .crm = 3, .opc2 = 1,
621
+ .access = PL1_R, .type = ARM_CP_IO | ARM_CP_NO_RAW,
622
+ .readfn = gicr_cdia_read,
623
+ },
624
{ .name = "ICC_IDR0_EL1", .state = ARM_CP_STATE_AA64,
625
.opc0 = 3, .opc1 = 0, .crn = 12, .crm = 10, .opc2 = 2,
626
.access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW,
target/arm/tcg/trace-events
+2
@@ -3,3 +3,5 @@
3
4
# gicv5-cpuif.c
5
gicv5_recalc_ppi_hppi(int domain, uint32_t id, uint8_t prio) "domain %d new PPI HPPI id 0x%x prio %u"
6
+gicv5_gicr_cdia_fail(int domain, const char *reason) "domain %d CDIA attempt failed: %s"
7
+gicv5_gicr_cdia(int domain, uint32_t id) "domain %d CDIA acknowledge of interrupt 0x%x"