@samitouri / QOSamiQemu / commits / 3f79212aba

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"