The GICv5 cpuif allows the hypervisor to configure PPIs to be directly injected into a VM. Setting the appropriate bit in ICH_PPI_DVIR<n>_EL2 causes that physical PPI to appear as a virtual PPI in the virtual interupt domain.
Directly injected PPIs mostly behave like true virtual PPIs; the exception is that their pending state tracks the pending state of the underlying physical PPI, and guest operations that read or update the pending state work on the physical PPI's pending state. (Hypervisor operations via ICH_PPI_PENDR<n>_EL2 are not changed, and don't work with the physical PPI pending state.) The complication here is that when nested virtualization is active the EL1 timer PPIs must be redirected so that they appear with different virtual PPI IDs from their physical PPI IDs. All other injection is a simple 1:1 mapping with a physical PPI always injecting to the virtual PPI with the same ID. In this commit we update the places which read the pending state of virtual PPIs to honour the DVI bits and report the pending state of the underlying direct-injected PPI. Signed-off-by: Peter Maydell <[email protected]> --- target/arm/tcg/gicv5-cpuif.c | 95 +++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 69e51359f22..cdde759bf41 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -192,6 +192,97 @@ static GICv5Domain gicv5_domain_for_insn(CPUARMState *env, return gicv5_current_phys_domain(env); } +static uint64_t gic_virt_dvi_mask(CPUARMState *env, int reg) +{ + /* + * Return the mask of bits of virtual PPIs which are being direct + * injected. 'reg' is which of the two ICH_PPI_DVIR<n>_EL2 regs we + * are dealing with. + * + * The complexity here comes from the requirement when doing + * nested virtualization to redirect injected EL1 timer PPIs so + * that they appear with the PPI numbers that the inner hypervisor + * expects (see R_GYGNW). + * + * Note that the ICH_PPI_DVIRn bits correspond to physical PPIs; + * the ICH_PPI_HVIRn bits correspond to virtual PPIs. + */ + uint64_t dvi_mask = env->gicv5_cpuif.ich_ppi_dvi[reg]; + + if (reg == 0 && + (arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == HCR_NV) { + if (arm_is_secure_below_el3(env)) { + /* + * If we are injecting phys PPI 30, it appears as virtual + * PPI 20; phys PPI 27 appears as virtual PPI 19. Note + * that if we are not injecting the phys PPI 30/27 then + * the guest is allowed to configure phys PPI 20 to + * direct-inject to virt PPI 20 if it likes. + */ + if (extract64(dvi_mask, 30, 1)) { + dvi_mask |= 1 << 20; + } + if (extract64(dvi_mask, 27, 1)) { + dvi_mask |= 1 << 19; + } + } else { + /* phys PPI 30 -> vPPI 26; phys PPI 27 -> vPPI 28 */ + if (extract64(dvi_mask, 30, 1)) { + dvi_mask |= 1 << 26; + } + if (extract64(dvi_mask, 27, 1)) { + dvi_mask |= 1 << 28; + } + } + /* We aren't direct injecting to virtual PPIs 30 or 27 */ + dvi_mask &= ~((1 << 30) | (1 << 27)); + } + + /* + * Now we've converted dvi_mask to virtual PPIs, fold in + * ICH_PPI_HVIR<n>_EL2: if the vPPI is hidden it can't be + * direct-injected to. + */ + dvi_mask &= ~env->gicv5_cpuif.ich_ppi_hvi[reg]; + return dvi_mask; +} + +static uint64_t gic_virt_ppi_pending(CPUARMState *env, int reg) +{ + /* + * Return the pending state of the virtual PPIs; this is the + * combination of the ICH_PPI_PENDR<n>_EL2 state and the pending + * state of the physical PPIs which are being direct-injected. + */ + uint64_t dvi_pend = env->gicv5_cpuif.ppi_pend[reg]; + uint64_t ppi_dvi = env->gicv5_cpuif.ich_ppi_dvi[reg]; + uint64_t dvi_mask = gic_virt_dvi_mask(env, reg); + + if (reg == 0 && + (arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == HCR_NV) { + if (arm_is_secure_below_el3(env)) { + /* 30 -> 20 and 27 -> 19 */ + if (extract64(ppi_dvi, 30, 1)) { + dvi_pend = deposit64(dvi_pend, extract64(dvi_pend, 30, 1), 20, 1); + } + if (extract64(ppi_dvi, 27, 1)) { + dvi_pend = deposit64(dvi_pend, extract64(dvi_pend, 27, 1), 19, 1); + } + } else { + /* 30 -> 26 and 27 -> 28 */ + if (extract64(ppi_dvi, 30, 1)) { + dvi_pend = deposit64(dvi_pend, extract64(dvi_pend, 30, 1), 26, 1); + } + if (extract64(ppi_dvi, 27, 1)) { + dvi_pend = deposit64(dvi_pend, extract64(dvi_pend, 27, 1), 28, 1); + } + } + dvi_pend &= ~((1 << 30) | (1 << 27)); + } + dvi_pend &= dvi_mask; + return (env->gicv5_cpuif.ich_ppi_pend[reg] & ~dvi_mask) | dvi_pend; +} + static uint64_t gic_running_prio(CPUARMState *env, GICv5Domain domain) { /* @@ -461,7 +552,7 @@ static void gic_recalc_virt_ppi_hppi(CPUARMState *env) for (int i = 0; i < ARRAY_SIZE(env->gicv5_cpuif.ich_ppi_active); i++) { uint64_t en_pend_nact = env->gicv5_cpuif.ich_ppi_enable[i] & - env->gicv5_cpuif.ich_ppi_pend[i] & + gic_virt_ppi_pending(env, i) & ~env->gicv5_cpuif.ich_ppi_active[i]; while (en_pend_nact) { @@ -716,7 +807,7 @@ static void gic_ppi_sactive_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t gic_ppi_pend_read(CPUARMState *env, const ARMCPRegInfo *ri) { if (gicv5_icv_access(env)) { - uint64_t value = env->gicv5_cpuif.ich_ppi_pend[ri->opc2 & 1]; + uint64_t value = gic_virt_ppi_pending(env, ri->opc2 & 1); value &= ~env->gicv5_cpuif.ich_ppi_hvi[ri->opc2 & 1]; return value; -- 2.43.0
