mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-08-26 22:23:12 -04:00
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:
parent
89ce374897
commit
3f79212aba
2 changed files with 103 additions and 0 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue