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
This commit is contained in:
Peter Maydell 2026-03-27 11:16:43 +00:00
parent 89ce374897
commit 3f79212aba
2 changed files with 103 additions and 0 deletions

View file

@ -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,

View file

@ -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"