From 615797c34bd8da9e84bd2fabb02a5454834bfa88 Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Wed, 22 Jul 2026 20:30:40 +0530 Subject: [PATCH 1/9] VMM: SVM AVIC work-in-progress, continuing work done by Alexander Eichner on the SVM AVIC implementation. --- include/VBox/err.h | 2 + include/VBox/vmm/hm_svm.h | 5 +- include/VBox/vmm/pdmapic.h | 27 +- include/iprt/x86.h | 2 + .../VMM/VMMAll/target-x86/APICAll-x86.cpp | 36 +- .../target-x86/IEMAllCImplSvmInstr-x86.cpp | 12 +- .../VMM/VMMAll/target-x86/PDMAllApic-x86.cpp | 17 + src/VBox/VMM/VMMR0/target-x86/HMR0-x86.cpp | 1 + src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp | 410 ++++++++++++++++-- src/VBox/VMM/VMMR3/EMR3.cpp | 5 +- src/VBox/VMM/VMMR3/EMR3HM.cpp | 2 + src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp | 15 + src/VBox/VMM/include/EMHandleRCTmpl.h | 4 + src/VBox/VMM/include/HMInternal.h | 34 +- src/VBox/VMM/include/HMInternal.mac | 3 + 15 files changed, 535 insertions(+), 40 deletions(-) diff --git a/include/VBox/err.h b/include/VBox/err.h index 069dd188ab8b..da36788687f3 100644 --- a/include/VBox/err.h +++ b/include/VBox/err.h @@ -3143,6 +3143,8 @@ #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 /** @} */ /** @name NEM Status Codes diff --git a/include/VBox/vmm/hm_svm.h b/include/VBox/vmm/hm_svm.h index 95d5bf59689e..e103aefa3e13 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 /** @} */ /* @@ -627,7 +629,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 feacab90e3b0..62267fd32db8 100644 --- a/include/VBox/vmm/pdmapic.h +++ b/include/VBox/vmm/pdmapic.h @@ -362,6 +362,17 @@ 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)); + + /** @name Reserved for future (MBZ). * @{ */ DECLR3CALLBACKMEMBER(int, pfnReserved0, (void)); @@ -371,7 +382,6 @@ typedef struct PDMAPICBACKENDR3 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 +634,21 @@ 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)); + /** @name Reserved for future (MBZ). * @{ */ DECLR0CALLBACKMEMBER(int, pfnReserved0, (void)); @@ -637,7 +658,6 @@ typedef struct PDMAPICBACKENDR0 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 +966,7 @@ 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); #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 10838f503bdc..5bc885268730 100644 --- a/include/iprt/x86.h +++ b/include/iprt/x86.h @@ -2392,6 +2392,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/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index b232215a150a..815484e7a819 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 @@ -1428,7 +1428,7 @@ DECLINLINE(VBOXSTRICTRC) apicWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, u { VMCPU_ASSERT_EMT(pVCpu); Assert(offReg <= XAPIC_OFF_MAX_VALID); - Assert(!XAPIC_IN_X2APIC_MODE(pVCpu->apic.s.uApicBaseMsr)); + //Assert(!XAPIC_IN_X2APIC_MODE(pVCpu->apic.s.uApicBaseMsr)); VBOXSTRICTRC rcStrict = VINF_SUCCESS; switch (offReg) @@ -2835,7 +2835,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 +2949,35 @@ 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))); + + PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu); + uint32_t u32Value = 0; + + VBOXSTRICTRC rcStrict = apicReadRegister(pDevIns, pVCpu, offApicReg, &u32Value); + if (rcStrict == VINF_SUCCESS) + { + /* 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. */ + 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 +3112,6 @@ const PDMAPICBACKEND g_ApicBackend = #endif /* .pfnImportState = */ apicImportState, /* .pfnExportState = */ apicExportState, + /* .pfnUpdateStateAfterWrite = */ apicVBoxUpdateStateAfterWrite }; diff --git a/src/VBox/VMM/VMMAll/target-x86/IEMAllCImplSvmInstr-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/IEMAllCImplSvmInstr-x86.cpp index f23bfcc4808c..3fec202433a7 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 9a03588e365b..dda26dd0e54e 100644 --- a/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp @@ -464,6 +464,22 @@ 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); +} + + /** * Registers a PDM APIC backend. * @@ -506,6 +522,7 @@ 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); /* * 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 7ab41223acf1..ac7ce3cff81f 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 8b546b6bb965..caf30d11c5c2 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,55 @@ static R0PTRTYPE(void *) g_pvIOBitmap; | HMSVM_LOG_GS \ | HMSVM_LOG_LBR) +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 +717,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 +836,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 +846,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 +1103,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; + + //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 +1295,25 @@ 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 + && PDMHasApic(pVM)) + { + 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,37 @@ 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)) + /* With the AVIC, we still need to deliver PIC style interrupts ourselves. */ + if ( !pVCpu->hm.s.svm.fUseAvic + || VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC)) { - 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); + 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 +4005,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 +4201,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 +4309,61 @@ 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 + && PDMHasApic(pVM)) /** @todo Check where we can merge the PDMHasApic() call into fUseAvic. */ + { + /* Get the APIC base MSR from the virtual APIC device. */ + uint64_t const uApicBaseMsr = PDMApicGetBaseMsrNoCheck(pVCpu); + + Log4(("fUpdateApicFF=%RTbool uApicBaseMsr=%#RX64 u64GstMsrApicBase=%RX64\n", VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_UPDATE_APIC), uApicBaseMsr, pVCpu->hm.s.svm.u64GstMsrApicBase)); + + 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); + + /* XAPIC. */ + if (uApicBaseMsr & MSR_IA32_APICBASE_EN) + { + rc = hmR0SvmMapHCApicAccessPage(pVCpu, GCPhysApic); + AssertRCReturn(rc, rc); + } + + /* X2APIC. */ + uint8_t *pbMsrBitmap = (uint8_t *)pVCpu->hmr0.s.svm.pvMsrBitmap; + if ((uApicBaseMsr & (MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD)) + == (MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD)) + { + /* Don't intercept X2APIC MSRs as the intercept has a higher priority than the AVIC hardware. */ + for (uint32_t i = 0; i < RT_ELEMENTS(g_aX2AvicMsrs); i++) + hmR0SvmSetMsrPermission(pVCpu, pbMsrBitmap, g_aX2AvicMsrs[i], SVMMSREXIT_PASSTHRU_READ, SVMMSREXIT_PASSTHRU_WRITE); + pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable = 1; + } + else + { + /* Restore intercepts to X2APIC MSRs. */ + for (uint32_t i = 0; i < RT_ELEMENTS(g_aX2AvicMsrs); i++) + hmR0SvmSetMsrPermission(pVCpu, pbMsrBitmap, g_aX2AvicMsrs[i], SVMMSREXIT_INTERCEPT_READ, SVMMSREXIT_INTERCEPT_WRITE); + pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable = 0; + } + + 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 +4501,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 +4644,15 @@ 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. */ + if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC)) + VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); + } + /* If VMRUN failed, we can bail out early. This does -not- cover SVM_EXIT_INVALID. */ if (RT_UNLIKELY(rcVMRun != VINF_SUCCESS)) { @@ -4496,6 +4746,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 +5654,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 +9278,109 @@ 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); + + uint32_t const u32ApicIcrL = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo1 & UINT32_MAX); + uint32_t const u32ApicIcrH = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo1 >> 32); + uint32_t const idApic = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo2 & 0xfff); + uint32_t const idFailure = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo2 >> 32); + + Log2Func(("\n")); + Log4(("AVICExitIncompleteIpi/%u: ICRL=%#x ICRH=%#x idApic=%#x idFailure=%#x\n", + pVCpu->idCpu, u32ApicIcrL, u32ApicIcrH, idApic, idFailure)); + if (idFailure == 0) + { + pVCpu->hm.s.offApicReg = XAPIC_OFF_ICR_LO; + return PDMApicUpdateStateAfterWrite(pVCpu, XAPIC_OFF_ICR_LO); + } + + 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; +} + + +/** + * \#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; + + 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 f36adaa26bc7..1b441abc2961 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 dc5631a720cf..94e7a750e1d9 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/target-x86/HMR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp index ead02c27cd9e..391827cf6192 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, true); + 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->hm.s.svm.fAvic) + LogRel(("HM: Enabled AVIC support\n")); + hmR3DisableRawMode(pVM); LogRel((pVM->hm.s.fTprPatchingAllowed ? "HM: Enabled TPR patching\n" diff --git a/src/VBox/VMM/include/EMHandleRCTmpl.h b/src/VBox/VMM/include/EMHandleRCTmpl.h index 4bd10840ff95..408d4f0a6740 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 f5d40a6aab74..212ec34cb372 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 a651083a5ac0..eb8ebeb2c127 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 From 7e5b0a64f0afa28c502eafb0b3524c127fe5076c Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Thu, 23 Jul 2026 12:22:28 +0530 Subject: [PATCH 2/9] VMM/target-x86: SVM AVIC, include the PDMHasApic check in fUseAvic. Logging nits. --- src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp index caf30d11c5c2..16d263315d29 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp @@ -1104,7 +1104,7 @@ VMMR0DECL(int) SVMR0SetupVM(PVMCC pVM) 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; + 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; @@ -1295,8 +1295,7 @@ 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 - && PDMHasApic(pVM)) + if (fUseAvic) { void *pvVirtApic = NULL; RTHCPHYS HCPhysVirtApic = 0; @@ -4312,14 +4311,12 @@ static VBOXSTRICTRC hmR0SvmPreRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie /* * Setup the AVIC state if enabled. */ - if ( pVCpu->hm.s.svm.fUseAvic - && PDMHasApic(pVM)) /** @todo Check where we can merge the PDMHasApic() call into fUseAvic. */ + 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); - - Log4(("fUpdateApicFF=%RTbool uApicBaseMsr=%#RX64 u64GstMsrApicBase=%RX64\n", VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_UPDATE_APIC), uApicBaseMsr, pVCpu->hm.s.svm.u64GstMsrApicBase)); - Assert( MSR_IA32_APICBASE_GET_ADDR(uApicBaseMsr) == MSR_IA32_APICBASE_ADDR || !(uApicBaseMsr & MSR_IA32_APICBASE_EN)); if (uApicBaseMsr != pVCpu->hm.s.svm.u64GstMsrApicBase) @@ -4329,12 +4326,14 @@ static VBOXSTRICTRC hmR0SvmPreRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie /* 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. */ @@ -4358,10 +4357,10 @@ static VBOXSTRICTRC hmR0SvmPreRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie 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; + /* 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 From 642357b7a638a24cf1be183e5873e1772d56e22b Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Fri, 24 Jul 2026 15:03:02 +0530 Subject: [PATCH 3/9] VMM: SVM AVIC work-in-progress. Ubuntu 26 live-CD now boots with single VCPU using the AVIC. --- include/VBox/vmm/pdmapic.h | 22 ++- .../VMM/VMMAll/target-x86/APICAll-x86.cpp | 134 ++++++++++++------ .../VMM/VMMAll/target-x86/PDMAllApic-x86.cpp | 18 +++ src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp | 29 ++-- src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp | 1 + src/VBox/VMM/include/APICInternal.h | 2 + 6 files changed, 144 insertions(+), 62 deletions(-) diff --git a/include/VBox/vmm/pdmapic.h b/include/VBox/vmm/pdmapic.h index 62267fd32db8..3c23fe29dfeb 100644 --- a/include/VBox/vmm/pdmapic.h +++ b/include/VBox/vmm/pdmapic.h @@ -372,6 +372,15 @@ typedef struct PDMAPICBACKENDR3 */ 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). * @{ */ @@ -381,7 +390,6 @@ typedef struct PDMAPICBACKENDR3 DECLR3CALLBACKMEMBER(int, pfnReserved3, (void)); DECLR3CALLBACKMEMBER(int, pfnReserved4, (void)); DECLR3CALLBACKMEMBER(int, pfnReserved5, (void)); - DECLR3CALLBACKMEMBER(int, pfnReserved6, (void)); /** @} */ } PDMAPICBACKENDR3; /** Pointer to ring-3 APIC backend. */ @@ -649,6 +657,16 @@ typedef struct PDMAPICBACKENDR0 */ 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)); @@ -657,7 +675,6 @@ typedef struct PDMAPICBACKENDR0 DECLR0CALLBACKMEMBER(int, pfnReserved3, (void)); DECLR0CALLBACKMEMBER(int, pfnReserved4, (void)); DECLR0CALLBACKMEMBER(int, pfnReserved5, (void)); - DECLR0CALLBACKMEMBER(int, pfnReserved6, (void)); /** @} */ } PDMAPICBACKENDR0; /** Pointer to ring-0 APIC backend. */ @@ -967,6 +984,7 @@ VMM_INT_DECL(int) PDMApicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vect 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/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index 815484e7a819..edd9abf4ebf0 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -905,6 +905,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 +989,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 +1007,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). * @@ -3112,6 +3151,7 @@ const PDMAPICBACKEND g_ApicBackend = #endif /* .pfnImportState = */ apicImportState, /* .pfnExportState = */ apicExportState, - /* .pfnUpdateStateAfterWrite = */ apicVBoxUpdateStateAfterWrite + /* .pfnUpdateStateAfterWrite = */ apicVBoxUpdateStateAfterWrite, + /* .pfnSetEoiFast = */ apicSetEoiFast, }; diff --git a/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp index dda26dd0e54e..ec34c5d26116 100644 --- a/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/PDMAllApic-x86.cpp @@ -480,6 +480,23 @@ VMM_INT_DECL(VBOXSTRICTRC) PDMApicUpdateStateAfterWrite(PVMCPUCC pVCpu, uint16_t } +/** + * 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. * @@ -523,6 +540,7 @@ VMM_INT_DECL(int) PDMApicRegisterBackend(PVMCC pVM, PDMAPICBACKENDTYPE enmBacken 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/HMR0SVM-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp index 16d263315d29..8636c9ff68fb 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp @@ -428,6 +428,7 @@ 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, @@ -4337,21 +4338,17 @@ static VBOXSTRICTRC hmR0SvmPreRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie } /* X2APIC. */ - uint8_t *pbMsrBitmap = (uint8_t *)pVCpu->hmr0.s.svm.pvMsrBitmap; - if ((uApicBaseMsr & (MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD)) - == (MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD)) { - /* Don't intercept X2APIC MSRs as the intercept has a higher priority than the AVIC hardware. */ - for (uint32_t i = 0; i < RT_ELEMENTS(g_aX2AvicMsrs); i++) - hmR0SvmSetMsrPermission(pVCpu, pbMsrBitmap, g_aX2AvicMsrs[i], SVMMSREXIT_PASSTHRU_READ, SVMMSREXIT_PASSTHRU_WRITE); - pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable = 1; - } - else - { - /* Restore intercepts to X2APIC MSRs. */ + /* 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], SVMMSREXIT_INTERCEPT_READ, SVMMSREXIT_INTERCEPT_WRITE); - pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable = 0; + hmR0SvmSetMsrPermission(pVCpu, pbMsrBitmap, g_aX2AvicMsrs[i], fRdPerm, fWrPerm); + pVmcb->ctrl.IntCtrl.n.u1X2AvicEnable = fX2AvicEnable; } pVmcb->ctrl.AvicBar.u = MSR_IA32_APICBASE_GET_ADDR(uApicBaseMsr); @@ -9368,6 +9365,12 @@ HMSVM_EXIT_DECL hmR0SvmExitAvicNoAccel(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie 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... */ diff --git a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp index f0c29d3aaddf..937f53066d90 100644 --- a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp +++ b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp @@ -1094,6 +1094,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/include/APICInternal.h b/src/VBox/VMM/include/APICInternal.h index b92029da3dcf..14fa057f46c8 100644 --- a/src/VBox/VMM/include/APICInternal.h +++ b/src/VBox/VMM/include/APICInternal.h @@ -336,6 +336,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(). */ From cbae6a4c0395ac2292d92ee2541b0476c21de59d Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Tue, 4 Aug 2026 09:58:39 +0530 Subject: [PATCH 4/9] VMM/target-x86: Fix APIC interrupt delivery when SVM AVIC is used. Fixed a typo (% vs &) in hmR0SvmExitAvicNoAccel. Ubuntu 26 VM single-VCPU now boots fully without manually triggering an interrupt, like for example pressing a key. --- include/VBox/vmm/hm.h | 1 + .../VMM/VMMAll/target-x86/APICAll-x86.cpp | 14 ++++++++++++ src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp | 21 +++++++++++++----- src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp | 5 +++-- src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp | 22 ++++++++++++++++++- src/VBox/VMM/include/APICInternal.h | 4 ++++ 6 files changed, 59 insertions(+), 8 deletions(-) diff --git a/include/VBox/vmm/hm.h b/include/VBox/vmm/hm.h index 6bd25b889b29..36a546a83c07 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/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index edd9abf4ebf0..0250e9e45368 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -2427,6 +2427,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_NO_DATA; + } + PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu); bool const fApicHwEnabled = apicIsEnabled(pVCpu); if ( fApicHwEnabled diff --git a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp index 8636c9ff68fb..43fbe9b35c63 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp @@ -3820,8 +3820,15 @@ static VBOXSTRICTRC hmR0SvmEvaluatePendingEvent(PVMCPUCC pVCpu, PCSVMTRANSIENT p } #endif /* With the AVIC, we still need to deliver PIC style interrupts ourselves. */ - if ( !pVCpu->hm.s.svm.fUseAvic - || VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC)) + bool fGetInterrupt = true; + if (pVCpu->hm.s.svm.fUseAvic) + { + if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC)) + fGetInterrupt = false; + 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); @@ -4645,8 +4652,12 @@ static void hmR0SvmPostRunGuest(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransient, VBO && 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. */ - if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC)) - VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); + /* + * 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. */ @@ -9335,7 +9346,7 @@ HMSVM_EXIT_DECL hmR0SvmExitAvicNoAccel(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmTransie 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)); + bool const fWr = RT_BOOL(pSvmTransient->pVmcb->ctrl.u64ExitInfo1 & RT_BIT_64(32)); /* * Determine whether the access is fault or trap like. diff --git a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp index 937f53066d90..31d8820edc2f 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; } diff --git a/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp index 391827cf6192..3e977a84554a 100644 --- a/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp +++ b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp @@ -2073,7 +2073,7 @@ static int hmR3InitFinalizeR0Amd(PVM pVM) if (pVM->hm.s.fPostedIntrs) LogRel(("HM: Enabled posted-interrupt processing support\n")); - if (pVM->hm.s.svm.fAvic) + if (pVM->apCpusR3[0]->hm.s.svm.fUseAvic) LogRel(("HM: Enabled AVIC support\n")); hmR3DisableRawMode(pVM); @@ -2961,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 14fa057f46c8..9bb6642f2a7a 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; /** @} */ From 042feb2fb68befa6a201d20462ebbc63cabd4958 Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Mon, 10 Aug 2026 18:11:04 +0530 Subject: [PATCH 5/9] VMM/target-x86: Fix incorrect force-flag clearing when retreiving the interrupt from the APIC when AMD AVIC is enabled. This fixes the SMP issue while booting Ubuntu 26 VMs with AVIC. --- include/VBox/err.h | 2 + include/VBox/vmm/hm_svm.h | 14 +++++ src/VBox/VMM/VMMAll/PDMAll.cpp | 13 +++-- .../VMM/VMMAll/target-x86/APICAll-x86.cpp | 55 +++++++++++++------ src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp | 5 ++ src/VBox/VMM/VMMR3/TRPMR3.cpp | 3 +- 6 files changed, 71 insertions(+), 21 deletions(-) diff --git a/include/VBox/err.h b/include/VBox/err.h index da36788687f3..b5d0bbadf086 100644 --- a/include/VBox/err.h +++ b/include/VBox/err.h @@ -3145,6 +3145,8 @@ #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_svm.h b/include/VBox/vmm/hm_svm.h index e103aefa3e13..e7a0c404425c 100644 --- a/include/VBox/vmm/hm_svm.h +++ b/include/VBox/vmm/hm_svm.h @@ -339,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 * @{ */ diff --git a/src/VBox/VMM/VMMAll/PDMAll.cpp b/src/VBox/VMM/VMMAll/PDMAll.cpp index a4dcd6bcf04f..5e5849733d44 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 0250e9e45368..310fabb7f18b 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -765,6 +765,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); } @@ -1467,7 +1468,7 @@ DECLINLINE(VBOXSTRICTRC) apicWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, u { VMCPU_ASSERT_EMT(pVCpu); Assert(offReg <= XAPIC_OFF_MAX_VALID); - //Assert(!XAPIC_IN_X2APIC_MODE(pVCpu->apic.s.uApicBaseMsr)); + Assert(!XAPIC_IN_X2APIC_MODE(pVCpu->apic.s.uApicBaseMsr)); VBOXSTRICTRC rcStrict = VINF_SUCCESS; switch (offReg) @@ -1593,7 +1594,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); /* @@ -2438,7 +2439,7 @@ static DECLCALLBACK(int) apicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, ui { *pu8Vector = 0; *puSrcTag = 0; - return VERR_NO_DATA; + return VERR_APIC_INTR_DEFER; } PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu); @@ -2669,7 +2670,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); @@ -3011,22 +3013,43 @@ static DECLCALLBACK(VBOXSTRICTRC) apicVBoxUpdateStateAfterWrite(PVMCPUCC pVCpu, 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); - uint32_t u32Value = 0; + 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); - VBOXSTRICTRC rcStrict = apicReadRegister(pDevIns, pVCpu, offApicReg, &u32Value); - if (rcStrict == VINF_SUCCESS) + /* + * 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 { - /* 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. */ - rcStrict = apicWriteRegister(pDevIns, pVCpu, offApicReg, u32Value); + 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; } - - if ( rcStrict == VINF_IOM_R3_MMIO_READ - || rcStrict == VINF_IOM_R3_MMIO_WRITE) - rcStrict = VINF_APIC_R3_UPDATE_STATE; - return rcStrict; } diff --git a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp index 43fbe9b35c63..c7c4e8ecf035 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp @@ -3825,6 +3825,11 @@ static VBOXSTRICTRC hmR0SvmEvaluatePendingEvent(PVMCPUCC pVCpu, PCSVMTRANSIENT p { if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC)) fGetInterrupt = false; + /* + * We clear the interrupt flag here because we are certain that all + * conditions necessary for the AVIC hardware to deliver the interrupt + * are met. + */ if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC)) VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); } diff --git a/src/VBox/VMM/VMMR3/TRPMR3.cpp b/src/VBox/VMM/VMMR3/TRPMR3.cpp index ec482f610241..88bd809513fc 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 From fab7496563338522ebf6a52b2707dd9cedce448d Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Wed, 12 Aug 2026 10:24:08 +0530 Subject: [PATCH 6/9] VMM/HM: Don't yet enable AVIC by default, needs more testing. --- src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp index 3e977a84554a..73547ae83574 100644 --- a/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp +++ b/src/VBox/VMM/VMMR3/target-x86/HMR3-x86.cpp @@ -429,7 +429,7 @@ VMMR3_INT_DECL(int) HMR3Init(PVM pVM) /** @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, true); + rc = CFGMR3QueryBoolDef(pCfgHm, "SvmAvic", &pVM->hm.s.svm.fAvic, false); AssertRCReturn(rc, rc); /** @cfgm{/HM/Exclusive, bool} From 2a3a82db95f6bdfac0395da533bf5eb338f0ca2b Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Wed, 12 Aug 2026 12:46:56 +0530 Subject: [PATCH 7/9] VMM/APIC: Fix error handling when an illegal vector is sent via an IPI. This is for the upcoming fix in the SVM AVIC incomplete IPI #VMEXIT handler. Tested this change booting Ubuntu 10.04-3 amd64 live CD to ensure it doesn't cause a regression there. --- src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index 1e3b62796b87..52312828e7d3 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -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 and 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); From 8b24d3d49cc31016ee81eb3405d672c4e903769b Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Wed, 12 Aug 2026 12:51:57 +0530 Subject: [PATCH 8/9] VMM/APIC: Comment typo. --- src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index 52312828e7d3..aa40cb399633 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -608,7 +608,7 @@ static VBOXSTRICTRC apicSendIntr(PVMCC pVM, PVMCPUCC pVCpu, uint8_t uVector, XAP && pVCpu) { /* - * Flag only errors when the delivery mode is fixed and lowest-priority and not + * 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". From 233136fab512fe236b5583f944c6f7db007b9d91 Mon Sep 17 00:00:00 2001 From: Teknomancer Date: Wed, 12 Aug 2026 14:13:24 +0530 Subject: [PATCH 9/9] VMM/HM: SVM AVIC: Incomplete IPI #VMEXIT nits. Handle invalid target similar to invalid interrupt type and only kick VCPUs if the failure reason is 'target not running', fail other cases. --- src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp index c7c4e8ecf035..6d8c98275efa 100644 --- a/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp +++ b/src/VBox/VMM/VMMR0/target-x86/HMR0SVM-x86.cpp @@ -9299,45 +9299,54 @@ HMSVM_EXIT_DECL hmR0SvmExitAvicIncompleteIpi(PVMCPUCC pVCpu, PSVMTRANSIENT pSvmT HMSVM_VALIDATE_EXIT_HANDLER_PARAMS(pVCpu, pSvmTransient); STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatSvmExitAvicIncompleteIpi); - uint32_t const u32ApicIcrL = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo1 & UINT32_MAX); - uint32_t const u32ApicIcrH = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo1 >> 32); - uint32_t const idApic = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo2 & 0xfff); - uint32_t const idFailure = (uint32_t)(pSvmTransient->pVmcb->ctrl.u64ExitInfo2 >> 32); + 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")); - Log4(("AVICExitIncompleteIpi/%u: ICRL=%#x ICRH=%#x idApic=%#x idFailure=%#x\n", - pVCpu->idCpu, u32ApicIcrL, u32ApicIcrH, idApic, idFailure)); - if (idFailure == 0) + 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); } - PVMCC pVM = pVCpu->CTX_SUFF(pVM); - PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idApic]; - VMCPUID idCpu = pVCpuDst->idCpu; - if (VMMGetCpuId(pVM) != idCpu) + if (idFailure == SVM_EXIT2_INC_IPI_INDEX_TARGET_NOT_RUNNING) { - switch (VMCPU_GET_STATE(pVCpuDst)) + PVMCC pVM = pVCpu->CTX_SUFF(pVM); + PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idApic]; + VMCPUID idCpu = pVCpuDst->idCpu; + if (VMMGetCpuId(pVM) != idCpu) { - case VMCPUSTATE_STARTED_EXEC: - Log7Func(("idCpu=%u VMCPUSTATE_STARTED_EXEC\n", idCpu)); - GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu); - break; + 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; + 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. */ + default: + Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState)); + break; /* nothing to do in other states. */ + } } + return VINF_SUCCESS; } - return VINF_SUCCESS; + AssertMsgFailed(("hmR0SvmExitAvicIncompleteIpi: Unexpected failure type %#x\n", idFailure)); + return VERR_SVM_IPE_4; }