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 <[email protected]> --- target/arm/tcg/gicv5-cpuif.c | 102 +++++++++++++++++++++++++++++++++++ target/arm/tcg/trace-events | 2 + 2 files changed, 104 insertions(+) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index adb4d2018f..d32263a5b7 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) @@ -464,6 +468,94 @@ 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 @@ -521,6 +613,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
