diff --git a/hypervisor/arch/x86/guest/vlapic.c b/hypervisor/arch/x86/guest/vlapic.c index c1f087aa7..f52bfcf52 100644 --- a/hypervisor/arch/x86/guest/vlapic.c +++ b/hypervisor/arch/x86/guest/vlapic.c @@ -2229,21 +2229,75 @@ static void apicv_basic_inject_intr(struct acrn_vlapic *vlapic, } /* - * @brief Send a Posted Interrupt to itself. - * - * Interrupts are disabled on pCPU at this point of time. - * Upon the next VMEnter the self-IPI is serviced by the logical processor. - * Since the IPI vector is Posted Interrupt vector, logical processor syncs - * PIR to vIRR and updates RVI. - * - * @pre get_pi_desc(vlapic->vcpu) != NULL + * Transfer the pending interrupts in the PIR descriptor to the IRR + * in the virtual APIC page. + * @pre get_pi_desc(vlapic2vcpu(vlapic)) != NULL */ +static void vlapic_apicv_inject_pir(struct acrn_vlapic *vlapic) +{ + struct pi_desc *pid; + struct lapic_regs *lapic; + uint64_t val, pirval; + uint16_t rvi, pirbase = 0U, i; + uint16_t intr_status_old, intr_status_new; + struct lapic_reg *irr = NULL; + + pid = get_pi_desc(vlapic2vcpu(vlapic)); + if (bitmap_test_and_clear_lock(POSTED_INTR_ON, &pid->control.value)) { + pirval = 0UL; + lapic = &(vlapic->apic_page); + irr = &lapic->irr[0]; + + for (i = 0U; i < 4U; i++) { + val = atomic_readandclear64(&pid->pir[i]); + if (val != 0UL) { + irr[i * 2U].v |= (uint32_t)val; + irr[(i * 2U) + 1U].v |= (uint32_t)(val >> 32U); + + pirbase = 64U * i; + pirval = val; + } + } + + /* + * Update RVI so the processor can evaluate pending virtual + * interrupts on VM-entry. + * + * It is possible for pirval to be 0 here, even though the + * pending bit has been set. The scenario is: + * CPU-Y is sending a posted interrupt to CPU-X, which + * is running a guest and processing posted interrupts in h/w. + * CPU-X will eventually exit and the state seen in s/w is + * the pending bit set, but no PIR bits set. + * + * CPU-X CPU-Y + * (vm running) (host running) + * rx posted interrupt + * CLEAR pending bit + * SET PIR bit + * READ/CLEAR PIR bits + * SET pending bit + * (vm exit) + * pending bit set, PIR 0 + */ + if (pirval != 0UL) { + rvi = pirbase + fls64(pirval); + + intr_status_old = 0xFFFFU & + exec_vmread16(VMX_GUEST_INTR_STATUS); + + intr_status_new = (intr_status_old & 0xFF00U) | rvi; + if (intr_status_new > intr_status_old) { + exec_vmwrite16(VMX_GUEST_INTR_STATUS, + intr_status_new); + } + } + } +} static void apicv_advanced_inject_intr(struct acrn_vlapic *vlapic, __unused bool guest_irq_enabled, __unused bool injected) { - struct acrn_vcpu *vcpu = vlapic2vcpu(vlapic); - struct pi_desc *pid = get_pi_desc(vcpu); /* * From SDM Vol3 26.3.2.5: * Once the virtual interrupt is recognized, it will be delivered @@ -2254,12 +2308,10 @@ static void apicv_advanced_inject_intr(struct acrn_vlapic *vlapic, * evaluation/delivery of apicv virtual interrupts in one time * vm-entry. * - * Here to sync the pending interrupts to irr and update rvi - * self-IPI with Posted Interrupt Notification Vector is sent. + * Here to sync the pending interrupts to irr and update rvi if + * needed. And then try to handle vmcs event injection. */ - if (bitmap_test(POSTED_INTR_ON, &(pid->control.value))) { - apicv_trigger_pi_anv(pcpuid_from_vcpu(vcpu), (uint32_t)(vcpu->arch.pid.control.bits.nv)); - } + vlapic_apicv_inject_pir(vlapic); } void vlapic_inject_intr(struct acrn_vlapic *vlapic, bool guest_irq_enabled, bool injected)