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>
---
target/arm/tcg/gicv5-cpuif.c | 101 +++++++++++++++++++++++++++++++++++
target/arm/tcg/trace-events | 2 +
2 files changed, 103 insertions(+)
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index 36bbb70c4a..09870e0b09 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -39,6 +39,10 @@ FIELD(GIC_CDHM, HM, 32, 1)
FIELD(GIC_CDRCFG, ID, 0, 24)
FIELD(GIC_CDRCFG, TYPE, 29, 3)
+FIELD(GICR_CDIA, ID, 0, 24)
+FIELD(GICR_CDIA, TYPE, 29, 3)
+FIELD(GICR_CDIA, VALID, 32, 1)
+
FIELD(ICC_IDR0_EL1, ID_BITS, 0, 4)
FIELD(ICC_IDR0_EL1, PRI_BITS, 4, 4)
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)
return hppi.intid;
}
+static bool gic_hppi_is_nmi(CPUARMState *env, GICv5PendingIrq hppi,
+ GICv5Domain domain)
+{
+ /*
+ * For GICv5 an interrupt is an NMI if it is signaled with
+ * Superpriority and SCTLR_ELx.NMI for the current EL is 1. GICR
+ * CDIA/CDNMIA always work on the current interrupt domain, so we
+ * do not need to consider preemptive interrupts. This means that
+ * the interrupt has Superpriority if and only if it has priority 0.
+ */
+ return hppi.prio == 0 && arm_sctlr(env, arm_current_el(env)) & SCTLR_NMI;
+}
+
+static uint64_t gicr_cdia_read(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+ /* Acknowledge HPPI in the current interrupt domain */
+ GICv5Common *gic = gicv5_get_gic(env);
+ GICv5Domain domain = gicv5_current_phys_domain(env);
+ GICv5PendingIrq hppi = gic_hppi(env, domain);
+ GICv5IntType type = FIELD_EX64(hppi.intid, INTID, TYPE);
+ uint32_t id = FIELD_EX64(hppi.intid, INTID, ID);
+
+ bool cdnmia = ri->opc2 == 1;
+
+ if (!hppi.intid) {
+ /* No interrupt available to acknowledge */
+ trace_gicv5_gicr_cdia_fail(domain,
+ "no available interrupt to acknowledge");
+ return 0;
+ }
+ assert(hppi.prio != PRIO_IDLE);
+
+ if (gic_hppi_is_nmi(env, hppi, domain) != cdnmia) {
+ /* GICR CDIA only acknowledges non-NMI; GICR CDNMIA only NMI */
+ trace_gicv5_gicr_cdia_fail(domain,
+ cdnmia ? "CDNMIA but HPPI is not NMI" :
+ "CDIA but HPPI is NMI");
+ return 0;
+ }
+
+ trace_gicv5_gicr_cdia(domain, hppi.intid);
+
+ /*
+ * The interrupt becomes Active. If the handling mode of the
+ * interrupt is Edge then we also clear the pending state.
+ */
+
+ /*
+ * Set the appropriate bit in the APR to track active priorities.
+ * We do this now so that when gic_recalc_ppi_hppi() or
+ * gicv5_activate() cause a re-evaluation of HPPIs they use the
+ * right (new) running priority.
+ */
+ env->gicv5_cpuif.icc_apr[domain] |= (1 << hppi.prio);
+ switch (type) {
+ case GICV5_PPI:
+ {
+ uint32_t ppireg, ppibit;
+
+ assert(id < GICV5_NUM_PPIS);
+ ppireg = id / 64;
+ ppibit = 1 << (id % 64);
+
+ env->gicv5_cpuif.ppi_active[ppireg] |= ppibit;
+ if (!(env->gicv5_cpuif.ppi_hm[ppireg] & ppibit)) {
+ /* handling mode is Edge: clear pending */
+ env->gicv5_cpuif.ppi_pend[ppireg] &= ~ppibit;
+ }
+ gic_recalc_ppi_hppi(env);
+ break;
+ }
+ case GICV5_LPI:
+ case GICV5_SPI:
+ /*
+ * Send an Activate command to the IRS, which, despite the
+ * name of the stream command, does both "set Active" and
+ * "maybe set not Pending" as a single atomic action.
+ */
+ gicv5_activate(gic, id, domain, type, false);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return hppi.intid | R_GICR_CDIA_VALID_MASK;
+}
+
static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
/*
* Barrier: wait until the effects of a cpuif system register
@@ -520,6 +611,16 @@ static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
.access = PL1_W, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.writefn = gic_cdhm_write,
},
+ { .name = "GICR_CDIA", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 0, .crn = 12, .crm = 3, .opc2 = 0,
+ .access = PL1_R, .type = ARM_CP_IO | ARM_CP_NO_RAW,
+ .readfn = gicr_cdia_read,
+ },
+ { .name = "GICR_CDNMIA", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 0, .crn = 12, .crm = 3, .opc2 = 1,
+ .access = PL1_R, .type = ARM_CP_IO | ARM_CP_NO_RAW,
+ .readfn = gicr_cdia_read,
+ },
{ .name = "ICC_IDR0_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 12, .crm = 10, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW,
diff --git a/target/arm/tcg/trace-events b/target/arm/tcg/trace-events
index 7dc5f781c5..13e15cfcfc 100644
--- a/target/arm/tcg/trace-events
+++ b/target/arm/tcg/trace-events
@@ -3,3 +3,5 @@
# gicv5-cpuif.c
gicv5_recalc_ppi_hppi(int domain, uint32_t id, uint8_t prio) "domain %d new PPI HPPI id 0x%x prio %u"
+gicv5_gicr_cdia_fail(int domain, const char *reason) "domain %d CDIA attempt failed: %s"
+gicv5_gicr_cdia(int domain, uint32_t id) "domain %d CDIA acknowledge of interrupt 0x%x"
--
2.43.0