The IRS_CR0.IRSEN bit is somewhat misleadingly named: it is not so much an "enable the IRS" bit, but instead an "enable having the IRS forward HPPIs to the CPU" bit. If it's 0 then the IRS still does all its usual activities, apart from telling the CPUs about interrupt candidates.
This applies also to virtual interrupts. For physical interrupts, we implemented this by not calculating the pending HPPI when IRSEN is zero, but for virtual interrupts we can't do it that way, for several reasons: * the vHPPI is visible to the guest via IRS_VPE_HPPIR * whether we should ring a doorbell interrupt depends on the vHPPI, and this still happens even when IRSEN is 0 So instead we need to check IRSEN in gicv5_get_hppi() and return "no interrupt available" if it is 0. We also need to re-prod the CPUs to ask the IRS about its HPPI candidates when IRSEN changes. We could check IRSEN only inside the "is (virtual)" block of gicv5_get_hppi(), but it's equivalent to do it unconditionally (s->hppi[domain][cpuidx] will bo IRQ_NONE in that case), and it's a bit clearer I think. Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index a1fe6a8a822..4572e5a4017 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -340,6 +340,31 @@ static void irs_recall_hppis(GICv5 *s, GICv5Domain domain) } } +static void irs_resend_vhppis(GICv5 *s, GICv5Domain domain) +{ + /* + * The IRS was enabled or disabled for this domain. In hardware + * this makes the IRS either forward or recall any pending vHPPI + * it had for a VPE resident on the CPU. For QEMU, we tell the CPU + * interface that it needs to ask the IRS about the status of + * virtual interrupts for any VPE that is currently resident + * (which will return either "none" or "here's the new one"). + */ + GICv5Common *cs = ARM_GICV5_COMMON(s); + + if (!gicv5_domain_has_virt(cs, domain)) { + return; + } + + for (int i = 0; i < cs->num_cpus; i++) { + VPE *vpe = s->res_vpe[i]; + + if (vpe && vpe->vmte->domain == domain) { + gicv5_forward_interrupt(cs->cpus[i], domain, true); + } + } +} + GICv5PendingIrq gicv5_get_hppi(GICv5Common *cs, GICv5Domain domain, bool virtual, uint32_t cpuidx) { @@ -347,6 +372,10 @@ GICv5PendingIrq gicv5_get_hppi(GICv5Common *cs, GICv5Domain domain, assert(cpuidx < cs->num_cpus); + if (!FIELD_EX32(cs->irs_cr0[domain], IRS_CR0, IRSEN)) { + return GICV5_PENDING_IRQ_NONE; + } + if (virtual) { VPE *vpe = s->res_vpe[cpuidx]; @@ -2738,6 +2767,8 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset, } else { irs_recall_hppis(s, domain); } + /* IRSEN affects whether we should forward vHPPIs */ + irs_resend_vhppis(s, domain); return true; case A_IRS_CR1: *data = cs->irs_cr1[domain]; -- 2.43.0
