diff --git a/include/VBox/err.h b/include/VBox/err.h index 069dd188ab8..b5d0bbadf08 100644 --- a/include/VBox/err.h +++ b/include/VBox/err.h @@ -3143,6 +3143,10 @@ #define VERR_APIC_IPE_1 (-6706) /** APIC internal error \#2. */ #define VERR_APIC_IPE_2 (-6707) +/** Update the APIC state in R3 after an unaccelerated write (AVIC/APICv). */ +#define VINF_APIC_R3_UPDATE_STATE 6708 +/** Pending interrupt deferred for delivery by hardware (AVIC/APICv). */ +#define VERR_APIC_INTR_DEFER (-6709) /** @} */ /** @name NEM Status Codes diff --git a/include/VBox/vmm/hm.h b/include/VBox/vmm/hm.h index 6bd25b889b2..36a546a83c0 100644 --- a/include/VBox/vmm/hm.h +++ b/include/VBox/vmm/hm.h @@ -300,6 +300,7 @@ VMMR3DECL(bool) HMR3IsEnabled(PUVM pUVM); VMMR3DECL(bool) HMR3IsNestedPagingActive(PUVM pUVM); VMMR3DECL(bool) HMR3AreVirtApicRegsEnabled(PUVM pUVM); VMMR3DECL(bool) HMR3IsPostedIntrsEnabled(PUVM pUVM); +VMMR3DECL(bool) HMR3IsAvicEnabled(PUVM pUVM); VMMR3DECL(bool) HMR3IsVpidActive(PUVM pUVM); VMMR3DECL(bool) HMR3IsUXActive(PUVM pUVM); VMMR3DECL(bool) HMR3IsSvmEnabled(PUVM pUVM); diff --git a/include/VBox/vmm/hm_svm.h b/include/VBox/vmm/hm_svm.h index 95d5bf59689..e7a0c404425 100644 --- a/include/VBox/vmm/hm_svm.h +++ b/include/VBox/vmm/hm_svm.h @@ -65,6 +65,8 @@ #define SVM_MSRPM_PAGES 2 /** Number of pages required for the IO permission bitmap. */ #define SVM_IOPM_PAGES 3 +/** Number of pages required for the AVIC per-VM (APIC access, logical, physical tables). */ +#define SVM_AVIC_PAGES 3 /** @} */ /* @@ -337,6 +339,20 @@ #endif /* !IN_REM_R3*/ +/** @name SVMVMCB.u64ExitInfo2 for AVIC Incomplete IPI. + * @{ + */ +#define SVM_EXIT2_INC_IPI_INDEX_MASK UINT64_C(0xfff) +#define SVM_EXIT2_INC_IPI_ID_SHIFT 32 +#define SVM_EXIT2_INC_IPI_INDEX_INVALID_INTR_TYPE 0 +#define SVM_EXIT2_INC_IPI_INDEX_TARGET_NOT_RUNNING 1 +#define SVM_EXIT2_INC_IPI_INDEX_INVALID_TARGET 2 +#define SVM_EXIT2_INC_IPI_INDEX_INVALID_PTR 3 +#define SVM_EXIT2_INC_IPI_INDEX_INVALID_IPI_VECTOR 4 +#define SVM_EXIT2_INC_IPI_INDEX_UNACCEL_IPI 5 +/** @} */ + + /** @name SVMVMCB.u64ExitInfo2 for task switches * @{ */ @@ -627,7 +643,8 @@ typedef union uint32_t u3Reserved : 3; uint32_t u1VIntrMasking : 1; /* V_INTR_MASKING */ uint32_t u1VGifEnable : 1; /* VGIF enable */ - uint32_t u5Reserved : 5; + uint32_t u4Reserved : 4; + uint32_t u1X2AvicEnable : 1; /* X2AVIC enable */ uint32_t u1AvicEnable : 1; /* AVIC enable */ uint32_t u8VIntrVector : 8; /* V_INTR_VECTOR */ uint32_t u24Reserved : 24; diff --git a/include/VBox/vmm/pdmapic.h b/include/VBox/vmm/pdmapic.h index feacab90e3b..3c23fe29dfe 100644 --- a/include/VBox/vmm/pdmapic.h +++ b/include/VBox/vmm/pdmapic.h @@ -362,6 +362,26 @@ typedef struct PDMAPICBACKENDR3 */ DECLR3CALLBACKMEMBER(VBOXSTRICTRC, pfnExportState, (PVMCPUCC pVCpu)); + /** + * Updates the APIC state after a write to the APIC page by hardware. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * + * @note This is a helper for AVIC/APICv when used on AMD or Intel. + */ + DECLR3CALLBACKMEMBER(VBOXSTRICTRC, pfnUpdateStateAfterWrite, (PVMCPUCC pVCpu, uint16_t offApicReg)); + + /** + * Sets the End-Of-Interrupt (EOI) register when the vector corresponding + * to the EOI is given. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param uVector The vector for the attempted EOI. + */ + DECLR3CALLBACKMEMBER(VBOXSTRICTRC, pfnSetEoiFast, (PVMCPUCC pVCpu, uint8_t uVector)); + /** @name Reserved for future (MBZ). * @{ */ DECLR3CALLBACKMEMBER(int, pfnReserved0, (void)); @@ -370,8 +390,6 @@ typedef struct PDMAPICBACKENDR3 DECLR3CALLBACKMEMBER(int, pfnReserved3, (void)); DECLR3CALLBACKMEMBER(int, pfnReserved4, (void)); DECLR3CALLBACKMEMBER(int, pfnReserved5, (void)); - DECLR3CALLBACKMEMBER(int, pfnReserved6, (void)); - DECLR3CALLBACKMEMBER(int, pfnReserved7, (void)); /** @} */ } PDMAPICBACKENDR3; /** Pointer to ring-3 APIC backend. */ @@ -624,10 +642,31 @@ typedef struct PDMAPICBACKENDR0 * Exports the APIC state. * * @returns Strict VBox status code. - * @param pVCpu The cross context virtual CPU structure. + * @param pVCpu The cross context virtual CPU structure. + * @param offApicReg The APIC register offset which was updated. */ DECLR0CALLBACKMEMBER(VBOXSTRICTRC, pfnExportState, (PVMCPUCC pVCpu)); + /** + * Updates the APIC state after a write to the APIC page by hardware. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * + * @note This is a helper for AVIC/APICv when used on AMD or Intel. + */ + DECLR0CALLBACKMEMBER(VBOXSTRICTRC, pfnUpdateStateAfterWrite, (PVMCPUCC pVCpu, uint16_t offApicReg)); + + /** + * Sets the End-Of-Interrupt (EOI) register when the vector corresponding + * to the EOI is given. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param uVector The vector for the attempted EOI. + */ + DECLR0CALLBACKMEMBER(VBOXSTRICTRC, pfnSetEoiFast, (PVMCPUCC pVCpu, uint8_t uVector)); + /** @name Reserved for future (MBZ). * @{ */ DECLR0CALLBACKMEMBER(int, pfnReserved0, (void)); @@ -636,8 +675,6 @@ typedef struct PDMAPICBACKENDR0 DECLR0CALLBACKMEMBER(int, pfnReserved3, (void)); DECLR0CALLBACKMEMBER(int, pfnReserved4, (void)); DECLR0CALLBACKMEMBER(int, pfnReserved5, (void)); - DECLR0CALLBACKMEMBER(int, pfnReserved6, (void)); - DECLR0CALLBACKMEMBER(int, pfnReserved7, (void)); /** @} */ } PDMAPICBACKENDR0; /** Pointer to ring-0 APIC backend. */ @@ -946,6 +983,8 @@ VMM_INT_DECL(int) PDMApicSetBaseMsr(PVMCPUCC pVCpu, uint64_t u64BaseMs VMM_INT_DECL(int) PDMApicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, uint32_t *puSrcTag); VMM_INT_DECL(int) PDMApicBusDeliver(PVMCC pVM, uint8_t uDest, uint8_t uDestMode, uint8_t uDeliveryMode, uint8_t uVector, uint8_t uPolarity, uint8_t uTriggerMode, uint8_t uIoApicPin, uint32_t uTagSrc); +VMM_INT_DECL(VBOXSTRICTRC) PDMApicUpdateStateAfterWrite(PVMCPUCC pVCpu, uint16_t offApicReg); +VMM_INT_DECL(VBOXSTRICTRC) PDMApicSetEoiFast(PVMCPUCC pVCpu, uint8_t uIsrVector); #ifdef IN_RING0 VMM_INT_DECL(int) PDMR0ApicGetApicPageForCpu(PCVMCPUCC pVCpu, PRTHCPHYS pHCPhys, PRTR0PTR pR0Ptr, PRTR3PTR pR3Ptr); #endif diff --git a/include/iprt/x86.h b/include/iprt/x86.h index 07885368277..8f657106d28 100644 --- a/include/iprt/x86.h +++ b/include/iprt/x86.h @@ -2395,6 +2395,8 @@ typedef const X86MTRRVAR *PCX86MTRRVAR; /** SVM - VM_HSAVE_PA - Physical address for saving and restoring * host state during world switch. */ #define MSR_K8_VM_HSAVE_PA UINT32_C(0xc0010117) +/** SVM - AVIC doorbell register. */ +#define MSR_AMD_AVIC_DOORBELL UINT32_C(0xc001011b) /** Virtualized speculation control for AMD processors. * diff --git a/src/VBox/VMM/VMMAll/PDMAll.cpp b/src/VBox/VMM/VMMAll/PDMAll.cpp index a4dcd6bcf04..5e5849733d4 100644 --- a/src/VBox/VMM/VMMAll/PDMAll.cpp +++ b/src/VBox/VMM/VMMAll/PDMAll.cpp @@ -72,18 +72,23 @@ VMMDECL(int) PDMGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Interrupt) int rc = VERR_NO_DATA; if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC)) { - VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); - uint32_t uTagSrc; rc = PDMApicGetInterrupt(pVCpu, pu8Interrupt, &uTagSrc); if (RT_SUCCESS(rc)) { VBOXVMM_PDM_IRQ_GET(pVCpu, RT_LOWORD(uTagSrc), RT_HIWORD(uTagSrc), *pu8Interrupt); Log8(("PDMGetInterrupt: irq=%#x tag=%#x (apic)\n", *pu8Interrupt, uTagSrc)); + VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); return VINF_SUCCESS; } - /* else if it's masked by TPR/PPR/whatever, go ahead checking the PIC. Such masked - interrupts shouldn't prevent ExtINT from being delivered. */ + + /* + * If it's masked by TPR/PPR/whatever, go ahead checking the PIC. Such masked + * interrupts shouldn't prevent ExtINT from being delivered. If the interrupt is + * deferred for delivery by the hardware, do -not- clear the force-flag here. + */ + if (rc != VERR_APIC_INTR_DEFER) + VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); } PVMCC pVM = pVCpu->CTX_SUFF(pVM); diff --git a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index 28140537f65..aa40cb39963 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -398,7 +398,7 @@ static void apicSignalNextPendingIntr(PVMCPUCC pVCpu) */ if (XAPIC_PPR_GET_PP(uVector) > XAPIC_PPR_GET_PP(uIsrVec)) { - Log2(("APIC%u: apicSignalNextPendingIntr: Signalling pending interrupt. uVector=%#x\n", pVCpu->idCpu, uVector)); + Log2(("APIC%u: apicSignalNextPendingIntr: Signalling pending interrupt. uVector=%#x irr=%.*Rhxd\n", pVCpu->idCpu, uVector, sizeof(pXApicPage->irr), &pXApicPage->irr.u)); apicSetInterruptFF(pVCpu, PDMAPICIRQ_HARDWARE); } else @@ -608,14 +608,19 @@ static VBOXSTRICTRC apicSendIntr(PVMCC pVM, PVMCPUCC pVCpu, uint8_t uVector, XAP && pVCpu) { /* - * Flag only errors when the delivery mode is fixed and not others. + * Flag only errors when the delivery mode is fixed or lowest-priority and not + * others. This applies to ICR and self-IPI in both xAPIC and x2APIC modes. + * - Intel: Documented under "Error Status Register (ESR)" in the + * Intel spec. "13.5.3 Error Handling". + * - AMD: Documented under "APICx280 [Error Status] (ErrorStatus)" in the CPU + * specific manual (e.g. "Processor Programming Reference (PPR) for + * AMD Family 17h Model 01h, Revision B1 Processors". * * Ubuntu 10.04-3 amd64 live CD with 2 VCPUs gets upset as it sends an SIPI to the * 2nd VCPU with vector 6 and checks the ESR for no errors, see @bugref{8245#c86}. */ - /** @todo The spec says this for LVT, but not explcitly for ICR-lo - * but it probably is true. */ - if (enmDeliveryMode == XAPICDELIVERYMODE_FIXED) + if ( enmDeliveryMode == XAPICDELIVERYMODE_FIXED + || enmDeliveryMode == XAPICDELIVERYMODE_LOWEST_PRIO) { if (RT_UNLIKELY(uVector <= XAPIC_ILLEGAL_VECTOR_END)) apicSetError(pVCpu, XAPIC_ESR_SEND_ILLEGAL_VECTOR); @@ -765,6 +770,7 @@ static VBOXSTRICTRC apicSetIcrLo(PVMCPUCC pVCpu, uint32_t uIcrLo, int rcRZ, bool STAM_COUNTER_INC(&pVCpu->apic.s.StatIcrLoWrite); RT_NOREF(fUpdateStat); + Assert(!(pXApicPage->icr_lo.all.u32IcrLo & XAPIC_LVT_DELIVERY_STATUS)); return apicSendIpi(pVCpu, rcRZ); } @@ -905,6 +911,65 @@ static int apicSetTprEx(PVMCPUCC pVCpu, uint32_t uTpr, bool fForceX2ApicBehaviou } +/** + * Helper for processing an EOI when the vector corresponding to the EOI is + * given. + * + * @param pVCpu The cross context virtual CPU structure. + * @param uVector The vector for attempted EOI. This is the highest + * in-service vector found in the ISR. + */ +static void apicProcessEoi(PVMCPUCC pVCpu, uint8_t uVector) +{ + /* + * Broadcast the EOI to the I/O APIC(s). + * + * We'll handle the EOI broadcast first as there is tiny chance we get rescheduled to + * ring-3 due to contention on the I/O APIC lock. This way we don't mess with the rest + * of the APIC state and simply restart the EOI write operation from ring-3. + */ + PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu); + bool const fLevelTriggered = apicTestVectorInReg(&pXApicPage->tmr, uVector); + if (fLevelTriggered) + { + PDMIoApicBroadcastEoi(pVCpu->CTX_SUFF(pVM), uVector); + + /* + * Clear the vector from the TMR. + * + * The broadcast to I/O APIC can re-trigger new interrupts to arrive via the bus. However, + * apicUpdatePendingInterrupts() which updates TMR can only be done from EMT which we + * currently are on, so no possibility of concurrent updates. + */ + apicClearVectorInReg(&pXApicPage->tmr, uVector); + + /* + * Clear the remote IRR bit for level-triggered, fixed mode LINT0 interrupt. + * The LINT1 pin does not support level-triggered interrupts. + * See Intel spec. 10.5.1 "Local Vector Table". + */ + uint32_t const uLvtLint0 = pXApicPage->lvt_lint0.all.u32LvtLint0; + if ( XAPIC_LVT_GET_REMOTE_IRR(uLvtLint0) + && XAPIC_LVT_GET_VECTOR(uLvtLint0) == uVector + && XAPIC_LVT_GET_DELIVERY_MODE(uLvtLint0) == XAPICDELIVERYMODE_FIXED) + { + ASMAtomicAndU32((volatile uint32_t *)&pXApicPage->lvt_lint0.all.u32LvtLint0, ~XAPIC_LVT_REMOTE_IRR); + Log2(("APIC%u: apicSetEoi: Cleared remote-IRR for LINT0. uVector=%#x\n", pVCpu->idCpu, uVector)); + } + + Log2(("APIC%u: apicSetEoi: Cleared level triggered interrupt from TMR. uVector=%#x\n", pVCpu->idCpu, uVector)); + } + + /* + * Mark interrupt as serviced, update the PPR and signal pending interrupts. + */ + Log2(("APIC%u: apicSetEoi: Clearing interrupt from ISR. uVector=%#x\n", pVCpu->idCpu, uVector)); + apicClearVectorInReg(&pXApicPage->isr, uVector); + apicUpdatePpr(pVCpu); + apicSignalNextPendingIntr(pVCpu); +} + + /** * Sets the End-Of-Interrupt (EOI) register. * @@ -930,53 +995,9 @@ static DECLCALLBACK(VBOXSTRICTRC) apicSetEoi(PVMCPUCC pVCpu, uint32_t uEoi, bool int isrv = apicGetHighestSetBitInReg(&pXApicPage->isr, -1 /* rcNotFound */); if (isrv >= 0) { - /* - * Broadcast the EOI to the I/O APIC(s). - * - * We'll handle the EOI broadcast first as there is tiny chance we get rescheduled to - * ring-3 due to contention on the I/O APIC lock. This way we don't mess with the rest - * of the APIC state and simply restart the EOI write operation from ring-3. - */ Assert(isrv <= (int)UINT8_MAX); - uint8_t const uVector = isrv; - bool const fLevelTriggered = apicTestVectorInReg(&pXApicPage->tmr, uVector); - if (fLevelTriggered) - { - PDMIoApicBroadcastEoi(pVCpu->CTX_SUFF(pVM), uVector); - - /* - * Clear the vector from the TMR. - * - * The broadcast to I/O APIC can re-trigger new interrupts to arrive via the bus. However, - * apicUpdatePendingInterrupts() which updates TMR can only be done from EMT which we - * currently are on, so no possibility of concurrent updates. - */ - apicClearVectorInReg(&pXApicPage->tmr, uVector); - - /* - * Clear the remote IRR bit for level-triggered, fixed mode LINT0 interrupt. - * The LINT1 pin does not support level-triggered interrupts. - * See Intel spec. 10.5.1 "Local Vector Table". - */ - uint32_t const uLvtLint0 = pXApicPage->lvt_lint0.all.u32LvtLint0; - if ( XAPIC_LVT_GET_REMOTE_IRR(uLvtLint0) - && XAPIC_LVT_GET_VECTOR(uLvtLint0) == uVector - && XAPIC_LVT_GET_DELIVERY_MODE(uLvtLint0) == XAPICDELIVERYMODE_FIXED) - { - ASMAtomicAndU32((volatile uint32_t *)&pXApicPage->lvt_lint0.all.u32LvtLint0, ~XAPIC_LVT_REMOTE_IRR); - Log2(("APIC%u: apicSetEoi: Cleared remote-IRR for LINT0. uVector=%#x\n", pVCpu->idCpu, uVector)); - } - - Log2(("APIC%u: apicSetEoi: Cleared level triggered interrupt from TMR. uVector=%#x\n", pVCpu->idCpu, uVector)); - } - - /* - * Mark interrupt as serviced, update the PPR and signal pending interrupts. - */ - Log2(("APIC%u: apicSetEoi: Clearing interrupt from ISR. uVector=%#x\n", pVCpu->idCpu, uVector)); - apicClearVectorInReg(&pXApicPage->isr, uVector); - apicUpdatePpr(pVCpu); - apicSignalNextPendingIntr(pVCpu); + uint8_t const uVector = isrv; + apicProcessEoi(pVCpu, uVector); } else { @@ -992,6 +1013,30 @@ static DECLCALLBACK(VBOXSTRICTRC) apicSetEoi(PVMCPUCC pVCpu, uint32_t uEoi, bool } +/** + * Sets the End-Of-Interrupt (EOI) register when the ISR is already known. + * + * This is an optimization that lets us avoid scanning the 256-bit sparse ISR + * register figuring out the highest pending in-service vector. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param uVector The vector for attempted EOI. This is the vector for the + * highest in-service vector. + * + * @note It it assumed the caller (hardware in the case of SVM AVIC) has + * already validated the value written to the EOI register. + */ +static DECLCALLBACK(VBOXSTRICTRC) apicSetEoiFast(PVMCPUCC pVCpu, uint8_t uVector) +{ + VMCPU_ASSERT_EMT(pVCpu); + Log2(("APIC%u: apicSetEoiFast: uEoi=%#RX32 uVector=%#x\n", pVCpu->idCpu, uVector)); + STAM_COUNTER_INC(&pVCpu->apic.s.StatEoiWriteFast); + apicProcessEoi(pVCpu, uVector); + return VINF_SUCCESS; +} + + /** * Sets the Logical Destination Register (LDR). * @@ -1554,7 +1599,7 @@ static DECLCALLBACK(VBOXSTRICTRC) apicReadMsr(PVMCPUCC pVCpu, uint32_t u32Reg, u * Validate. */ VMCPU_ASSERT_EMT(pVCpu); - Assert(u32Reg >= MSR_IA32_X2APIC_ID && u32Reg <= MSR_IA32_X2APIC_SELF_IPI); + AssertMsg(u32Reg >= MSR_IA32_X2APIC_ID && u32Reg <= MSR_IA32_X2APIC_SELF_IPI, ("u32Reg=%#x\n", u32Reg)); Assert(pu64Value); /* @@ -2388,6 +2433,20 @@ static DECLCALLBACK(int) apicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, ui LogFlow(("APIC%u: apicGetInterrupt:\n", pVCpu->idCpu)); + /* + * When SVM AVIC is in use, we only deliver PIC-style interrupts here. + * Other interrupts are updated in the APIC page by the usual mechanism + * and picked up by the hardware without explicit event injection. + */ + PVMCC pVM = pVCpu->CTX_SUFF(pVM); + PCAPIC pApic = VM_TO_APIC(pVM); + if (pApic->fAvicEnabled) + { + *pu8Vector = 0; + *puSrcTag = 0; + return VERR_APIC_INTR_DEFER; + } + PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu); bool const fApicHwEnabled = apicIsEnabled(pVCpu); if ( fApicHwEnabled @@ -2616,7 +2675,8 @@ DECLCALLBACK(bool) apicPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGG uint32_t uSrcTag) { Assert(pVCpu); - Assert(uVector > XAPIC_ILLEGAL_VECTOR_END); + AssertMsg(uVector > XAPIC_ILLEGAL_VECTOR_END, ("uVector=%#x, IcrLo=%#RX32 IcrHi=%#RX32\n", uVector, + VMCPU_TO_CX2APICPAGE(pVCpu)->icr_lo.all.u32IcrLo, VMCPU_TO_CX2APICPAGE(pVCpu)->icr_hi.u32IcrHi)); RT_NOREF(fAutoEoi); PVMCC pVM = pVCpu->CTX_SUFF(pVM); @@ -2835,7 +2895,7 @@ static DECLCALLBACK(void) apicUpdatePendingInterrupts(PVMCPUCC pVCpu) PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu); bool fHasPendingIntrs = false; - Log3(("APIC%u: apicUpdatePendingInterrupts:\n", pVCpu->idCpu)); + Log2(("APIC%u: apicUpdatePendingInterrupts:\n", pVCpu->idCpu)); STAM_PROFILE_START(&pApicCpu->StatUpdatePendingIntrs, a); /* Update edge-triggered pending interrupts. */ @@ -2949,6 +3009,56 @@ static DECLCALLBACK(VBOXSTRICTRC) apicExportState(PVMCPUCC pVCpu) } +/** + * @interface_method_impl{PDMAPICBACKENDR0,pfnUpdateStateAfterWrite} + */ +static DECLCALLBACK(VBOXSTRICTRC) apicVBoxUpdateStateAfterWrite(PVMCPUCC pVCpu, uint16_t offApicReg) +{ + AssertReturn(pVCpu, VERR_INVALID_PARAMETER); + + Assert(PDMHasApic(pVCpu->CTX_SUFF(pVM))); + + /* + * In SVM, vAPIC registers are 32-bits wide and currently the two 64-bit accesses + * (Self-IPI, and ICR) are both trap-like accesses meaning the the higher 32 bits + * are already updated. + */ + PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu); + VBOXSTRICTRC rcStrict; + if (XAPIC_IN_X2APIC_MODE(pVCpu->apic.s.uApicBaseMsr)) + { + /* This is the conversion documented by AMD in 16.11.1 "x2APIC Register Address Space". */ + uint32_t const idMsr = MSR_IA32_X2APIC_START + (offApicReg >> 4); + + /* + * We shouldn't be getting called with reading just the ICR_HI bits in x2APIC mode. + * If we do, we just forward the error to the guest, like normal x2APIC operation. + * The assert is thus debug only. + */ + Assert(offApicReg != XAPIC_OFF_ICR_HI); + + uint64_t u64Value = 0; + rcStrict = apicReadMsr(pVCpu, idMsr, &u64Value); + if (rcStrict == VINF_SUCCESS) + rcStrict = apicWriteMsr(pVCpu, idMsr, u64Value); + if ( rcStrict == VINF_CPUM_R3_MSR_READ + || rcStrict == VINF_CPUM_R3_MSR_WRITE) + rcStrict = VINF_APIC_R3_UPDATE_STATE; + } + else + { + uint32_t u32Value = 0; + rcStrict = apicReadRegister(pDevIns, pVCpu, offApicReg, &u32Value); + if (rcStrict == VINF_SUCCESS) + rcStrict = apicWriteRegister(pDevIns, pVCpu, offApicReg, u32Value); + if ( rcStrict == VINF_IOM_R3_MMIO_READ + || rcStrict == VINF_IOM_R3_MMIO_WRITE) + rcStrict = VINF_APIC_R3_UPDATE_STATE; + } + return rcStrict; +} + + #ifndef IN_RING3 /** @@ -3083,5 +3193,7 @@ const PDMAPICBACKEND g_ApicBackend = #endif /* .pfnImportState = */ apicImportState, /* .pfnExportState = */ apicExportState, + /* .pfnUpdateStateAfterWrite = */ apicVBoxUpdateStateAfterWrite, + /* .pfnSetEoiFast = */ apicSetEoiFast, }; diff --git a/src/VBox/VMM/VMMAll/target-x86/IEMAllCImplSvmInstr-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/IEMAllCImplSvmInstr-x86.cpp index f23bfcc4808..3fec202433a 100644 --- a/src/VBox/VMM/VMMAll/target-x86/IEMAllCImplSvmInstr-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/IEMAllCImplSvmInstr-x86.cpp @@ -482,7 +482,7 @@ static VBOXSTRICTRC iemSvmVmrun(PVMCPUCC pVCpu, uint8_t cbInstr, RTGCPHYS GCPhys pVmcbCtrl->TLBCtrl.n.u24Reserved = 0; pVmcbCtrl->IntCtrl.n.u6Reserved = 0; pVmcbCtrl->IntCtrl.n.u3Reserved = 0; - pVmcbCtrl->IntCtrl.n.u5Reserved = 0; + pVmcbCtrl->IntCtrl.n.u4Reserved = 0; pVmcbCtrl->IntCtrl.n.u24Reserved = 0; pVmcbCtrl->IntShadow.n.u30Reserved = 0; pVmcbCtrl->ExitIntInfo.n.u19Reserved = 0; @@ -516,6 +516,16 @@ static VBOXSTRICTRC iemSvmVmrun(PVMCPUCC pVCpu, uint8_t cbInstr, RTGCPHYS GCPhys pVmcbCtrl->IntCtrl.n.u1AvicEnable = 0; } + /* X2AVIC. */ + if ( pVmcbCtrl->IntCtrl.n.u1X2AvicEnable + && ( !pVmcbCtrl->IntCtrl.n.u1AvicEnable + || !pVM->cpum.ro.GuestFeatures.fSvmX2Avic + || !pVM->cpum.ro.GuestFeatures.fSvmAvic)) + { + Log(("iemSvmVmrun: X2AVIC not supported or trying to be enabled without XAVIC -> #VMEXIT\n")); + return iemSvmVmexit(pVCpu, SVM_EXIT_INVALID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */); + } + /* Last branch record (LBR) virtualization. */ if ( pVmcbCtrl->LbrVirt.n.u1LbrVirt && !pVM->cpum.ro.GuestFeatures.fSvmLbrVirt) diff --git a/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp index 9a03588e365..ec34c5d2611 100644 --- a/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp @@ -464,6 +464,39 @@ VMM_INT_DECL(VBOXSTRICTRC) PDMApicExportState(PVMCPUCC pVCpu) } +/** + * Updates the APIC state after a write to the APIC page by hardware. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param offApicReg The APIC register offset which was updated. + * + * @note This is a helper for AVIC/APICv when used on AMD or Intel. + */ +VMM_INT_DECL(VBOXSTRICTRC) PDMApicUpdateStateAfterWrite(PVMCPUCC pVCpu, uint16_t offApicReg) +{ + AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnUpdateStateAfterWrite, VERR_INVALID_POINTER); + return PDMCPU_TO_APICBACKEND(pVCpu)->pfnUpdateStateAfterWrite(pVCpu, offApicReg); +} + + +/** + * Sets the End-Of-Interrupt (EOI) register when the ISR is already known. + * + * This is an optimization that lets us avoid scanning the 256-bit sparse ISR + * register figuring out the highest pending in-service vector. + * + * @returns Strict VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param uVector The vector (highest ISR) for the attempted EOI. + */ +VMM_INT_DECL(VBOXSTRICTRC) PDMApicSetEoiFast(PVMCPUCC pVCpu, uint8_t uVector) +{ + AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetEoiFast, VERR_INVALID_POINTER); + return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetEoiFast(pVCpu, uVector); +} + + /** * Registers a PDM APIC backend. * @@ -506,6 +539,8 @@ VMM_INT_DECL(int) PDMApicRegisterBackend(PVMCC pVM, PDMAPICBACKENDTYPE enmBacken #elif defined(IN_RING0) AssertPtrReturn(pBackend->pfnGetApicPageForCpu, VERR_INVALID_POINTER); #endif + AssertPtrReturn(pBackend->pfnUpdateStateAfterWrite, VERR_INVALID_POINTER); + AssertPtrReturn(pBackend->pfnSetEoiFast, VERR_INVALID_POINTER); /* * Register the backend. diff --git a/src/VBox/VMM/VMMR0/target-x86/HMR0-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0-x86.cpp index 7ab41223acf..ac7ce3cff81 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0-x86.cpp @@ -861,6 +861,7 @@ static int hmR0EnableCpu(PVMCC pVM, RTCPUID idCpu) Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD)); pHostCpu->idCpu = idCpu; + pHostCpu->idApic = ASMGetApicId(); /* Do NOT reset cTlbFlushes here, see @bugref{6255}. */ int rc; diff --git a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp index 8b546b6bb96..6d8c98275ef 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp @@ -33,6 +33,7 @@ #define VMCPU_INCL_CPUM_GST_CTX #include #include +#include #include #include @@ -380,6 +381,8 @@ static FNSVMEXITHANDLER hmR0SvmExitSwInt; static FNSVMEXITHANDLER hmR0SvmExitTrRead; static FNSVMEXITHANDLER hmR0SvmExitTrWrite; static FNSVMEXITHANDLER hmR0SvmExitBusLock; +static FNSVMEXITHANDLER hmR0SvmExitAvicIncompleteIpi; +static FNSVMEXITHANDLER hmR0SvmExitAvicNoAccel; #ifdef VBOX_WITH_NESTED_HWVIRT_SVM static FNSVMEXITHANDLER hmR0SvmExitClgi; static FNSVMEXITHANDLER hmR0SvmExitStgi; @@ -425,6 +428,56 @@ static R0PTRTYPE(void *) g_pvIOBitmap; | HMSVM_LOG_GS \ | HMSVM_LOG_LBR) +/** A list of x2APIC MSRs we don't want to intercept when using the AVIC. */ +static const uint32_t g_aX2AvicMsrs[] = +{ + MSR_IA32_X2APIC_ID, + MSR_IA32_X2APIC_VERSION, + MSR_IA32_X2APIC_TPR, + MSR_IA32_X2APIC_PPR, + MSR_IA32_X2APIC_EOI, + MSR_IA32_X2APIC_LDR, + MSR_IA32_X2APIC_SVR, + MSR_IA32_X2APIC_ISR0, + MSR_IA32_X2APIC_ISR1, + MSR_IA32_X2APIC_ISR2, + MSR_IA32_X2APIC_ISR3, + MSR_IA32_X2APIC_ISR4, + MSR_IA32_X2APIC_ISR5, + MSR_IA32_X2APIC_ISR6, + MSR_IA32_X2APIC_ISR7, + MSR_IA32_X2APIC_TMR0, + MSR_IA32_X2APIC_TMR1, + MSR_IA32_X2APIC_TMR2, + MSR_IA32_X2APIC_TMR3, + MSR_IA32_X2APIC_TMR4, + MSR_IA32_X2APIC_TMR5, + MSR_IA32_X2APIC_TMR6, + MSR_IA32_X2APIC_TMR7, + MSR_IA32_X2APIC_IRR0, + MSR_IA32_X2APIC_IRR1, + MSR_IA32_X2APIC_IRR2, + MSR_IA32_X2APIC_IRR3, + MSR_IA32_X2APIC_IRR4, + MSR_IA32_X2APIC_IRR5, + MSR_IA32_X2APIC_IRR6, + MSR_IA32_X2APIC_IRR7, + MSR_IA32_X2APIC_ESR, + MSR_IA32_X2APIC_LVT_CMCI, + MSR_IA32_X2APIC_ICR, + MSR_IA32_X2APIC_LVT_TIMER, + MSR_IA32_X2APIC_LVT_THERMAL, + MSR_IA32_X2APIC_LVT_PERF, + MSR_IA32_X2APIC_LVT_LINT0, + MSR_IA32_X2APIC_LVT_LINT1, + MSR_IA32_X2APIC_LVT_ERROR, + MSR_IA32_X2APIC_TIMER_ICR, + MSR_IA32_X2APIC_TIMER_CCR, + MSR_IA32_X2APIC_TIMER_DCR, + MSR_IA32_X2APIC_SELF_IPI +}; + + /** * Dumps virtual CPU state and additional info. to the logger for diagnostics. * @@ -665,6 +718,14 @@ VMMR0DECL(void) SVMR0GlobalTerm(void) */ DECLINLINE(void) hmR0SvmFreeStructs(PVMCC pVM) { + if (pVM->hmr0.s.svm.hMemObjAvicHost != NIL_RTR0MEMOBJ) + { + RTR0MemObjFree(pVM->hmr0.s.svm.hMemObjAvicHost, false); + pVM->hmr0.s.svm.HCPhysAvicPhysIdTbl = 0; + pVM->hmr0.s.svm.HCPhysAvicLogicalIdTbl = 0; + pVM->hmr0.s.svm.hMemObjAvicHost = NIL_RTR0MEMOBJ; + } + for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) { PVMCPUCC pVCpu = VMCC_GET_CPU(pVM, idCpu); @@ -776,6 +837,8 @@ VMMR0DECL(int) SVMR0InitVM(PVMCC pVM) /* * Initialize the R0 memory objects up-front so we can properly cleanup on allocation failures. */ + pVM->hmr0.s.svm.hMemObjAvicHost = NIL_RTR0MEMOBJ; + for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) { PVMCPUCC pVCpu = VMCC_GET_CPU(pVM, idCpu); @@ -784,6 +847,23 @@ VMMR0DECL(int) SVMR0InitVM(PVMCC pVM) pVCpu->hmr0.s.svm.hMemObjMsrBitmap = NIL_RTR0MEMOBJ; } + /* + * Create the physical and logical APIC ID tables if AVIC is going to be used. + */ + void *pvAvicHost = NULL; + size_t const cbAvicPages = SVM_AVIC_PAGES << HOST_PAGE_SHIFT; + rc = RTR0MemObjAllocCont(&pVM->hmr0.s.svm.hMemObjAvicHost, cbAvicPages, + NIL_RTHCPHYS /*PhysHighest*/, false /* fExecutable */); + if (RT_FAILURE(rc)) + goto failure_cleanup; + + pvAvicHost = RTR0MemObjAddress(pVM->hmr0.s.svm.hMemObjAvicHost); + RT_BZERO(pvAvicHost, cbAvicPages); + pVM->hmr0.s.svm.HCPhysAvicPhysIdTbl = RTR0MemObjGetPagePhysAddr(pVM->hmr0.s.svm.hMemObjAvicHost, 0 /* iPage */); + pVM->hmr0.s.svm.HCPhysAvicLogicalIdTbl = RTR0MemObjGetPagePhysAddr(pVM->hmr0.s.svm.hMemObjAvicHost, 1 /* iPage */); + pVM->hmr0.s.svm.HCPhysApicAccess = RTR0MemObjGetPagePhysAddr(pVM->hmr0.s.svm.hMemObjAvicHost, 2 /* iPage */); + pVM->hmr0.s.svm.paAvicPhysIdTbl = (volatile uint64_t *)pvAvicHost; + for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++) { PVMCPUCC pVCpu = VMCC_GET_CPU(pVM, idCpu); @@ -1024,6 +1104,12 @@ VMMR0DECL(int) SVMR0SetupVM(PVMCC pVM) bool const fLbrVirt = RT_BOOL(g_fHmSvmFeatures & X86_CPUID_SVM_FEATURE_EDX_LBR_VIRT); bool const fUseLbrVirt = fLbrVirt && pVM->hm.s.svm.fLbrVirt; /** @todo IEM implementation etc. */ + bool const fAvic = RT_BOOL(g_fHmSvmFeatures & X86_CPUID_SVM_FEATURE_EDX_AVIC); + bool const fUseAvic = fAvic && pVM->hm.s.svm.fAvic && PDMHasApic(pVM); + + //bool const fX2Avic = RT_BOOL(g_fHmSvmFeatures & X86_CPUID_SVM_FEATURE_EDX_X2AVIC); + //bool const fUseX2Avic = fX2Avic && pVM->hm.s.svm.fAvic; + #ifdef VBOX_WITH_NESTED_HWVIRT_SVM bool const fVirtVmsaveVmload = RT_BOOL(g_fHmSvmFeatures & X86_CPUID_SVM_FEATURE_EDX_VIRT_VMSAVE_VMLOAD); bool const fUseVirtVmsaveVmload = fVirtVmsaveVmload && pVM->hm.s.svm.fVirtVmsaveVmload && fNestedPaging; @@ -1210,6 +1296,24 @@ VMMR0DECL(int) SVMR0SetupVM(PVMCC pVM) /* Initially all VMCB clean bits MBZ indicating that everything should be loaded from the VMCB in memory. */ Assert(pVmcbCtrl0->u32VmcbCleanBits == 0); + if (fUseAvic) + { + void *pvVirtApic = NULL; + RTHCPHYS HCPhysVirtApic = 0; + int rc = PDMR0ApicGetApicPageForCpu(pVCpu0, &HCPhysVirtApic, (PRTR0PTR)&pvVirtApic, NULL /*pR3Ptr*/); + AssertRCReturn(rc, rc); + + pVmcbCtrl0->AvicBackingPagePtr.u = HCPhysVirtApic; + pVmcbCtrl0->AvicLogicalTablePtr.u = pVM->hmr0.s.svm.HCPhysAvicLogicalIdTbl; + pVmcbCtrl0->AvicPhysicalTablePtr.u = pVM->hmr0.s.svm.HCPhysAvicPhysIdTbl | (pVM->cCpus - 1); + pVmcbCtrl0->IntCtrl.n.u1AvicEnable = 1; + + pVCpu0->hmr0.s.svm.u64PhysIdEntry = RT_BIT_64(63) | HCPhysVirtApic; + pVCpu0->hm.s.svm.fUseAvic = fUseAvic; + } + else + Assert(!pVCpu0->hm.s.svm.fUseAvic); + for (VMCPUID idCpu = 1; idCpu < pVM->cCpus; idCpu++) { PVMCPUCC pVCpuCur = VMCC_GET_CPU(pVM, idCpu); @@ -1232,6 +1336,26 @@ VMMR0DECL(int) SVMR0SetupVM(PVMCC pVM) Assert(pVCpuCur->hm.s.fGIMTrapXcptUD == pVCpu0->hm.s.fGIMTrapXcptUD); /* Same for GCM, #DE trapping should be uniform across VCPUs. */ Assert(pVCpuCur->hm.s.fGCMTrapXcptDE == pVCpu0->hm.s.fGCMTrapXcptDE); + + /* Update the per-VCPU/VMCB specific fields for the AVIC. */ + if (pVCpu0->hm.s.svm.fUseAvic) + { + void *pvVirtApic = NULL; + RTHCPHYS HCPhysVirtApic = 0; + int rc = PDMR0ApicGetApicPageForCpu(pVCpuCur, &HCPhysVirtApic, (PRTR0PTR)&pvVirtApic, NULL /*pR3Ptr*/); + AssertRCReturn(rc, rc); + + pVmcbCtrlCur->AvicBackingPagePtr.u = HCPhysVirtApic; + pVCpuCur->hmr0.s.svm.u64PhysIdEntry = RT_BIT_64(63) | HCPhysVirtApic; + pVCpuCur->hm.s.svm.fUseAvic = true; + + Assert(pVmcbCtrlCur->AvicLogicalTablePtr.u == pVmcb0->ctrl.AvicLogicalTablePtr.u); + Assert(pVmcbCtrlCur->AvicPhysicalTablePtr.u == pVmcb0->ctrl.AvicPhysicalTablePtr.u); + Assert(pVmcbCtrlCur->IntCtrl.n.u1AvicEnable == pVmcb0->ctrl.IntCtrl.n.u1AvicEnable); + Assert(pVCpuCur->hm.s.svm.fUseAvic == pVCpu0->hm.s.svm.fUseAvic); + } + else + Assert(!pVCpuCur->hm.s.svm.fUseAvic); } #ifdef VBOX_WITH_NESTED_HWVIRT_SVM @@ -2163,7 +2287,8 @@ static int hmR0SvmExportGuestApicTpr(PVMCPUCC pVCpu, PSVMVMCB pVmcb) { PVMCC pVM = pVCpu->CTX_SUFF(pVM); if ( PDMHasApic(pVM) - && PDMApicIsEnabled(pVCpu)) + && PDMApicIsEnabled(pVCpu) + && !pVCpu->hm.s.svm.fUseAvic) { bool fPendingIntr; uint8_t u8Tpr; @@ -3164,8 +3289,8 @@ static VBOXSTRICTRC hmR0SvmExitToRing3(PVMCPUCC pVCpu, VBOXSTRICTRC rcExit) /* Please, no longjumps here (any logging shouldn't flush jump back to ring-3). NO LOGGING BEFORE THIS POINT! */ VMMRZCallRing3Disable(pVCpu); - Log4Func(("rcExit=%d LocalFF=%#RX64 GlobalFF=%#RX32\n", VBOXSTRICTRC_VAL(rcExit), (uint64_t)pVCpu->fLocalForcedActions, - pVCpu->CTX_SUFF(pVM)->fGlobalForcedActions)); + Log4Func(("rcExit=%d LocalFF=%#RX64 GlobalFF=%#RX32 PicInterrupt=%RTbool\n", VBOXSTRICTRC_VAL(rcExit), (uint64_t)pVCpu->fLocalForcedActions, + pVCpu->CTX_SUFF(pVM)->fGlobalForcedActions, VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC))); /* We need to do this only while truly exiting the "inner loop" back to ring-3 and -not- for any longjmp to ring3. */ if (pVCpu->hm.s.Event.fPending) @@ -3677,11 +3802,11 @@ static VBOXSTRICTRC hmR0SvmEvaluatePendingEvent(PVMCPUCC pVCpu, PCSVMTRANSIENT p * * See AMD spec. 15.21.4 "Injecting Virtual (INTR) Interrupts". */ - else if ( VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC) - && !pVCpu->hm.s.fSingleInstruction) + if ( VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC) + && !pVCpu->hm.s.fSingleInstruction) { bool const fBlockInt = !pSvmTransient->fIsNestedGuest ? !(pCtx->eflags.u & X86_EFL_IF) - : CPUMIsGuestSvmPhysIntrEnabled(pVCpu, pCtx); + : CPUMIsGuestSvmPhysIntrEnabled(pVCpu, pCtx); if ( fGif && !fBlockInt && !fIntShadow) @@ -3694,28 +3819,49 @@ static VBOXSTRICTRC hmR0SvmEvaluatePendingEvent(PVMCPUCC pVCpu, PCSVMTRANSIENT p return IEMExecSvmVmexit(pVCpu, SVM_EXIT_INTR, 0, 0); } #endif - uint8_t u8Interrupt; - int rc = PDMGetInterrupt(pVCpu, &u8Interrupt); - if (RT_SUCCESS(rc)) - { - Log4(("Setting external interrupt %#x pending for injection\n", u8Interrupt)); - SVMEVENT Event; - Event.u = 0; - Event.n.u1Valid = 1; - Event.n.u8Vector = u8Interrupt; - Event.n.u3Type = SVM_EVENT_EXTERNAL_IRQ; - hmR0SvmSetPendingEvent(pVCpu, &Event, 0 /* GCPtrFaultAddress */); - } - else if (rc == VERR_APIC_INTR_MASKED_BY_TPR) + /* With the AVIC, we still need to deliver PIC style interrupts ourselves. */ + bool fGetInterrupt = true; + if (pVCpu->hm.s.svm.fUseAvic) { + if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC)) + fGetInterrupt = false; /* - * AMD-V has no TPR thresholding feature. TPR and the force-flag will be - * updated eventually when the TPR is written by the guest. + * We clear the interrupt flag here because we are certain that all + * conditions necessary for the AVIC hardware to deliver the interrupt + * are met. */ - STAM_COUNTER_INC(&pVCpu->hm.s.StatSwitchTprMaskedIrq); + if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC)) + VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); + } + if (fGetInterrupt) + { + uint8_t u8Interrupt; + int rc = PDMGetInterrupt(pVCpu, &u8Interrupt); + if (RT_SUCCESS(rc)) + { + Log4(("Setting external interrupt %#x pending for injection\n", u8Interrupt)); + SVMEVENT Event; + Event.u = 0; + Event.n.u1Valid = 1; + Event.n.u8Vector = u8Interrupt; + Event.n.u3Type = SVM_EVENT_EXTERNAL_IRQ; + hmR0SvmSetPendingEvent(pVCpu, &Event, 0 /* GCPtrFaultAddress */); + } + else if (rc == VERR_APIC_INTR_MASKED_BY_TPR) + { + /* + * AMD-V has no TPR thresholding feature. TPR and the force-flag will be + * updated eventually when the TPR is written by the guest. + */ + STAM_COUNTER_INC(&pVCpu->hm.s.StatSwitchTprMaskedIrq); + Log4(("External interrupt %#x masked by TPR\n", u8Interrupt)); + } + else + { + Log4(("PDMGetInterrupt failed. rc=%Rrc\n", rc)); + STAM_COUNTER_INC(&pVCpu->hm.s.StatSwitchGuestIrq); + } } - else - STAM_COUNTER_INC(&pVCpu->hm.s.StatSwitchGuestIrq); } else if (!fGif) hmR0SvmSetCtrlIntercept(pVmcb, SVM_CTRL_INTERCEPT_STGI); @@ -3871,7 +4017,9 @@ static void hmR0SvmReportWorldSwitchError(PVMCPUCC pVCpu, int rcVMRun) Log4(("ctrl.IntCtrl.u3Reserved %#x\n", pVmcb->ctrl.IntCtrl.n.u3Reserved)); Log4(("ctrl.IntCtrl.u1VIntrMasking %#x\n", pVmcb->ctrl.IntCtrl.n.u1VIntrMasking)); Log4(("ctrl.IntCtrl.u1VGifEnable %#x\n", pVmcb->ctrl.IntCtrl.n.u1VGifEnable)); - Log4(("ctrl.IntCtrl.u5Reserved1 %#x\n", pVmcb->ctrl.IntCtrl.n.u5Reserved)); + Log4(("ctrl.IntCtrl.u4Reserved4 %#x\n", pVmcb->ctrl.IntCtrl.n.u4Reserved)); + Log4(("ctrl.IntCtrl.u1X2AvicEnable %#x\n", pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable)); + Log4(("ctrl.IntCtrl.u1AvicEnable %#x\n", pVmcb->ctrl.IntCtrl.n.u1AvicEnable)); Log4(("ctrl.IntCtrl.u8VIntrVector %#x\n", pVmcb->ctrl.IntCtrl.n.u8VIntrVector)); Log4(("ctrl.IntCtrl.u24Reserved %#x\n", pVmcb->ctrl.IntCtrl.n.u24Reserved)); @@ -4065,6 +4213,47 @@ static VBOXSTRICTRC hmR0SvmCheckForceFlags(PVMCPUCC pVCpu) } +/** + * Unmaps the APIC-access page for virtualizing APIC accesses. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param GCPhysApicBase The guest-physical address of the APIC access page. + */ +static int hmR0SvmUnmapHCApicAccessPage(PVMCPUCC pVCpu, RTGCPHYS GCPhysApicBase) +{ + PVMCC pVM = pVCpu->CTX_SUFF(pVM); + Assert(GCPhysApicBase); + + Log4Func(("Unaliasing any existing mapping to the HC APIC-access page at %#RGp\n", GCPhysApicBase)); + return PGMHandlerPhysicalReset(pVM, GCPhysApicBase); +} + + +/** + * Map the APIC-access page for virtualizing APIC accesses. + * + * This can cause a longjumps to R3 due to the acquisition of the PGM lock. Hence, + * this not done as part of exporting guest state, see @bugref{8721}. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param GCPhysApicBase The guest-physical address of the APIC access page. + */ +static int hmR0SvmMapHCApicAccessPage(PVMCPUCC pVCpu, RTGCPHYS GCPhysApicBase) +{ + PVMCC pVM = pVCpu->CTX_SUFF(pVM); + Assert(GCPhysApicBase); + + Log4Func(("Mapping HC APIC-access page at %#RGp\n", GCPhysApicBase)); + + int const rc = IOMR0MmioMapMmioHCPage(pVM, pVCpu, GCPhysApicBase, pVM->hmr0.s.svm.HCPhysApicAccess, + X86_PTE_RW | X86_PTE_P); + AssertRCReturn(rc, rc); + return VINF_SUCCESS; +} + + /** * Does the preparations before executing guest code in AMD-V. * @@ -4132,6 +4321,57 @@ static VBOXSTRICTRC hmR0SvmPreRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_ALL_GUEST); #endif + /* + * Setup the AVIC state if enabled. + */ + if (pVCpu->hm.s.svm.fUseAvic) + { + Assert(PDMHasApic(pVM)); + + /* Get the APIC base MSR from the virtual APIC device. */ + uint64_t const uApicBaseMsr = PDMApicGetBaseMsrNoCheck(pVCpu); + Assert( MSR_IA32_APICBASE_GET_ADDR(uApicBaseMsr) == MSR_IA32_APICBASE_ADDR + || !(uApicBaseMsr & MSR_IA32_APICBASE_EN)); + if (uApicBaseMsr != pVCpu->hm.s.svm.u64GstMsrApicBase) + { + PSVMVMCB pVmcb = pVCpu->hmr0.s.svm.pVmcb; + + /* Unalias any existing mapping. */ + RTGCPHYS const GCPhysApic = uApicBaseMsr & ~(RTGCPHYS)GUEST_PAGE_OFFSET_MASK; + hmR0SvmUnmapHCApicAccessPage(pVCpu, GCPhysApic); + Log4(("Unaliasing any previous AVIC backing page mappings at %#RGp\n", GCPhysApic)); + + /* XAPIC. */ + if (uApicBaseMsr & MSR_IA32_APICBASE_EN) + { + rc = hmR0SvmMapHCApicAccessPage(pVCpu, GCPhysApic); + AssertRCReturn(rc, rc); + Log4(("Mapped AVIC backing page at %#RGp\n", GCPhysApic)); + } + + /* X2APIC. */ + { + /* If enabled don't intercept X2APIC MSRs as the intercept has higher priority than the AVIC hardware. */ + bool const fX2AvicEnable = (uApicBaseMsr & (MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD)) + == (MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD); + SVMMSREXITREAD const fRdPerm = fX2AvicEnable ? SVMMSREXIT_PASSTHRU_READ : SVMMSREXIT_INTERCEPT_READ; + SVMMSREXITWRITE const fWrPerm = fX2AvicEnable ? SVMMSREXIT_PASSTHRU_WRITE : SVMMSREXIT_INTERCEPT_WRITE; + + uint8_t *pbMsrBitmap = (uint8_t *)pVCpu->hmr0.s.svm.pvMsrBitmap; + for (uint32_t i = 0; i < RT_ELEMENTS(g_aX2AvicMsrs); i++) + hmR0SvmSetMsrPermission(pVCpu, pbMsrBitmap, g_aX2AvicMsrs[i], fRdPerm, fWrPerm); + pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable = fX2AvicEnable; + } + + pVmcb->ctrl.AvicBar.u = MSR_IA32_APICBASE_GET_ADDR(uApicBaseMsr); + pVmcb->ctrl.u32VmcbCleanBits &= ~HMSVM_VMCB_CLEAN_AVIC; + /** @todo Do we need to flush the TLB with SVM_TLB_FLUSH_SINGLE_CONTEXT here? */ + + /* Update the per-VCPU cache of the APIC base MSR corresponding to the mapped APIC access page. */ + pVCpu->hm.s.svm.u64GstMsrApicBase = uApicBaseMsr; + } + } + #ifdef VBOX_WITH_NESTED_HWVIRT_SVM /* * Set up the nested-guest VMCB for execution using hardware-assisted SVM. @@ -4269,6 +4509,15 @@ static void hmR0SvmPreRunGuestCommitted(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransi pSvmTransient->fWasGuestDebugStateActive = CPUMIsGuestDebugStateActive(pVCpu); pSvmTransient->fWasHyperDebugStateActive = CPUMIsHyperDebugStateActive(pVCpu); + /* Set AVIC state. */ + if ( pVCpu->hm.s.svm.fUseAvic + && PDMHasApic(pVM)) /** @todo Check where we can merge the PDMHasApic() call into fUseAvic. */ + { + ASMAtomicUoWriteU64(&pVM->hmr0.s.svm.paAvicPhysIdTbl[pVCpu->idCpu], pVCpu->hmr0.s.svm.u64PhysIdEntry /*| RT_BIT_64(62) | pHostCpu->idApic*/); + if (fMigratedHostCpu) + pVCpu->hmr0.s.fForceTLBFlush = true; /* Need to flush the TLB with SVM_TLB_FLUSH_SINGLE_CONTEXT */ + } + #ifdef VBOX_WITH_NESTED_HWVIRT_SVM uint8_t *pbMsrBitmap; if (!pSvmTransient->fIsNestedGuest) @@ -4403,6 +4652,19 @@ static void hmR0SvmPostRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransient, VBO ASMSetFlags(pSvmTransient->fEFlags); /* Enable interrupts. */ VMMRZCallRing3Enable(pVCpu); /* It is now safe to do longjmps to ring-3!!! */ + /* Set AVIC state. */ + if ( pVCpu->hm.s.svm.fUseAvic + && PDMHasApic(pVM)) /** @todo Check where we can merge the PDMHasApic() call into fUseAvic. */ + { + ASMAtomicUoWriteU64(&pVM->hmr0.s.svm.paAvicPhysIdTbl[pVCpu->idCpu], pVCpu->hmr0.s.svm.u64PhysIdEntry); /* Clear IsRunning bit. */ + /* + * It's possible VMCPU_FF_INTERRUPT_APIC might be pending here if interrupts were seen as disabled + * (or interrupt shadow was active) in hmR0SvmEvaluatePendingEvent. There is no easy way to detect + * if the interrupt was delivered by the AVIC hardware. Leaving the force-flag pending here should + * be fine (an extra check the next time around, mostly harmless). + */ + } + /* If VMRUN failed, we can bail out early. This does -not- cover SVM_EXIT_INVALID. */ if (RT_UNLIKELY(rcVMRun != VINF_SUCCESS)) { @@ -4496,6 +4758,7 @@ static VBOXSTRICTRC hmR0SvmRunGuestCodeNormal(PVMCPUCC pVCpu, uint32_t *pcLoops) SvmTransient.fUpdateTscOffsetting = true; SvmTransient.pVmcb = pVCpu->hmr0.s.svm.pVmcb; + Log2Func(("\n")); VBOXSTRICTRC rc = VERR_INTERNAL_ERROR_5; for (;;) { @@ -5403,6 +5666,8 @@ static VBOXSTRICTRC hmR0SvmHandleExit(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransien case SVM_EXIT_XSETBV: VMEXIT_CALL_RET(0, hmR0SvmExitXsetbv(pVCpu, pSvmTransient)); case SVM_EXIT_FERR_FREEZE: VMEXIT_CALL_RET(0, hmR0SvmExitFerrFreeze(pVCpu, pSvmTransient)); case SVM_EXIT_BUSLOCK: VMEXIT_CALL_RET(0, hmR0SvmExitBusLock(pVCpu, pSvmTransient)); + case SVM_EXIT_AVIC_INCOMPLETE_IPI: VMEXIT_CALL_RET(0, hmR0SvmExitAvicIncompleteIpi(pVCpu, pSvmTransient)); + case SVM_EXIT_AVIC_NOACCEL: VMEXIT_CALL_RET(0, hmR0SvmExitAvicNoAccel(pVCpu, pSvmTransient)); default: { @@ -9025,6 +9290,124 @@ HMSVM_EXIT_DECL hmR0SvmExitBusLock(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransient) } +/** + * \#VMEXIT handler for AVIC incomplete IPI delivery operations due to a halting vCPU. + * Conditional \#VMEXIT. + */ +HMSVM_EXIT_DECL hmR0SvmExitAvicIncompleteIpi(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransient) +{ + HMSVM_VALIDATE_EXIT_HANDLER_PARAMS(pVCpu, pSvmTransient); + STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatSvmExitAvicIncompleteIpi); + + uint64_t const u64ExitInfo1 = pSvmTransient->pVmcb->ctrl.u64ExitInfo1; + uint32_t const u32ApicIcrLo = RT_LO_U32(u64ExitInfo1); + uint32_t const u32ApicIcrHi = RT_HI_U32(u64ExitInfo1); + + uint64_t const u64ExitInfo2 = pSvmTransient->pVmcb->ctrl.u64ExitInfo2; + uint32_t const idApic = (uint32_t)(u64ExitInfo2 & SVM_EXIT2_INC_IPI_INDEX_MASK); + uint32_t const idFailure = (uint32_t)(u64ExitInfo2 >> SVM_EXIT2_INC_IPI_ID_SHIFT); + + Log2Func(("\n")); + Log4Func(("AVICExitIncompleteIpi/%u: ICRL=%#x ICRH=%#x idApic=%#x idFailure=%#x\n", + pVCpu->idCpu, u32ApicIcrLo, u32ApicIcrHi, idApic, idFailure)); + if ( idFailure == SVM_EXIT2_INC_IPI_INDEX_INVALID_INTR_TYPE + || idFailure == SVM_EXIT2_INC_IPI_INDEX_INVALID_TARGET) + { + pVCpu->hm.s.offApicReg = XAPIC_OFF_ICR_LO; + return PDMApicUpdateStateAfterWrite(pVCpu, XAPIC_OFF_ICR_LO); + } + + if (idFailure == SVM_EXIT2_INC_IPI_INDEX_TARGET_NOT_RUNNING) + { + PVMCC pVM = pVCpu->CTX_SUFF(pVM); + PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idApic]; + VMCPUID idCpu = pVCpuDst->idCpu; + if (VMMGetCpuId(pVM) != idCpu) + { + switch (VMCPU_GET_STATE(pVCpuDst)) + { + case VMCPUSTATE_STARTED_EXEC: + Log7Func(("idCpu=%u VMCPUSTATE_STARTED_EXEC\n", idCpu)); + GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu); + break; + + case VMCPUSTATE_STARTED_HALTED: + Log7Func(("idCpu=%u VMCPUSTATE_STARTED_HALTED\n", idCpu)); + VMCPU_FF_SET(pVCpuDst, VMCPU_FF_UNHALT); + GVMMR0SchedWakeUpNoGVMNoLock(pVM, idCpu); + break; + + default: + Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState)); + break; /* nothing to do in other states. */ + } + } + return VINF_SUCCESS; + } + + AssertMsgFailed(("hmR0SvmExitAvicIncompleteIpi: Unexpected failure type %#x\n", idFailure)); + return VERR_SVM_IPE_4; +} + + +/** + * \#VMEXIT handler for AVIC no acceleration operations. + * Conditional \#VMEXIT. + */ +HMSVM_EXIT_DECL hmR0SvmExitAvicNoAccel(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransient) +{ + HMSVM_VALIDATE_EXIT_HANDLER_PARAMS(pVCpu, pSvmTransient); + STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatSvmExitAvicNoAccel); + + uint16_t const offApicReg = pSvmTransient->pVmcb->ctrl.u64ExitInfo1 & 0xfff; + bool const fWr = RT_BOOL(pSvmTransient->pVmcb->ctrl.u64ExitInfo1 & RT_BIT_64(32)); + + /* + * Determine whether the access is fault or trap like. + * Trap like exits have the value updated in the APIC page already + * while fault like exits need emulating the instruction. + * + * Table 15-22 in chapter 15.29.3.1 (40332_4.09_APM_PUB.pdf) gives an overview. + */ + switch (offApicReg) + { + case XAPIC_OFF_ID: + case XAPIC_OFF_RRD: + case XAPIC_OFF_LDR: + case XAPIC_OFF_DFR: + case XAPIC_OFF_SVR: + case XAPIC_OFF_ESR: + case XAPIC_OFF_ICR_LO: + case XAPIC_OFF_LVT_TIMER: + case XAPIC_OFF_LVT_THERMAL: + case XAPIC_OFF_LVT_PERF: + case XAPIC_OFF_LVT_LINT0: + case XAPIC_OFF_LVT_LINT1: + case XAPIC_OFF_LVT_ERROR: + case XAPIC_OFF_TIMER_ICR: + case XAPIC_OFF_TIMER_DCR: + /** @todo Extended Interrupt Local Vector Table Registers when we start supporting it. */ + Assert(fWr); + break; + + case XAPIC_OFF_EOI: + { + uint8_t const uVector = pSvmTransient->pVmcb->ctrl.u64ExitInfo2 & 0xff; + return PDMApicSetEoiFast(pVCpu, uVector); + } + + default: + /* These accesses fault -> emulate completely and be done with it. */ + /** @todo Check whether we can speed things up with EMHistoryExec... */ + return hmR0SvmExitInterpretInstruction(pVCpu, pSvmTransient, HMSVM_CPUMCTX_EXTRN_ALL, HM_CHANGED_ALL_GUEST); + } + + Log2(("AVICNoAccelExit/%u: Trapping offApicReg=%#x\n", pVCpu->idCpu, offApicReg)); + pVCpu->hm.s.offApicReg = offApicReg; + return PDMApicUpdateStateAfterWrite(pVCpu, offApicReg); +} + + #ifdef VBOX_WITH_NESTED_HWVIRT_SVM /** * \#VMEXIT handler for CLGI (SVM_EXIT_CLGI). Conditional \#VMEXIT. diff --git a/src/VBox/VMM/VMMR3/EMR3.cpp b/src/VBox/VMM/VMMR3/EMR3.cpp index f36adaa26bc..1b441abc296 100644 --- a/src/VBox/VMM/VMMR3/EMR3.cpp +++ b/src/VBox/VMM/VMMR3/EMR3.cpp @@ -1970,9 +1970,9 @@ int emR3ForcedActions(PVM pVM, PVMCPU pVCpu, int rc) rc2 = emR3SvmNstGstIntrIntercept(pVCpu); else rc2 = VINF_NO_CHANGE; - if (rc2 == VINF_NO_CHANGE) { +#if 1 bool fInjected = false; CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_XCPT_MASK); /** @todo this really isn't nice, should properly handle this */ @@ -1989,6 +1989,9 @@ int emR3ForcedActions(PVM pVM, PVMCPU pVCpu, int rc) if (fInjected) rcIrq = rc2; # endif +#else + rc2 = VINF_EM_RESCHEDULE; +#endif } UPDATE_RC(); } diff --git a/src/VBox/VMM/VMMR3/EMR3HM.cpp b/src/VBox/VMM/VMMR3/EMR3HM.cpp index dc5631a720c..94e7a750e1d 100644 --- a/src/VBox/VMM/VMMR3/EMR3HM.cpp +++ b/src/VBox/VMM/VMMR3/EMR3HM.cpp @@ -43,10 +43,12 @@ #include #include #include +#include #include #include #include #include "EMInternal.h" +#include "HMInternal.h" #include #include #include diff --git a/src/VBox/VMM/VMMR3/TRPMR3.cpp b/src/VBox/VMM/VMMR3/TRPMR3.cpp index ec482f61024..88bd809513f 100644 --- a/src/VBox/VMM/VMMR3/TRPMR3.cpp +++ b/src/VBox/VMM/VMMR3/TRPMR3.cpp @@ -439,7 +439,8 @@ VMMR3DECL(int) TRPMR3InjectEvent(PVM pVM, PVMCPU pVCpu, TRPMEVENT enmEvent, bool else { /* Can happen if the interrupt is masked by TPR or APIC is disabled. */ - AssertMsg(rc == VERR_APIC_INTR_MASKED_BY_TPR || rc == VERR_NO_DATA, ("PDMGetInterrupt failed. rc=%Rrc\n", rc)); + AssertMsg(rc == VERR_APIC_INTR_MASKED_BY_TPR || rc == VERR_NO_DATA || rc == VERR_APIC_INTR_DEFER, + ("PDMGetInterrupt failed. rc=%Rrc\n", rc)); } # if 0 /* HMR3IsActive is not reliable (esp. after restore), just return VINF_EM_RESCHEDULE. */ return HMR3IsActive(pVCpu) ? VINF_EM_RESCHEDULE_HM diff --git a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp index f0c29d3aadd..31d8820edc2 100644 --- a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp +++ b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp @@ -912,9 +912,10 @@ DECLCALLBACK(int) apicR3InitComplete(PPDMDEVINS pDevIns) pApic->fSupportsTscDeadline = RT_BOOL(CpuLeaf.uEcx & X86_CPUID_FEATURE_ECX_TSCDEADL); pApic->fPostedIntrsEnabled = HMR3IsPostedIntrsEnabled(pVM->pUVM); pApic->fVirtApicRegsEnabled = HMR3AreVirtApicRegsEnabled(pVM->pUVM); + pApic->fAvicEnabled = HMR3IsAvicEnabled(pVM->pUVM); - LogRel(("APIC: fPostedIntrsEnabled=%RTbool fVirtApicRegsEnabled=%RTbool fSupportsTscDeadline=%RTbool\n", - pApic->fPostedIntrsEnabled, pApic->fVirtApicRegsEnabled, pApic->fSupportsTscDeadline)); + LogRel(("APIC: fPostedIntrsEnabled=%RTbool fVirtApicRegsEnabled=%RTbool fSupportsTscDeadline=%RTbool fAvicEnabled=%RTbool\n", + pApic->fPostedIntrsEnabled, pApic->fVirtApicRegsEnabled, pApic->fSupportsTscDeadline, pApic->fAvicEnabled)); return VINF_SUCCESS; } @@ -1094,6 +1095,7 @@ DECLCALLBACK(int) apicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE p APIC_REG_COUNTER(&pApicCpu->StatTprWrite, "%u/TprWrite", "Number of TPR writes."); APIC_REG_COUNTER(&pApicCpu->StatTprRead, "%u/TprRead", "Number of TPR reads."); APIC_REG_COUNTER(&pApicCpu->StatEoiWrite, "%u/EoiWrite", "Number of EOI writes."); + APIC_REG_COUNTER(&pApicCpu->StatEoiWriteFast, "%u/EoiWriteFast", "Number of EOI writes using the fast path."); APIC_REG_COUNTER(&pApicCpu->StatMaskedByTpr, "%u/MaskedByTpr", "Number of times TPR masks an interrupt in apicGetInterrupt."); APIC_REG_COUNTER(&pApicCpu->StatMaskedByPpr, "%u/MaskedByPpr", "Number of times PPR masks an interrupt in apicGetInterrupt."); APIC_REG_COUNTER(&pApicCpu->StatTimerIcrWrite, "%u/TimerIcrWrite", "Number of times the timer ICR is written."); diff --git a/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp index ead02c27cd9..73547ae8357 100644 --- a/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp +++ b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp @@ -281,6 +281,7 @@ VMMR3_INT_DECL(int) HMR3Init(PVM pVM) "|SvmPauseFilterThreshold" "|SvmVirtVmsaveVmload" "|SvmVGif" + "|SvmAvic" "|LovelyMesaDrvWorkaround" "|MissingOS2TlbFlushWorkaround" "|AlwaysInterceptVmxMovDRx" @@ -425,6 +426,12 @@ VMMR3_INT_DECL(int) HMR3Init(PVM pVM) rc = CFGMR3QueryBoolDef(pCfgHm, "SvmLbrVirt", &pVM->hm.s.svm.fLbrVirt, false); AssertRCReturn(rc, rc); + /** @cfgm{/HM/SvmAvic, bool, false} + * Whether to make use of the AVIC virtualization feature of the CPU if it's + * available. */ + rc = CFGMR3QueryBoolDef(pCfgHm, "SvmAvic", &pVM->hm.s.svm.fAvic, false); + AssertRCReturn(rc, rc); + /** @cfgm{/HM/Exclusive, bool} * Determines the init method for AMD-V and VT-x. If set to true, HM will do a * global init for each host CPU. If false, we do local init each time we wish @@ -991,6 +998,11 @@ static int hmR3InitFinalizeR3(PVM pVM) HM_REG_COUNTER(&pHmCpu->StatVmxPreemptionRecalcingDeadline, "/HM/CPU%u/PreemptTimer/RecalcingDeadline", "VMX-preemption timer arming logic recalculating the deadline (slightly expensive)"); HM_REG_COUNTER(&pHmCpu->StatVmxPreemptionRecalcingDeadlineExpired, "/HM/CPU%u/PreemptTimer/RecalcingDeadlineExpired", "VMX-preemption timer arming logic found recalculated deadline expired (ignored)"); } + else + { + HM_REG_COUNTER(&pHmCpu->StatSvmExitAvicIncompleteIpi, "/HM/CPU%u/AVIC/IncompleteIpi", "VM exit due to an incomplete IPI."); + HM_REG_COUNTER(&pHmCpu->StatSvmExitAvicNoAccel, "/HM/CPU%u/AVIC/UnAcceleratedAccess", "VM exit due to an unaccelerated APIC access"); + } #ifdef VBOX_WITH_STATISTICS /* * Guest Exit reason stats. @@ -2061,6 +2073,9 @@ static int hmR3InitFinalizeR0Amd(PVM pVM) if (pVM->hm.s.fPostedIntrs) LogRel(("HM: Enabled posted-interrupt processing support\n")); + if (pVM->apCpusR3[0]->hm.s.svm.fUseAvic) + LogRel(("HM: Enabled AVIC support\n")); + hmR3DisableRawMode(pVM); LogRel((pVM->hm.s.fTprPatchingAllowed ? "HM: Enabled TPR patching\n" @@ -2946,6 +2961,26 @@ VMMR3DECL(bool) HMR3IsPostedIntrsEnabled(PUVM pUVM) } +/** + * Checks if the SVM AVIC feature is enabled. + * + * This returns whether the APIC should only deliver PIC-style interrupts + * to the guest while all other interrupts are updated to the APIC page + * using the posted-interrupt bitmap. + * + * @returns @c true if SVM AVIC feature is enabled, @c false otherwise. + * @param pUVM The user mode VM handle. + */ +VMMR3DECL(bool) HMR3IsAvicEnabled(PUVM pUVM) +{ + UVM_ASSERT_VALID_EXT_RETURN(pUVM, false); + PVM pVM = pUVM->pVM; + VM_ASSERT_VALID_EXT_RETURN(pVM, false); + PCVMCPU pVCpu0 = pVM->apCpusR3[0]; + return pVCpu0->hm.s.svm.fUseAvic; +} + + /** * Checks if we are currently using VPID in VT-x mode. * diff --git a/src/VBox/VMM/include/APICInternal.h b/src/VBox/VMM/include/APICInternal.h index b92029da3dc..9bb6642f2a7 100644 --- a/src/VBox/VMM/include/APICInternal.h +++ b/src/VBox/VMM/include/APICInternal.h @@ -195,6 +195,8 @@ typedef struct APIC bool fVirtApicRegsEnabled; /** Whether posted-interrupt processing is enabled. */ bool fPostedIntrsEnabled; + /** Whether the SVM AVIC feature is enabled. */ + bool fAvicEnabled; /** Whether TSC-deadline timer mode is supported for the guest. */ bool fSupportsTscDeadline; /** Whether this VM has an IO-APIC. */ @@ -211,6 +213,8 @@ typedef struct APIC * kernel load area and macOS kernel selector value (8), as we must not ever * apply this to the EFI code. */ bool fMacOSWorkaround; + /** Alignment padding. */ + bool afPadding[3]; /** The max supported APIC mode from CFGM. */ PDMAPICMODE enmMaxMode; /** @} */ @@ -336,6 +340,8 @@ typedef struct APICCPU STAMCOUNTER StatTprRead; /** Number of times the EOI is written. */ STAMCOUNTER StatEoiWrite; + /** Number of times the EOI is written in the fast path. */ + STAMCOUNTER StatEoiWriteFast; /** Number of times TPR masks an interrupt in apicGetInterrupt(). */ STAMCOUNTER StatMaskedByTpr; /** Number of times PPR masks an interrupt in apicGetInterrupt(). */ diff --git a/src/VBox/VMM/include/EMHandleRCTmpl.h b/src/VBox/VMM/include/EMHandleRCTmpl.h index 4bd10840ff9..408d4f0a674 100644 --- a/src/VBox/VMM/include/EMHandleRCTmpl.h +++ b/src/VBox/VMM/include/EMHandleRCTmpl.h @@ -151,6 +151,10 @@ int emR3NemHandleRC(PVM pVM, PVMCPU pVCpu, int rc) case VINF_EM_HM_PATCH_TPR_INSTR: rc = HMR3PatchTprInstr(pVM, pVCpu); break; + + case VINF_APIC_R3_UPDATE_STATE: + rc = VBOXSTRICTRC_TODO(PDMApicUpdateStateAfterWrite(pVCpu, pVCpu->hm.s.offApicReg)); + break; #endif case VINF_EM_RAW_GUEST_TRAP: diff --git a/src/VBox/VMM/include/HMInternal.h b/src/VBox/VMM/include/HMInternal.h index f5d40a6aab7..212ec34cb37 100644 --- a/src/VBox/VMM/include/HMInternal.h +++ b/src/VBox/VMM/include/HMInternal.h @@ -134,6 +134,8 @@ typedef struct HMPHYSCPU bool fVmxeAlreadyEnabled; /** In use by our code. (for power suspend) */ bool volatile fInUse; + /** The APIC ID of the physical CPU associated with this entry. */ + uint8_t idApic; #ifdef VBOX_WITH_NESTED_HWVIRT_SVM /** Nested-guest union (put data common to SVM/VMX outside the union). */ union @@ -321,7 +323,9 @@ typedef struct HM bool fVGif; /** Whether to use LBR virtualization feature. */ bool fLbrVirt; - bool afAlignment1[2]; + /** Whether to use the AVIC feature if available. */ + bool fAvic; + bool fAlignment1; /** Pause filter counter. */ uint16_t cPauseFilter; @@ -520,6 +524,16 @@ typedef struct HMR0PERVM { /** Set if erratum 170 affects the AMD cpu. */ bool fAlwaysFlushTLB; + /** Ring-0 memory object for per-VM SVM AVIC structures. */ + RTR0MEMOBJ hMemObjAvicHost; + /** Host physical address of the physical APIC ID table. */ + RTHCPHYS HCPhysAvicPhysIdTbl; + /** Host physical address of the logical APIC ID table. */ + RTHCPHYS HCPhysAvicLogicalIdTbl; + /** Host-physical address of the APIC-access page. */ + RTHCPHYS HCPhysApicAccess; + /** R0 pointer to the physical APIC ID table. */ + volatile uint64_t *paAvicPhysIdTbl; } svm; /** VT-x specific data. */ @@ -844,11 +858,16 @@ typedef struct HMCPU * long-mode and to intercept reads and writes to the SYSENTER MSRs in order to * preserve the upper 32 bits written to them (AMD will ignore and discard). */ bool fEmulateLongModeSysEnterExit; - uint8_t au8Alignment0[7]; + /** Flag whether to utilize AVIC hardware. */ + bool fUseAvic; + uint8_t au8Alignment0[6]; /** Cache of the nested-guest's VMCB fields that we modify in order to run the * nested-guest using AMD-V. This will be restored on \#VMEXIT. */ SVMNESTEDVMCBCACHE NstGstVmcbCache; + + /** Cached guest APIC-base MSR for identifying when to enable the AVIC if supported. */ + uint64_t u64GstMsrApicBase; } svm; /** Event injection state. */ @@ -857,7 +876,9 @@ typedef struct HMCPU /** Current shadow paging mode for updating CR4. * @todo move later (@bugref{9217}). */ PGMMODE enmShadowMode; - uint32_t u32TemporaryPadding; + uint16_t u16TemporaryPadding; + /** The APIC register offset from an unaccelerated write causing the return to R3. */ + uint16_t offApicReg; /** The PAE PDPEs used with Nested Paging (only valid when * VMCPU_FF_HM_UPDATE_PAE_PDPES is set). */ @@ -1016,6 +1037,9 @@ typedef struct HMCPU STAMCOUNTER StatVmxPreemptionReusingDeadline; STAMCOUNTER StatVmxPreemptionReusingDeadlineExpired; + STAMCOUNTER StatSvmExitAvicIncompleteIpi; + STAMCOUNTER StatSvmExitAvicNoAccel; + #ifdef VBOX_WITH_STATISTICS STAMCOUNTER aStatExitReason[MAX_EXITREASON_STAT]; STAMCOUNTER aStatNestedExitReason[MAX_EXITREASON_STAT]; @@ -1162,6 +1186,9 @@ typedef struct HMR0PERVCPU /** Host's TSC_AUX MSR (used when RDTSCP doesn't cause VM-exits). */ uint64_t u64HostTscAux; + /* The AVIC physical ID entry for this vCPU. */ + uint64_t u64PhysIdEntry; + /** For saving stack space, the disassembler state is allocated here * instead of on the stack. */ DISSTATE Dis; @@ -1173,7 +1200,6 @@ AssertCompileMemberAlignment(HMR0PERVCPU, cWorldSwitchExits, 4); AssertCompileMemberAlignment(HMR0PERVCPU, fForceTLBFlush, 4); AssertCompileMemberAlignment(HMR0PERVCPU, vmx.RestoreHost, 8); - /** @name HM_WSF_XXX - @bugref{9453}, @bugref{9087} * @note If you change these values don't forget to update the assembly * defines as well! diff --git a/src/VBox/VMM/include/HMInternal.mac b/src/VBox/VMM/include/HMInternal.mac index a651083a5ac..eb8ebeb2c12 100644 --- a/src/VBox/VMM/include/HMInternal.mac +++ b/src/VBox/VMM/include/HMInternal.mac @@ -151,9 +151,11 @@ endstruc struc HMCPUSVM .fEmulateLongModeSysEnterExit resb 1 + .fUseAvic resb 1 alignb 8 .NstGstVmcbCache resb 40 + .u64GstMsrApicBase resq 1 endstruc struc HMCPU @@ -241,6 +243,7 @@ struc HMR0CPUSVM alignb 8 .pSvmTransient RTR0PTR_RES 1 .u64HostTscAux resq 1 + .u64PhysIdEntry resq 1 alignb 8 .Dis resb 0d8h