diff --git a/src/VBox/VMM/VMMAll/GIMAllHv.cpp b/src/VBox/VMM/VMMAll/GIMAllHv.cpp index 7d87a1e7b14..01ac2f351ce 100644 --- a/src/VBox/VMM/VMMAll/GIMAllHv.cpp +++ b/src/VBox/VMM/VMMAll/GIMAllHv.cpp @@ -50,6 +50,9 @@ # include #endif +#ifdef IN_RING0 +# include /* for RT_ZERO. */ +#endif #ifdef IN_RING3 /** @@ -313,9 +316,9 @@ VMM_INT_DECL(VBOXSTRICTRC) gimHvHypercall(PVMCPUCC pVCpu, PCPUMCTX pCtx) */ GIMHVMSG HvMsg; RT_ZERO(HvMsg); - HvMsg.MsgHdr.enmMessageType = GIMHVMSGTYPE_VMBUS; - HvMsg.MsgHdr.cbPayload = 0xf; - HvMsg.aPayload[0] = 0xf; + HvMsg.Header.enmMessageType = GIMHVMSGTYPE_VMBUS; + HvMsg.Header.cbPayload = 0xf; + HvMsg.u.aPayload[0] = 0xf; uint16_t const offMsg = GIM_HV_VMBUS_MSG_SINT * sizeof(GIMHVMSG); int rc2 = PGMPhysSimpleWriteGCPhys(pVM, GCPhysSimp + offMsg, &HvMsg, sizeof(HvMsg)); if (RT_SUCCESS(rc2)) @@ -493,7 +496,7 @@ static const char *gimHvGetGuestOsIdVariantName(uint64_t uGuestOsIdMsr) * @returns The time reference count. * @param pVCpu The cross context virtual CPU structure. */ -DECLINLINE(uint64_t) gimHvGetTimeRefCount(PVMCPUCC pVCpu) +static uint64_t gimHvGetTimeRefCount(PVMCPUCC pVCpu) { /* Hyper-V reports the time in 100 ns units (10 MHz). */ VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu); @@ -509,42 +512,46 @@ DECLINLINE(uint64_t) gimHvGetTimeRefCount(PVMCPUCC pVCpu) /** * Starts the synthetic timer. * - * @param pVCpu The cross context virtual CPU structure. - * @param pHvStimer Pointer to the Hyper-V synthetic timer. + * @param pVCpu The cross context virtual CPU structure. + * @param pHvStimer Pointer to the Hyper-V synthetic timer. * * @remarks Caller needs to hold the timer critical section. * @thread Any. */ -VMM_INT_DECL(void) gimHvStartStimer(PVMCPUCC pVCpu, PCGIMHVSTIMER pHvStimer) +static void gimHvStartStimer(PVMCPUCC pVCpu, PGIMHVSTIMER pHvStimer) { PVMCC pVM = pVCpu->CTX_SUFF(pVM); TMTIMERHANDLE hTimer = pHvStimer->hTimer; Assert(TMTimerIsLockOwner(pVM, hTimer)); - uint64_t const uTimerCount = pHvStimer->uStimerCountMsr; - if (uTimerCount) + if (pHvStimer->uStimerCountMsr) { - uint64_t const uTimerCountNS = uTimerCount * 100; + uint64_t const uTimerCount = pHvStimer->uStimerCountMsr; /* in 100-ns units. */ + uint64_t const uCurRefTime = gimHvGetTimeRefCount(pVCpu); /* in 100-ns units. */ - /* For periodic timers, 'uTimerCountNS' represents the relative interval. */ if (MSR_GIM_HV_STIMER_IS_PERIODIC(pHvStimer->uStimerConfigMsr)) { - TMTimerSetNano(pVM, hTimer, uTimerCountNS); - LogFlow(("GIM%u: HyperV: Started relative periodic STIMER%u with uTimerCountNS=%RU64\n", pVCpu->idCpu, - pHvStimer->idxStimer, uTimerCountNS)); + /* For periodic timers, 'uTimerCount' represents the relative interval in 100-ns units. */ + pHvStimer->uExpirationTime = uCurRefTime + uTimerCount; /* absolute 100-ns timestamp. */ + uint64_t const cNanosToNext = uTimerCount * 100 /* ns */; /* number of nanos to expiry. */ + TMTimerSetNano(pVM, hTimer, cNanosToNext); + LogFlow(("GIM%u: HyperV: Started relative periodic STIMER%u with cNanosToNext=%RU64\n", + pVCpu->idCpu, pHvStimer->idxStimer, cNanosToNext)); } else { - /* For one-shot timers, 'uTimerCountNS' represents an absolute expiration wrt to Hyper-V reference time, - we convert it to a relative time and program the timer. */ - uint64_t const uCurRefTimeNS = gimHvGetTimeRefCount(pVCpu) * 100; - if (uTimerCountNS > uCurRefTimeNS) - { - uint64_t const uRelativeNS = uTimerCountNS - uCurRefTimeNS; - TMTimerSetNano(pVM, hTimer, uRelativeNS); - LogFlow(("GIM%u: HyperV: Started one-shot relative STIMER%u with uRelativeNS=%RU64\n", pVCpu->idCpu, - pHvStimer->idxStimer, uRelativeNS)); - } + /* For one-shot timers, 'uTimerCount' represents an absolute expiration (in 100-ns units) + wrt to Hyper-V reference time. */ + pHvStimer->uExpirationTime = uTimerCount; /* absolute 100-ns timestamp. */ + + /* Use a signed delta to ensure expiration is in the future as Hyper-V allows the counter to wrap. */ + int64_t const cTicksToNext = (int64_t)(uTimerCount - uCurRefTime); + uint64_t const cNanosToNext = cTicksToNext > 0 + ? (uint64_t)cTicksToNext * 100 + : 1; /** @todo not ideal, should call gimR3HvTimerCallback here */ + TMTimerSetNano(pVM, hTimer, cNanosToNext); + LogFlow(("GIM%u: HyperV: Started one-shot relative STIMER%u with cNanosToNext=%RU64\n", + pVCpu->idCpu, pHvStimer->idxStimer, cNanosToNext)); } /** @todo frequency hinting? */ } @@ -569,7 +576,105 @@ static void gimHvStopStimer(PVMCPUCC pVCpu, PGIMHVSTIMER pHvStimer) Assert(TMTimerIsLockOwner(pVM, hTimer)); if (TMTimerIsActive(pVM, hTimer)) + { TMTimerStop(pVM, hTimer); + LogFlow(("GIM%u: HyperV: Stopped STIMER%u\n", pVCpu->idCpu, pHvStimer->idxStimer)); + } +} + + +/** + * Delivery a synthetic timer message. + * + * @param pVCpu The cross context virtual CPU structure. + * @param pHvStimer Pointer to the Hyper-V synthetic timer. + */ +VMM_INT_DECL(void) gimHvDeliverTimerMsg(PVMCPUCC pVCpu, PGIMHVSTIMER pHvStimer) +{ + VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu); + Assert(pHvStimer->idCpu == pVCpu->idCpu); + PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu; + + /* Get the VCPU's synthetic timer config and timer index. */ + uint64_t const uStimerConfig = pHvStimer->uStimerConfigMsr; + uint16_t const idxStimer = pHvStimer->idxStimer; + + /* Sanity. */ + Assert(idxStimer < RT_ELEMENTS(pHvCpu->aStatStimerFired)); + Assert(idxStimer < RT_ELEMENTS(pHvCpu->aStimers)); + AssertCompile(MSR_GIM_HV_STIMER_SINT_MASK < RT_ELEMENTS(pHvCpu->auSintMsrs)); + AssertCompile(RT_ELEMENTS(pHvCpu->auSintMsrs) * GIM_HV_MSG_SIZE == GIM_HV_PAGE_SIZE); + + STAM_COUNTER_INC(&pHvCpu->aStatStimerFired[idxStimer]); + + /* For direct mode, send the interrupt directly, no messing with the SIM page. */ + if (MSR_GIM_HV_STIMER_IS_DIRECT_MODE(uStimerConfig)) + { + uint8_t const uVector = MSR_GIM_HV_STIMER_GET_VECTOR(uStimerConfig); + PDMApicHvSendInterrupt(pVCpu, uVector, false /* fAutoEoi */, XAPICTRIGGERMODE_EDGE); + } + else if (MSR_GIM_HV_SIMP_IS_ENABLED(pHvCpu->uSimpMsr)) + { + /* For indirect mode, send the interrupt via a message slot in the SIM page. */ + uint8_t const idxSint = MSR_GIM_HV_STIMER_GET_SINTX(uStimerConfig); + RTGCPHYS const GCPhysSimp = MSR_GIM_HV_SIMP_GPA(pHvCpu->uSimpMsr); + RTGCPHYS const GCPhysMsg = GCPhysSimp + idxSint * GIM_HV_MSG_SIZE; + GIMHVMSG Msg; + PVMCC pVM = pVCpu->CTX_SUFF(pVM); + int rc = PGMPhysSimpleReadGCPhys(pVM, &Msg, GCPhysMsg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + if (Msg.Header.enmMessageType == GIMHVMSGTYPE_NONE) + { + /* The SINT message slot is free, update the timer message in the slot. */ + RT_ZERO(Msg); + Msg.Header.enmMessageType = GIMHVMSGTYPE_TIMEREXPIRED; + Msg.Header.cbPayload = sizeof(Msg.u.timer); + Msg.u.timer.idxStimer = pHvStimer->idxStimer; + Msg.u.timer.uExpirationTime = pHvStimer->uExpirationTime; + /** @todo We cannot call gimHvGetTimeRefCount(pVCpu) as we're not on the EMT corresponding to the timer */ + Msg.u.timer.uDeliveryTime = pHvStimer->uExpirationTime; /* Not really correct but see todo above. */ + rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysMsg, &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + pHvStimer->fMsgPending = false; + uint64_t const uSint = pHvCpu->auSintMsrs[idxSint]; + if (!MSR_GIM_HV_SINT_IS_MASKED(uSint)) + { + uint8_t const uVector = MSR_GIM_HV_SINT_GET_VECTOR(uSint); + bool const fAutoEoi = MSR_GIM_HV_SINT_IS_AUTOEOI(uSint); + Assert(fAutoEoi == false); + PDMApicHvSendInterrupt(pVCpu, uVector, fAutoEoi, XAPICTRIGGERMODE_EDGE); + } + } + else + { + LogRelMax(10, ("GIM%u: HyperV: WARNING! Failed to write STIMER%u in SIM page. rc=%Rrc\n", + pVCpu->idCpu, pHvStimer->idxStimer, rc)); + } + } + else + { + /* The SINT message slot is not free, queue the timer as pending. */ + if (!Msg.Header.MessageFlags.n.u1Pending) + { + pHvStimer->fMsgPending = true; + Msg.Header.MessageFlags.n.u1Pending = 1; + rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysMsg, &Msg, sizeof(Msg)); + if (RT_FAILURE(rc)) + LogRelMax(10, ("GIM%u: HyperV: WARNING! Failed to update STIMER%u in SIM page. rc=%Rrc\n", + pVCpu->idCpu, pHvStimer->idxStimer, rc)); + } + } + } + else + { + LogRelMax(10, ("GIM%u: HyperV: WARNING! Failed to read STIMER%u in SIM page. rc=%Rrc\n", + pVCpu->idCpu, pHvStimer->idxStimer, rc)); + } + } + else + pHvStimer->fMsgPending = true; } @@ -902,34 +1007,35 @@ VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMM #endif /* IN_RING3 */ } - case MSR_GIM_HV_APIC_ASSIST_PAGE: + case MSR_GIM_HV_VP_ASSIST: { #ifndef IN_RING3 return VINF_CPUM_R3_MSR_WRITE; #else /* IN_RING3 */ PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu; - pHvCpu->uApicAssistPageMsr = uRawValue; + pHvCpu->uVpAssistMsr = uRawValue; - if (MSR_GIM_HV_APICASSIST_PAGE_IS_ENABLED(uRawValue)) + if (MSR_GIM_HV_VP_ASSIST_PAGE_IS_ENABLED(uRawValue)) { - RTGCPHYS GCPhysApicAssistPage = MSR_GIM_HV_APICASSIST_GUEST_PFN(uRawValue) << GUEST_PAGE_SHIFT; - if (PGMPhysIsGCPhysNormal(pVM, GCPhysApicAssistPage)) + RTGCPHYS GCPhysVpAssistPage = MSR_GIM_HV_VP_ASSIST_GUEST_PFN(uRawValue) << GUEST_PAGE_SHIFT; + if (PGMPhysIsGCPhysNormal(pVM, GCPhysVpAssistPage)) { - int rc = gimR3HvEnableApicAssistPage(pVCpu, GCPhysApicAssistPage); + int rc = gimR3HvEnableVpAssistPage(pVCpu, GCPhysVpAssistPage); if (RT_SUCCESS(rc)) { - pHvCpu->uApicAssistPageMsr = uRawValue; + pHvCpu->uVpAssistMsr = uRawValue; + LogRel(("GIM%u: HyperV: Enabled VP Assist page at %#RGp\n", pVCpu->idCpu, GCPhysVpAssistPage)); return VINF_SUCCESS; } } else { - LogRelMax(5, ("GIM%u: HyperV: APIC-assist page address %#RGp invalid!\n", pVCpu->idCpu, - GCPhysApicAssistPage)); + LogRelMax(10, ("GIM%u: HyperV: VP Assist page address %#RGp invalid!\n", pVCpu->idCpu, + GCPhysVpAssistPage)); } } else - gimR3HvDisableApicAssistPage(pVCpu); + gimR3HvDisableVpAssistPage(pVCpu); return VERR_CPUM_RAISE_GP_0; #endif /* IN_RING3 */ @@ -1156,66 +1262,55 @@ VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMM { PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu; uint8_t const idxStimer = (idMsr - MSR_GIM_HV_STIMER0_CONFIG) >> 1; + int const rcBusy = VINF_CPUM_R3_MSR_WRITE; /* Validate the writable bits. */ if (RT_LIKELY(!(uRawValue & ~MSR_GIM_HV_STIMER_RW_VALID))) { - Assert(idxStimer < RT_ELEMENTS(pHvCpu->aStimers)); - PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer]; - - /* Lock to prevent concurrent access from the timer callback. */ - int rc = TMTimerLock(pVM, pHvStimer->hTimer, VERR_IGNORED); - if (rc == VINF_SUCCESS) + if (RT_LIKELY(idxStimer < RT_ELEMENTS(pHvCpu->aStimers))) { - /* Update the MSR value. */ - pHvStimer->uStimerConfigMsr = uRawValue; - Log(("GIM%u: HyperV: Set STIMER_CONFIG%u=%#RX64\n", pVCpu->idCpu, idxStimer, uRawValue)); + PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer]; - /* Process the MSR bits. */ - if ( !MSR_GIM_HV_STIMER_GET_SINTX(uRawValue) /* Writing SINTx as 0 causes the timer to be disabled. */ - || !MSR_GIM_HV_STIMER_IS_ENABLED(uRawValue)) - { - pHvStimer->uStimerConfigMsr &= ~MSR_GIM_HV_STIMER_ENABLE; - gimHvStopStimer(pVCpu, pHvStimer); - Log(("GIM%u: HyperV: Disabled STIMER_CONFIG%u\n", pVCpu->idCpu, idxStimer)); - } - else if (MSR_GIM_HV_STIMER_IS_ENABLED(uRawValue)) + /* Lock to prevent concurrent access from the timer callback. */ + int rc = TMTimerLock(pVM, pHvStimer->hTimer, rcBusy); + if (rc == VINF_SUCCESS) { - /* Auto-enable implies writing to the STIMERx_COUNT MSR is what starts the timer. */ - if (!MSR_GIM_HV_STIMER_IS_AUTO_ENABLED(uRawValue)) + /* Update the MSR value. */ + pHvStimer->uStimerConfigMsr = uRawValue; + Log(("GIM%u: HyperV: Set STIMER_CONFIG%u=%#RX64\n", pVCpu->idCpu, idxStimer, uRawValue)); + + /* Writing SINTx as 0 or the enabled bit causes the timer to be disabled. */ + if ( !MSR_GIM_HV_STIMER_GET_SINTX(uRawValue) + || !MSR_GIM_HV_STIMER_IS_ENABLED(uRawValue)) { - if (!TMTimerIsActive(pVM, pHvStimer->hTimer)) - { - gimHvStartStimer(pVCpu, pHvStimer); - Log(("GIM%u: HyperV: Started STIMER%u\n", pVCpu->idCpu, idxStimer)); - } - else - { - /* - * Enabling a timer that's already enabled is undefined behaviour, - * see Hyper-V spec. 15.3.1 "Synthetic Timer Configuration Register". - * - * Our implementation just re-starts the timer. Guests that comform to - * the Hyper-V specs. should not be doing this anyway. - */ - AssertFailed(); - gimHvStopStimer(pVCpu, pHvStimer); - gimHvStartStimer(pVCpu, pHvStimer); - } + pHvStimer->uStimerConfigMsr &= ~MSR_GIM_HV_STIMER_ENABLE; + gimHvStopStimer(pVCpu, pHvStimer); + } + else if ( MSR_GIM_HV_STIMER_IS_ENABLED(uRawValue) + && !MSR_GIM_HV_STIMER_IS_AUTO_ENABLED(uRawValue)) + { + /* + * Enabling a timer that's already enabled is undefined behaviour, + * see Hyper-V spec. 15.3.1 "Synthetic Timer Configuration Register". + * + * Our implementation just re-starts the timer. Guests that comform to + * the Hyper-V specs. should not be changing timer properties without + * explicitly disabling and enabling the timer. + */ + Assert(MSR_GIM_HV_STIMER_GET_SINTX(uRawValue)); + gimHvStopStimer(pVCpu, pHvStimer); + gimHvStartStimer(pVCpu, pHvStimer); } + TMTimerUnlock(pVM, pHvStimer->hTimer); } - - TMTimerUnlock(pVM, pHvStimer->hTimer); + return rc; } - return rc; + LogRel(("GIM%u: HyperV: Invalid STIMER_CONFIG index %#RX32 -> #GP(0)\n", pVCpu->idCpu, idxStimer)); } -#ifndef IN_RING3 - return VINF_CPUM_R3_MSR_WRITE; -#else - LogRel(("GIM%u: HyperV: Setting reserved bits of STIMER%u MSR (uRawValue=%#RX64) -> #GP(0)\n", pVCpu->idCpu, - idxStimer, uRawValue)); + else + LogRel(("GIM%u: HyperV: Setting reserved bits in STIMER%u_CONFIG MSR (uRawValue=%#RX64) -> #GP(0)\n", + pVCpu->idCpu, idxStimer, uRawValue)); return VERR_CPUM_RAISE_GP_0; -#endif } case MSR_GIM_HV_STIMER0_COUNT: @@ -1229,46 +1324,44 @@ VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMM PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer]; int const rcBusy = VINF_CPUM_R3_MSR_WRITE; - /* - * Writing zero to this MSR disables the timer regardless of whether the auto-enable - * flag is set in the config MSR corresponding to the timer. - */ - if (!uRawValue) + int rc = TMTimerLock(pVM, pHvStimer->hTimer, rcBusy); + if (rc == VINF_SUCCESS) { + /* + * The timer may have changed from one-shot to periodic mode and vice versa, + * so we always stop/start the timer using the regular start/stop machinery + * which takes into account the type of timer rather than just updating the + * interval directly using TMTimerSetNano(). + */ gimHvStopStimer(pVCpu, pHvStimer); - pHvStimer->uStimerCountMsr = 0; - Log(("GIM%u: HyperV: Set STIMER_COUNT%u=%RU64, stopped timer\n", pVCpu->idCpu, idxStimer, uRawValue)); - return VINF_SUCCESS; - } - - /* - * Concurrent writes to the config. MSR can't happen as it's serialized by way - * of being done on the same EMT as this. - */ - if (MSR_GIM_HV_STIMER_IS_AUTO_ENABLED(pHvStimer->uStimerConfigMsr)) - { - int rc = TMTimerLock(pVM, pHvStimer->hTimer, rcBusy); - if (rc == VINF_SUCCESS) + pHvStimer->uStimerCountMsr = uRawValue; + + /* + * Writing zero to this MSR disables the timer regardless of whether the + * auto-enable flag is set in the config MSR corresponding to the timer. + * When auto-enable flag is set, writing should start the timer. + */ + if ( uRawValue + && MSR_GIM_HV_STIMER_IS_AUTO_ENABLED(pHvStimer->uStimerConfigMsr)) { - pHvStimer->uStimerCountMsr = uRawValue; + pHvStimer->uStimerConfigMsr |= MSR_GIM_HV_STIMER_ENABLE; gimHvStartStimer(pVCpu, pHvStimer); - TMTimerUnlock(pVM, pHvStimer->hTimer); - Log(("GIM%u: HyperV: Set STIMER_COUNT%u=%RU64 %RU64 msec, auto-started timer\n", pVCpu->idCpu, idxStimer, - uRawValue, (uRawValue * 100) / RT_NS_1MS_64)); } - return rc; + TMTimerUnlock(pVM, pHvStimer->hTimer); } - - /* Simple update of the counter without any timer start/stop side-effects. */ - pHvStimer->uStimerCountMsr = uRawValue; - Log(("GIM%u: HyperV: Set STIMER_COUNT%u=%RU64\n", pVCpu->idCpu, idxStimer, uRawValue)); - return VINF_SUCCESS; + return rc; } case MSR_GIM_HV_EOM: { - /** @todo implement EOM. */ Log(("GIM%u: HyperV: EOM\n", pVCpu->idCpu)); + PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu; + for (uint8_t idxStimer = 0; idxStimer < RT_ELEMENTS(pHvCpu->aStimers); idxStimer++) + { + PGIMHVSTIMER pHvStimer = &pHvCpu->aStimers[idxStimer]; + if (pHvStimer->fMsgPending) + gimHvDeliverTimerMsg(pVCpu, pHvStimer); + } return VINF_SUCCESS; } @@ -1306,38 +1399,22 @@ VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMM case MSR_GIM_HV_SIMP: { -#ifndef IN_RING3 - return VINF_CPUM_R3_MSR_WRITE; -#else PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu; pHvCpu->uSimpMsr = uRawValue; if (MSR_GIM_HV_SIMP_IS_ENABLED(uRawValue)) { - RTGCPHYS GCPhysSimp = MSR_GIM_HV_SIMP_GPA(uRawValue); + RTGCPHYS const GCPhysSimp = MSR_GIM_HV_SIMP_GPA(uRawValue); if (PGMPhysIsGCPhysNormal(pVM, GCPhysSimp)) - { - uint8_t abSimp[GIM_HV_PAGE_SIZE]; - RT_ZERO(abSimp); - int rc2 = PGMPhysSimpleWriteGCPhys(pVM, GCPhysSimp, &abSimp[0], sizeof(abSimp)); - if (RT_SUCCESS(rc2)) - LogRel(("GIM%u: HyperV: Enabled synthetic interrupt message page at %#RGp\n", pVCpu->idCpu, GCPhysSimp)); - else - { - LogRel(("GIM%u: HyperV: Failed to update synthetic interrupt message page at %#RGp. uSimpMsr=%#RX64 rc=%Rrc\n", - pVCpu->idCpu, pHvCpu->uSimpMsr, GCPhysSimp, rc2)); - return VERR_CPUM_RAISE_GP_0; - } - } + LogRel(("GIM%u: HyperV: Enabled SIM page at %#RGp\n", pVCpu->idCpu, GCPhysSimp)); else { - LogRel(("GIM%u: HyperV: Enabled synthetic interrupt message page at invalid address %#RGp\n", pVCpu->idCpu, - GCPhysSimp)); + LogRel(("GIM%u: HyperV: SIM page at invalid address %#RGp\n", pVCpu->idCpu, GCPhysSimp)); + pHvCpu->uSimpMsr &= ~MSR_GIM_HV_SIMP_ENABLE; } } else - LogRel(("GIM%u: HyperV: Disabled synthetic interrupt message page\n", pVCpu->idCpu)); + LogRel(("GIM%u: HyperV: Disabled SIM page\n", pVCpu->idCpu)); return VINF_SUCCESS; -#endif } case MSR_GIM_HV_CRASH_P0: pHv->uCrashP0Msr = uRawValue; return VINF_SUCCESS; diff --git a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp index cae8be37b79..a7c0f072b01 100644 --- a/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp +++ b/src/VBox/VMM/VMMAll/target-x86/APICAll-x86.cpp @@ -134,56 +134,136 @@ DECLINLINE(void) apicClearVectorInReg(volatile XAPIC256BITREG *pApicReg, uint8_t } -#if 0 /* unused */ /** - * Checks if a vector is set in an APIC Pending-Interrupt Bitmap (PIB). + * Atomically sets the notification bit in the edge-triggered PIB. * - * @returns true if the specified vector is set, false otherwise. - * @param pvPib Opaque pointer to the PIB. - * @param uVector The vector to check if set. + * @returns non-zero if the bit was already set, 0 otherwise. + * @param pApicCpu The APIC CPU state. */ -DECLINLINE(bool) apicTestVectorInPib(volatile void *pvPib, uint8_t uVector) +DECLINLINE(uint32_t) apicSetNotificationBitInEdgePib(PAPICCPU pApicCpu) { - return ASMBitTest(pvPib, uVector); + PAPICEDGEPIB pApicPib = (PAPICEDGEPIB)pApicCpu->CTX_SUFF(pvApicPib); + return ASMAtomicXchgU32(&pApicPib->fOutstandingNotification, RT_BIT_32(31)); } -#endif /* unused */ /** - * Atomically sets the PIB notification bit. + * Atomically tests and clears the notification bit in the edge-triggered PIB. * * @returns non-zero if the bit was already set, 0 otherwise. - * @param pApicPib Pointer to the PIB. + * @param pApicCpu The APIC CPU state. */ -DECLINLINE(uint32_t) apicSetNotificationBitInPib(PAPICPIB pApicPib) +DECLINLINE(uint32_t) apicClearNotificationBitInEdgePib(PAPICCPU pApicCpu) { + PAPICEDGEPIB pApicPib = (PAPICEDGEPIB)pApicCpu->CTX_SUFF(pvApicPib); + return ASMAtomicXchgU32(&pApicPib->fOutstandingNotification, UINT32_C(0)); +} + + +/** + * Atomically sets the notification bit in the level-sensitive PIB. + * + * @returns non-zero if the bit was already set, 0 otherwise. + * @param pApicCpu The APIC CPU state. + */ +DECLINLINE(uint32_t) apicSetNotificationBitInLevelPib(PAPICCPU pApicCpu) +{ + PAPICLEVELPIB pApicPib = &pApicCpu->ApicPibLevel; return ASMAtomicXchgU32(&pApicPib->fOutstandingNotification, RT_BIT_32(31)); } /** - * Atomically tests and clears the PIB notification bit. + * Atomically tests and clears the notification bit in the level-sensitive PIB. * * @returns non-zero if the bit was already set, 0 otherwise. - * @param pApicPib Pointer to the PIB. + * @param pApicCpu The APIC CPU state. */ -DECLINLINE(uint32_t) apicClearNotificationBitInPib(PAPICPIB pApicPib) +DECLINLINE(uint32_t) apicClearNotificationBitInLevelPib(PAPICCPU pApicCpu) { + PAPICLEVELPIB pApicPib = &pApicCpu->ApicPibLevel; return ASMAtomicXchgU32(&pApicPib->fOutstandingNotification, UINT32_C(0)); } /** - * Sets the vector in an APIC Pending-Interrupt Bitmap (PIB). + * Sets the vector in the level-sensitive PIB. * - * @param pvPib Opaque pointer to the PIB. - * @param uVector The vector to set. + * @param pApicCpu The APIC CPU state. + * @param uVector The vector to set. + */ +DECLINLINE(void) apicSetVectorInLevelPib(PAPICCPU pApicCpu, uint8_t uVector) +{ + PAPICLEVELPIB pApicPib = &pApicCpu->ApicPibLevel; + ASMAtomicBitSet((volatile void *)pApicPib, uVector); +} + + +/** + * Sets the vector in the edge-triggered PIB. + * + * @param pApicCpu The APIC CPU state. + * @param uVector The vector to set. + * @param fAutoEoi Whether this vector is subject to Hyper-V AutoEOI. + */ +static void apicSetVectorInEdgePib(PAPICCPU pApicCpu, uint8_t uVector, bool fAutoEoi) +{ + PAPICEDGEPIB pApicPib = (PAPICEDGEPIB)pApicCpu->CTX_SUFF(pvApicPib); + uint8_t const cBitsPerVector = 2; + uint8_t const cVectorsPerFrag = sizeof(pApicPib->au64VectorStates[0]) * 8 /* bits */ / cBitsPerVector; + uint8_t const idxFrag = uVector / cVectorsPerFrag; + uint8_t const cShift = (uVector % cVectorsPerFrag) * cBitsPerVector; + uint64_t const u64StateMask = (uint64_t)APIC_PIB_INTR_MASK << cShift; + uint64_t const u64NewState = ((uint64_t)(fAutoEoi ? APIC_PIB_INTR_PENDING_AUTO_EOI + : APIC_PIB_INTR_PENDING)) << cShift; + Assert(u64NewState != 0); + uint64_t u64Old = 0; + for (;;) + { + /* + * The first vector that transitions from non-pending to the pending state will make it + * to the PIB. Subsequent pending interrupt for the same vector won't update the PIB. + */ + uint64_t u64Cur = 0; + uint64_t const u64New = (u64Old & ~u64StateMask) | u64NewState; + bool const fXchg = ASMAtomicCmpXchgExU64(&pApicPib->au64VectorStates[idxFrag], u64New, u64Old, &u64Cur); + if (fXchg) + break; + + /* + * If the vector is still not pending, another vector in this fragment must have + * changed it in parallel, we must retry. Otherwise, the vector is already in the + * pending state and we're done. + */ + uint8_t const fState = (u64Cur >> cShift) & APIC_PIB_INTR_MASK; + if (fState != APIC_PIB_INTR_NOT_PENDING) + { + Assert(fState != APIC_PIB_INTR_RSVD); + break; + } + + u64Old = u64Cur; + } +} + + +/** + * Tests and clears an AutoEOI vector. + * + * @returns @c true if it's an AutoEOI vector, @c false otherwise. + * @param pVCpu The cross context virtual CPU structure. + * @param uVector The AutoEOI vector. + * @thread EMT(pVCpu). */ -DECLINLINE(void) apicSetVectorInPib(volatile void *pvPib, uint8_t uVector) +static bool apicTestAndClearAutoEoiVector(PVMCPUCC pVCpu, uint8_t uVector) { - ASMAtomicBitSet(pvPib, uVector); + VMCPU_ASSERT_EMT(pVCpu); + PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu); + AssertCompile(sizeof(pApicCpu->auAutoEoiVectors) * 8 > UINT8_MAX); + return ASMBitTestAndClear(&pApicCpu->auAutoEoiVectors, uVector); } + #if 0 /* unused */ /** * Clears the vector in an APIC Pending-Interrupt Bitmap (PIB). @@ -1946,8 +2026,8 @@ static DECLCALLBACK(void) apicInitIpi(PVMCPUCC pVCpu) /* Clear the pending-interrupt bitmaps. */ PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu); - RT_BZERO(&pApicCpu->ApicPibLevel, sizeof(APICPIB)); - RT_BZERO(pApicCpu->CTX_SUFF(pvApicPib), sizeof(APICPIB)); + RT_BZERO(&pApicCpu->ApicPibLevel, sizeof(APICLEVELPIB)); + RT_BZERO(pApicCpu->CTX_SUFF(pvApicPib), sizeof(APICEDGEPIB)); /* Clear the interrupt line states for LINT0 and LINT1 pins. */ pApicCpu->fActiveLint0 = false; @@ -2006,6 +2086,10 @@ void apicResetCpu(PVMCPUCC pVCpu, bool fResetApicBaseMsr) */ RT_BZERO(&pXApicPage->id, sizeof(pXApicPage->id)); pXApicPage->id.u8ApicId = pVCpu->idCpu; + + /* Clear all AutoEOI vectors. */ + PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu); + RT_BZERO(&pApicCpu->auAutoEoiVectors, sizeof(pApicCpu->auAutoEoiVectors)); } @@ -2518,7 +2602,16 @@ static DECLCALLBACK(int) apicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, ui || XAPIC_PPR_GET_PP(uVector) > XAPIC_PPR_GET_PP(uPpr)) { apicClearVectorInReg(&pXApicPage->irr, uVector); - apicSetVectorInReg(&pXApicPage->isr, uVector); + + /* + * With Hyper-V AutoEOI, the hypervisor implicitly performs an EOI at the + * time of delivering the interrupt. This is different from regular interrupt + * handling because it delibarately loses the ISR/PPR protection of their + * interrupt handler in the guest. In regular APIC operation, the AutoEOI + * bitmap is 0 and thus we will always end up setting the ISR. + */ + if (!apicTestAndClearAutoEoiVector(pVCpu, uVector)) + apicSetVectorInReg(&pXApicPage->isr, uVector); apicUpdatePpr(pVCpu); apicSignalNextPendingIntr(pVCpu); @@ -2711,7 +2804,6 @@ DECLCALLBACK(bool) apicPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGG Assert(pVCpu); 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); PCAPIC pApic = VM_TO_APIC(pVM); @@ -2749,9 +2841,9 @@ DECLCALLBACK(bool) apicPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGG { /** @todo posted-interrupt call to hardware */ } else { - apicSetVectorInPib(pApicCpu->CTX_SUFF(pvApicPib), uVector); - uint32_t const fAlreadySet = apicSetNotificationBitInPib((PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib)); - if (!fAlreadySet) + apicSetVectorInEdgePib(pApicCpu, uVector, fAutoEoi); + uint32_t const fNotificationAlreadySet = apicSetNotificationBitInEdgePib(pApicCpu); + if (!fNotificationAlreadySet) { Log2(("APIC: apicPostInterrupt: Setting UPDATE_APIC FF for edge-triggered intr. uVector=%#x\n", uVector)); apicSetInterruptFF(pVCpu, PDMAPICIRQ_UPDATE_PENDING); @@ -2764,8 +2856,8 @@ DECLCALLBACK(bool) apicPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGG * Level-triggered interrupts requires updating of the TMR and thus cannot be * delivered asynchronously. */ - apicSetVectorInPib(&pApicCpu->ApicPibLevel, uVector); - uint32_t const fAlreadySet = apicSetNotificationBitInPib(&pApicCpu->ApicPibLevel); + apicSetVectorInLevelPib(pApicCpu, uVector); + uint32_t const fAlreadySet = apicSetNotificationBitInLevelPib(pApicCpu); if (!fAlreadySet) { Log2(("APIC: apicPostInterrupt: Setting UPDATE_APIC FF for level-triggered intr. uVector=%#x\n", uVector)); @@ -2933,40 +3025,69 @@ static DECLCALLBACK(void) apicUpdatePendingInterrupts(PVMCPUCC pVCpu) STAM_PROFILE_START(&pApicCpu->StatUpdatePendingIntrs, a); /* Update edge-triggered pending interrupts. */ - PAPICPIB pPib = (PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib); + PAPICEDGEPIB pEdgePib = (PAPICEDGEPIB)pApicCpu->CTX_SUFF(pvApicPib); for (;;) { - uint32_t const fAlreadySet = apicClearNotificationBitInPib((PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib)); + uint32_t const fAlreadySet = apicClearNotificationBitInEdgePib(pApicCpu); if (!fAlreadySet) break; - AssertCompile(RT_ELEMENTS(pXApicPage->irr.u) == 2 * RT_ELEMENTS(pPib->au64VectorBitmap)); - for (size_t idxPib = 0, idxReg = 0; idxPib < RT_ELEMENTS(pPib->au64VectorBitmap); idxPib++, idxReg += 2) + AssertCompile(RT_ELEMENTS(pXApicPage->irr.u) == RT_ELEMENTS(pEdgePib->au64VectorStates)); + for (size_t idxReg = 0; idxReg < RT_ELEMENTS(pEdgePib->au64VectorStates); idxReg ++) { - uint64_t const u64Fragment = ASMAtomicXchgU64(&pPib->au64VectorBitmap[idxPib], 0); + uint64_t const u64Fragment = ASMAtomicXchgU64(&pEdgePib->au64VectorStates[idxReg], 0); if (u64Fragment) { - uint32_t const u32FragmentLo = RT_LO_U32(u64Fragment); - uint32_t const u32FragmentHi = RT_HI_U32(u64Fragment); - Log6Func(("edge[%u/%u]: %'016RX64: irr=%08RX32'%08RX32 |; tmr=%08RX32'%08RX32 &~\n", idxPib, idxReg, u64Fragment, + Log6Func(("edge[%u]: %'016RX64: irr=%08RX32'%08RX32 |; tmr=%08RX32'%08RX32 &~\n", idxReg, u64Fragment, pXApicPage->irr.u[idxReg].u32Reg, pXApicPage->irr.u[idxReg + 1].u32Reg, pXApicPage->tmr.u[idxReg].u32Reg, pXApicPage->tmr.u[idxReg + 1].u32Reg)); + uint32_t u32PibPending = 0; + uint32_t u32PibAutoEoi = 0; + uint8_t const cBitsPerVector = 2; + uint8_t const cVectorsPerFragment = sizeof(u64Fragment) * 8 / 2; + AssertCompile(cVectorsPerFragment == sizeof(u32PibPending) * 8); + for (unsigned i = 0; i < cVectorsPerFragment; i++) + { + AssertCompile(sizeof(u32PibAutoEoi) == sizeof(u32PibPending)); + uint8_t const cShift = i * cBitsPerVector; + uint8_t const fState = (u64Fragment >> cShift) & APIC_PIB_INTR_MASK; + switch (fState) + { + case APIC_PIB_INTR_NOT_PENDING: + break; + case APIC_PIB_INTR_PENDING_AUTO_EOI: + u32PibAutoEoi |= RT_BIT_32(i); + RT_FALL_THRU(); + case APIC_PIB_INTR_PENDING: + u32PibPending |= RT_BIT_32(i); + break; + default: + AssertReleaseMsgFailed(("Invalid APIC PIB edge-triggered vector state!\n")); + break; + } + } - pXApicPage->irr.u[idxReg].u32Reg |= u32FragmentLo; - pXApicPage->irr.u[idxReg + 1].u32Reg |= u32FragmentHi; - - pXApicPage->tmr.u[idxReg].u32Reg &= ~u32FragmentLo; - pXApicPage->tmr.u[idxReg + 1].u32Reg &= ~u32FragmentHi; + /* + * The Hyper-V spec doesn't specify how AutoEOI state for vectors take + * preceedence when multiple interrupts for the same vector are pending. + * We preserve the state that is already in the IRR. This also matches + * what we do in apicSetVectorInEdgePib(). + */ + uint32_t const u32OldIrr = pXApicPage->irr.u[idxReg].u32Reg; + uint32_t const u32NewIrrMask = u32PibPending & ~u32OldIrr; + pXApicPage->irr.u[idxReg].u32Reg |= u32PibPending; + pXApicPage->tmr.u[idxReg].u32Reg &= ~u32PibPending; + pApicCpu->auAutoEoiVectors[idxReg] |= u32PibAutoEoi & u32NewIrrMask; fHasPendingIntrs = true; } } } /* Update level-triggered pending interrupts. */ - pPib = (PAPICPIB)&pApicCpu->ApicPibLevel; + PAPICLEVELPIB pPib = (PAPICLEVELPIB)&pApicCpu->ApicPibLevel; for (;;) { - uint32_t const fAlreadySet = apicClearNotificationBitInPib((PAPICPIB)&pApicCpu->ApicPibLevel); + uint32_t const fAlreadySet = apicClearNotificationBitInLevelPib(pApicCpu); if (!fAlreadySet) break; diff --git a/src/VBox/VMM/VMMR3/GIMR3Hv.cpp b/src/VBox/VMM/VMMR3/GIMR3Hv.cpp index 85af5fe0644..2f238e2fa04 100644 --- a/src/VBox/VMM/VMMR3/GIMR3Hv.cpp +++ b/src/VBox/VMM/VMMR3/GIMR3Hv.cpp @@ -299,6 +299,7 @@ VMMR3_INT_DECL(int) gimR3HvInit(PVM pVM, PCFGMNODE pGimCfg) | GIM_HV_MISC_FEAT_TIMER_FREQ | GIM_HV_MISC_FEAT_GUEST_CRASH_MSRS //| GIM_HV_MISC_FEAT_DEBUG_MSRS + //| GIM_HV_MISC_FEAT_USE_DIRECT_SYNTH_MSRS ; /* Hypervisor recommendations to the guest. */ @@ -700,7 +701,7 @@ VMMR3_INT_DECL(void) gimR3HvReset(PVM pVM) pHvCpu->uSControlMsr = 0; pHvCpu->uSimpMsr = 0; pHvCpu->uSiefpMsr = 0; - pHvCpu->uApicAssistPageMsr = 0; + pHvCpu->uVpAssistMsr = 0; for (uint8_t idxSint = 0; idxSint < RT_ELEMENTS(pHvCpu->auSintMsrs); idxSint++) pHvCpu->auSintMsrs[idxSint] = MSR_GIM_HV_SINT_MASKED; @@ -1045,30 +1046,29 @@ VMMR3_INT_DECL(int) gimR3HvLoadDone(PVM pVM, PSSMHANDLE pSSM) /** - * Enables the Hyper-V APIC-assist page. + * Enables the Hyper-V VP Assist page. * * @returns VBox status code. * @param pVCpu The cross context virtual CPU structure. - * @param GCPhysApicAssistPage Where to map the APIC-assist page. + * @param GCPhysVpAssistPage Where to map the VP ssist page. */ -VMMR3_INT_DECL(int) gimR3HvEnableApicAssistPage(PVMCPU pVCpu, RTGCPHYS GCPhysApicAssistPage) +VMMR3_INT_DECL(int) gimR3HvEnableVpAssistPage(PVMCPU pVCpu, RTGCPHYS GCPhysVpAssistPage) { PVM pVM = pVCpu->CTX_SUFF(pVM); PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3; AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED); /* - * Map the APIC-assist-page at the specified address. + * Map the VP Assist page at the specified address. */ /** @todo this is buggy when large pages are used due to a PGM limitation, see * @bugref{7532}. Instead of the overlay style mapping, we just * rewrite guest memory directly. */ - AssertCompile(sizeof(g_abRTZero64K) >= GUEST_PAGE_SIZE); - int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysApicAssistPage, g_abRTZero64K, GUEST_PAGE_SIZE); + int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysVpAssistPage, g_abRTZero64K, GUEST_PAGE_SIZE); if (RT_SUCCESS(rc)) { /** @todo Inform APIC. */ - LogRel(("GIM%u: HyperV: Enabled APIC-assist page at %#RGp\n", pVCpu->idCpu, GCPhysApicAssistPage)); + LogRel(("GIM%u: HyperV: Enabled VP Assist page at %#RGp\n", pVCpu->idCpu, GCPhysVpAssistPage)); } else { @@ -1080,14 +1080,14 @@ VMMR3_INT_DECL(int) gimR3HvEnableApicAssistPage(PVMCPU pVCpu, RTGCPHYS GCPhysApi /** - * Disables the Hyper-V APIC-assist page. + * Disables the Hyper-V VP Assist page. * * @returns VBox status code. * @param pVCpu The cross context virtual CPU structure. */ -VMMR3_INT_DECL(int) gimR3HvDisableApicAssistPage(PVMCPU pVCpu) +VMMR3_INT_DECL(int) gimR3HvDisableVpAssistPage(PVMCPU pVCpu) { - LogRel(("GIM%u: HyperV: Disabled APIC-assist page\n", pVCpu->idCpu)); + LogRel(("GIM%u: HyperV: Disabled VP Assist page\n", pVCpu->idCpu)); /** @todo inform APIC */ return VINF_SUCCESS; } @@ -1105,28 +1105,25 @@ static DECLCALLBACK(void) gimR3HvTimerCallback(PVM pVM, TMTIMERHANDLE hTimer, vo Assert(pHvStimer->hTimer == hTimer); RT_NOREF(hTimer); - PVMCPU pVCpu = pVM->apCpusR3[pHvStimer->idCpu]; - PGIMHVCPU pHvCpu = &pVCpu->gim.s.u.HvCpu; - Assert(pHvStimer->idxStimer < RT_ELEMENTS(pHvCpu->aStatStimerFired)); - - STAM_COUNTER_INC(&pHvCpu->aStatStimerFired[pHvStimer->idxStimer]); + /** @todo This is currently broken for SMP and single-VCPU only because + * this callback NEEDS to be executed on the VCPU EMT owning the + * timer. Hence the release assertion below. + */ + if (pVM->cCpus > 1) + AssertReleaseMsgFailed(("Hyper-V Synthetic Timers not yet implemented for SMP VMs!")); + PVMCPU pVCpu = pVM->apCpusR3[pHvStimer->idCpu]; + gimHvDeliverTimerMsg(pVCpu, pHvStimer); + /* Re-arm the timer if it's periodic. Disable the timer if it's one-shot. */ uint64_t const uStimerConfig = pHvStimer->uStimerConfigMsr; - uint16_t const idxSint = MSR_GIM_HV_STIMER_GET_SINTX(uStimerConfig); - if (RT_LIKELY(idxSint < RT_ELEMENTS(pHvCpu->auSintMsrs))) + if (MSR_GIM_HV_STIMER_IS_PERIODIC(uStimerConfig)) { - uint64_t const uSint = pHvCpu->auSintMsrs[idxSint]; - if (!MSR_GIM_HV_SINT_IS_MASKED(uSint)) - { - uint8_t const uVector = MSR_GIM_HV_SINT_GET_VECTOR(uSint); - bool const fAutoEoi = MSR_GIM_HV_SINT_IS_AUTOEOI(uSint); - PDMApicHvSendInterrupt(pVCpu, uVector, fAutoEoi, XAPICTRIGGERMODE_EDGE); - } + uint64_t const uTimerCount = pHvStimer->uStimerCountMsr; /* in 100-ns units. */ + uint64_t const cNanosToNext = uTimerCount * 100 /* ns */; /* number of nanos to expiry. */ + TMTimerSetNano(pVM, hTimer, cNanosToNext); } - - /* Re-arm the timer if it's periodic. */ - if (MSR_GIM_HV_STIMER_IS_PERIODIC(uStimerConfig)) - gimHvStartStimer(pVCpu, pHvStimer); + else + pHvStimer->uStimerConfigMsr &= ~MSR_GIM_HV_STIMER_ENABLE; } @@ -1295,56 +1292,6 @@ VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool f } -/** - * Enables the Hyper-V SIM page. - * - * @returns VBox status code. - * @param pVCpu The cross context virtual CPU structure. - * @param GCPhysSimPage Where to map the SIM page. - */ -VMMR3_INT_DECL(int) gimR3HvEnableSimPage(PVMCPU pVCpu, RTGCPHYS GCPhysSimPage) -{ - PVM pVM = pVCpu->CTX_SUFF(pVM); - PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3; - AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED); - - /* - * Map the SIMP page at the specified address. - */ - /** @todo this is buggy when large pages are used due to a PGM limitation, see - * @bugref{7532}. Instead of the overlay style mapping, we just - * rewrite guest memory directly. */ - AssertCompile(sizeof(g_abRTZero64K) >= GUEST_PAGE_SIZE); - int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysSimPage, g_abRTZero64K, GUEST_PAGE_SIZE); - if (RT_SUCCESS(rc)) - { - /** @todo SIM setup. */ - LogRel(("GIM%u: HyperV: Enabled SIM page at %#RGp\n", pVCpu->idCpu, GCPhysSimPage)); - } - else - { - LogRelFunc(("GIM%u: HyperV: PGMPhysSimpleWriteGCPhys failed. rc=%Rrc\n", pVCpu->idCpu, rc)); - rc = VERR_GIM_OPERATION_FAILED; - } - return rc; -} - - -/** - * Disables the Hyper-V SIM page. - * - * @returns VBox status code. - * @param pVCpu The cross context virtual CPU structure. - */ -VMMR3_INT_DECL(int) gimR3HvDisableSimPage(PVMCPU pVCpu) -{ - LogRel(("GIM%u: HyperV: Disabled SIM page\n", pVCpu->idCpu)); - /** @todo SIM teardown. */ - return VINF_SUCCESS; -} - - - /** * Disables the Hyper-V TSC page. * diff --git a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp index e7239fddd8c..bd4d12f68cf 100644 --- a/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp +++ b/src/VBox/VMM/VMMR3/target-x86/APICR3-x86.cpp @@ -98,12 +98,12 @@ DECLCALLBACK(int) apicR3SetHvCompatMode(PVM pVM, bool fHyperVCompatMode) /** - * Helper for dumping an APIC pending-interrupt bitmap. + * Helper for dumping a level-sensitive APIC pending-interrupt bitmap. * * @param pApicPib The pending-interrupt bitmap. * @param pHlp The debug output helper. */ -static void apicR3DbgInfoPib(PCAPICPIB pApicPib, PCDBGFINFOHLP pHlp) +static void apicR3DbgInfoLevelPib(PCAPICLEVELPIB pApicPib, PCDBGFINFOHLP pHlp) { /* Copy the pending-interrupt bitmap as an APIC 256-bit sparse register. */ XAPIC256BITREG ApicReg; @@ -125,6 +125,66 @@ static void apicR3DbgInfoPib(PCAPICPIB pApicPib, PCDBGFINFOHLP pHlp) } + +/** + * Helper for dumping a edge-triggered APIC pending-interrupt bitmap. + * + * @param pApicPib The pending-interrupt bitmap. + * @param pHlp The debug output helper. + */ +static void apicR3DbgInfoEdgePib(PCAPICEDGEPIB pApicPib, PCDBGFINFOHLP pHlp) +{ + /* Raw dump */ + ssize_t const cFragments = RT_ELEMENTS(pApicPib->au64VectorStates); + pHlp->pfnPrintf(pHlp, " "); + for (ssize_t idxFragment = cFragments - 1; idxFragment >= 0; idxFragment--) + { + uint64_t const uFragment = pApicPib->au64VectorStates[idxFragment]; + if (idxFragment == 3) + pHlp->pfnPrintf(pHlp, "\n "); + pHlp->pfnPrintf(pHlp, "%016RX64", uFragment); + } + pHlp->pfnPrintf(pHlp, "\n"); + + /* Dump each vector */ + uint32_t cPending = 0; + unsigned const cVectorsPerFragment = 2; + pHlp->pfnPrintf(pHlp, " Pending:"); + for (ssize_t idxFragment = cFragments - 1; idxFragment >= 0; idxFragment--) + { + uint64_t uFragment = pApicPib->au64VectorStates[idxFragment]; + if (uFragment) + { + for (ssize_t idxVector = 31; idxVector >= 0; idxVector--) + { + unsigned const cShift = idxVector * cVectorsPerFragment; + uint8_t const fState = (uFragment >> cShift) & APIC_PIB_INTR_MASK; + uint8_t const uVector = idxFragment * 32 + idxVector; + switch (fState) + { + case APIC_PIB_INTR_PENDING: + pHlp->pfnPrintf(pHlp, " (%#02x Regular)", uVector); + ++cPending; + break; + case APIC_PIB_INTR_PENDING_AUTO_EOI: + pHlp->pfnPrintf(pHlp, " (%#02x AutoEoi)", uVector); + ++cPending; + break; + case APIC_PIB_INTR_RSVD: + pHlp->pfnPrintf(pHlp, " (%#02x Invalid)", uVector); + case APIC_PIB_INTR_NOT_PENDING: + default: + break; + } + } + } + } + if (!cPending) + pHlp->pfnPrintf(pHlp, " None"); + pHlp->pfnPrintf(pHlp, "\n"); +} + + /** * Dumps basic APIC state. * @@ -142,10 +202,10 @@ static DECLCALLBACK(void) apicR3Info(PVM pVM, PCDBGFINFOHLP pHlp, const char *ps PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu); apicR3CommonDbgInfo(pVCpu, pHlp, pApicCpu->uApicBaseMsr); pHlp->pfnPrintf(pHlp, " ESR Internal = %#x\n", pApicCpu->uEsrInternal); - pHlp->pfnPrintf(pHlp, " PIB\n"); - apicR3DbgInfoPib((PCAPICPIB)pApicCpu->pvApicPibR3, pHlp); - pHlp->pfnPrintf(pHlp, " Level PIB\n"); - apicR3DbgInfoPib(&pApicCpu->ApicPibLevel, pHlp); + pHlp->pfnPrintf(pHlp, " Edge PIB:\n"); + apicR3DbgInfoEdgePib((PCAPICEDGEPIB)pApicCpu->pvApicPibR3, pHlp); + pHlp->pfnPrintf(pHlp, " Level PIB:\n"); + apicR3DbgInfoLevelPib(&pApicCpu->ApicPibLevel, pHlp); } @@ -239,8 +299,8 @@ static void apicR3DumpState(PVMCPU pVCpu, const char *pszPrefix, uint32_t uVersi LogRel(("APIC%u: uTimerCCR = %#RX32\n", pVCpu->idCpu, pXApicPage->timer_ccr.u32CurrentCount)); /* The PIBs. */ - LogRel(("APIC%u: Edge PIB : %.*Rhxs\n", pVCpu->idCpu, sizeof(APICPIB), pApicCpu->pvApicPibR3)); - LogRel(("APIC%u: Level PIB: %.*Rhxs\n", pVCpu->idCpu, sizeof(APICPIB), &pApicCpu->ApicPibLevel)); + LogRel(("APIC%u: Edge PIB : %.*Rhxs\n", pVCpu->idCpu, sizeof(APICEDGEPIB), pApicCpu->pvApicPibR3)); + LogRel(("APIC%u: Level PIB: %.*Rhxs\n", pVCpu->idCpu, sizeof(APICLEVELPIB), &pApicCpu->ApicPibLevel)); /* The LINT0, LINT1 interrupt line active states. */ LogRel(("APIC%u: fActiveLint0 = %RTbool\n", pVCpu->idCpu, pApicCpu->fActiveLint0)); @@ -786,7 +846,7 @@ static int apicR3InitState(PVM pVM) */ Assert(pApic->pvApicPibR3 == NIL_RTR3PTR); Assert(pApic->pvApicPibR0 == NIL_RTR0PTR); - pApic->cbApicPib = RT_ALIGN_Z(pVM->cCpus * sizeof(APICPIB), HOST_PAGE_SIZE_DYNAMIC); + pApic->cbApicPib = RT_ALIGN_Z(pVM->cCpus * sizeof(APICEDGEPIB), HOST_PAGE_SIZE_DYNAMIC); size_t const cHostPages = pApic->cbApicPib >> HOST_PAGE_SHIFT_DYNAMIC; if (cHostPages == 1) { @@ -845,7 +905,7 @@ static int apicR3InitState(PVM pVM) AssertLogRelReturn(pApicCpu->HCPhysApicPage != NIL_RTHCPHYS || fDriverless, VERR_INTERNAL_ERROR); /* Associate the per-VCPU PIB pointers to the per-VM PIB mapping. */ - uint32_t const offApicPib = idCpu * sizeof(APICPIB); + uint32_t const offApicPib = idCpu * sizeof(APICEDGEPIB); pApicCpu->pvApicPibR0 = !fDriverless ? (RTR0PTR)((RTR0UINTPTR)pApic->pvApicPibR0 + offApicPib) : NIL_RTR0PTR; pApicCpu->pvApicPibR3 = (RTR3PTR)((RTR3UINTPTR)pApic->pvApicPibR3 + offApicPib); diff --git a/src/VBox/VMM/include/APICInternal.h b/src/VBox/VMM/include/APICInternal.h index bd4ba61d6db..ff81df4d35f 100644 --- a/src/VBox/VMM/include/APICInternal.h +++ b/src/VBox/VMM/include/APICInternal.h @@ -155,21 +155,57 @@ typedef enum APICMSRACCESS */ #define APIC_CACHE_LINE_SIZE 128 +/** @def APIC_PIB_INTR_XXX. + * The interrupt state of each vector is represented by 2 bits. + * This is used only for edge-triggered interrupts. + */ +/** The interrupt is not pending. */ +#define APIC_PIB_INTR_NOT_PENDING 0x0 +/** The interrupt is pending. */ +#define APIC_PIB_INTR_PENDING 0x1 +/** The interrupt is pending and requires Hyper-V AutoEoi handling. */ +#define APIC_PIB_INTR_PENDING_AUTO_EOI 0x2 +/** Reserved/invalid - not currently used. */ +#define APIC_PIB_INTR_RSVD 0x3 +/** The mask of the per-vector edge-triggered PIB interrupt state. */ +#define APIC_PIB_INTR_MASK 0x3 + + +/** + * APIC Pending-Interrupt Bitmap (PIB) for edge-triggered interrupts. + */ +typedef struct APICEDGEPIB +{ + /** Interrupt state for each vector, see APIC_PIB_INTR_XXX. */ + uint64_t volatile au64VectorStates[8]; + uint32_t volatile fOutstandingNotification; + uint8_t au8Reserved[APIC_CACHE_LINE_SIZE - sizeof(uint32_t) - (sizeof(uint64_t) * 8)]; +} APICEDGEPIB; +AssertCompileMemberOffset(APICEDGEPIB, fOutstandingNotification, 512 / 8); +AssertCompileSize(APICEDGEPIB, APIC_CACHE_LINE_SIZE); +/** Pointer to a pending-interrupt bitmap. */ +typedef APICEDGEPIB *PAPICEDGEPIB; +/** Pointer to a const pending-interrupt bitmap. */ +typedef const APICEDGEPIB *PCAPICEDGEPIB; + /** - * APIC Pending-Interrupt Bitmap (PIB). + * APIC Pending-Interrupt Bitmap (PIB) for level-sensitive interrupts. */ -typedef struct APICPIB +typedef struct APICLEVELPIB { uint64_t volatile au64VectorBitmap[4]; uint32_t volatile fOutstandingNotification; uint8_t au8Reserved[APIC_CACHE_LINE_SIZE - sizeof(uint32_t) - (sizeof(uint64_t) * 4)]; -} APICPIB; -AssertCompileMemberOffset(APICPIB, fOutstandingNotification, 256 / 8); -AssertCompileSize(APICPIB, APIC_CACHE_LINE_SIZE); +} APICLEVELPIB; +AssertCompileMemberOffset(APICLEVELPIB, fOutstandingNotification, 256 / 8); +AssertCompileSize(APICLEVELPIB, APIC_CACHE_LINE_SIZE); /** Pointer to a pending-interrupt bitmap. */ -typedef APICPIB *PAPICPIB; +typedef APICLEVELPIB *PAPICLEVELPIB; /** Pointer to a const pending-interrupt bitmap. */ -typedef const APICPIB *PCAPICPIB; +typedef const APICLEVELPIB *PCAPICLEVELPIB; +/* Size must be identical since we alloc all per-VCPU PIBs contiguously per-VM + and point at the right offsets for each VCPU. */ +AssertCompile(sizeof(APICLEVELPIB) == sizeof(APICEDGEPIB)); /** * APIC PDM instance data (per-VM). @@ -203,7 +239,7 @@ typedef struct APIC /** The ring-3 device instance. */ PPDMDEVINSR3 pDevInsR3; - /** @name The APIC pending-interrupt bitmap (PIB). + /** @name The contiguous pending-interrupt bitmaps (PIB) of all VCPUs. * @{ */ /** The host-context physical address of the PIB. */ RTHCPHYS HCPhysApicPib; @@ -307,7 +343,7 @@ typedef struct APICCPU /** The APIC PIB virtual address - R3 ptr. */ R3PTRTYPE(void *) pvApicPibR3; /** The APIC PIB for level-sensitive interrupts. */ - APICPIB ApicPibLevel; + APICLEVELPIB ApicPibLevel; /** @} */ /** @name Other miscellaneous data. @@ -320,6 +356,8 @@ typedef struct APICCPU uint8_t auAlignment2[6]; /** The source tags corresponding to each interrupt vector (debugging). */ uint32_t auSrcTags[256]; + /** Hyper-V Auto EOI vectors. */ + uint32_t auAutoEoiVectors[8]; /** @} */ /** @name The APIC timer. @@ -421,6 +459,7 @@ typedef APICCPU *PAPICCPU; /** Pointer to a const APIC VMCPU instance data. */ typedef APICCPU const *PCAPICCPU; AssertCompileMemberAlignment(APICCPU, uApicBaseMsr, 8); +AssertCompileMemberSize(APICCPU, auAutoEoiVectors, 32 /* =256 bits, one per vector */); void apicHintTimerFreq(PPDMDEVINS pDevIns, PAPICCPU pApicCpu, uint32_t uInitialCount, uint8_t uTimerShift); diff --git a/src/VBox/VMM/include/GIMHvInternal.h b/src/VBox/VMM/include/GIMHvInternal.h index 0020ec76fbe..6ca1bbfec51 100644 --- a/src/VBox/VMM/include/GIMHvInternal.h +++ b/src/VBox/VMM/include/GIMHvInternal.h @@ -277,9 +277,9 @@ /** Access to APIC TPR (Task Priority) register (R/W) */ #define MSR_GIM_HV_TPR UINT32_C(0x40000072) /** Enables lazy EOI processing (R/W) */ -#define MSR_GIM_HV_APIC_ASSIST_PAGE UINT32_C(0x40000073) +#define MSR_GIM_HV_VP_ASSIST UINT32_C(0x40000073) /** End of range 3. */ -#define MSR_GIM_HV_RANGE3_LAST MSR_GIM_HV_APIC_ASSIST_PAGE +#define MSR_GIM_HV_RANGE3_LAST MSR_GIM_HV_VP_ASSIST /** Start of range 4. */ #define MSR_GIM_HV_RANGE4_FIRST UINT32_C(0x40000080) @@ -501,15 +501,15 @@ AssertCompile(MSR_GIM_HV_RANGE11_FIRST <= MSR_GIM_HV_RANGE11_LAST); #define MSR_GIM_HV_GUEST_OS_ID_BUILD(a) (uint32_t)((a) & 0xffff) /** @} */ -/** @name Hyper-V MSR - APIC-assist page (MSR_GIM_HV_APIC_ASSIST_PAGE). +/** @name Hyper-V MSR - VP assist (MSR_GIM_HV_VP_ASSIST). * @{ */ /** Guest-physical page frame number of the APIC-assist page. */ -#define MSR_GIM_HV_APICASSIST_GUEST_PFN(a) ((a) >> 12) +#define MSR_GIM_HV_VP_ASSIST_GUEST_PFN(a) ((a) >> 12) /** The APIC-assist page enable mask. */ -#define MSR_GIM_HV_APICASSIST_PAGE_ENABLE RT_BIT_64(0) +#define MSR_GIM_HV_VP_ASSIST_PAGE_ENABLE RT_BIT_64(0) /** Whether the APIC-assist page is enabled or not. */ -#define MSR_GIM_HV_APICASSIST_PAGE_IS_ENABLED(a) RT_BOOL((a) & MSR_GIM_HV_APICASSIST_PAGE_ENABLE) +#define MSR_GIM_HV_VP_ASSIST_PAGE_IS_ENABLED(a) RT_BOOL((a) & MSR_GIM_HV_VP_ASSIST_PAGE_ENABLE) /** @} */ /** @name Hyper-V MSR - Synthetic Interrupt Event Flags page @@ -552,10 +552,20 @@ AssertCompile(MSR_GIM_HV_RANGE11_FIRST <= MSR_GIM_HV_RANGE11_LAST); #define MSR_GIM_HV_STIMER_AUTO_ENABLE RT_BIT_64(3) /** Whether Stimer is enabled or not. */ #define MSR_GIM_HV_STIMER_IS_AUTO_ENABLED(a) RT_BOOL((a) & MSR_GIM_HV_STIMER_AUTO_ENABLE) +/** The Stimer direct-mode APIC vector mask (bits 11:4). */ +#define MSR_GIM_HV_STIMER_VECTOR_MASK UINT64_C(0xff0) +/** Gets the APIC vector when direct-mode is used. */ +#define MSR_GIM_HV_STIMER_GET_VECTOR(a) ((a) & MSR_GIM_HV_STIMER_VECTOR_MASK) /** The Stimer SINTx mask (bits 16:19). */ #define MSR_GIM_HV_STIMER_SINTX UINT64_C(0xf0000) +/** The Stimer direct mode mask. */ +#define MSR_GIM_HV_STIMER_DIRECT_MODE RT_BIT_64(12) +/** Whether direct mode is enabled. */ +#define MSR_GIM_HV_STIMER_IS_DIRECT_MODE(a) RT_BOOL((a) & MSR_GIM_HV_STIMER_DIRECT_MODE) +/** THe SINT source mask. */ +#define MSR_GIM_HV_STIMER_SINT_MASK 0xf /** Gets the Stimer synthetic interrupt source. */ -#define MSR_GIM_HV_STIMER_GET_SINTX(a) (((a) >> 16) & 0xf) +#define MSR_GIM_HV_STIMER_GET_SINTX(a) (((a) >> 16) & MSR_GIM_HV_STIMER_SINT_MASK) /** The Stimer valid read/write mask. */ #define MSR_GIM_HV_STIMER_RW_VALID ( MSR_GIM_HV_STIMER_ENABLE | MSR_GIM_HV_STIMER_PERIODIC \ | MSR_GIM_HV_STIMER_LAZY | MSR_GIM_HV_STIMER_AUTO_ENABLE \ @@ -572,19 +582,18 @@ AssertCompile(MSR_GIM_HV_RANGE11_FIRST <= MSR_GIM_HV_RANGE11_LAST); #define GIM_HV_VENDOR_MICROSOFT "Microsoft Hv" /** - * Hyper-V APIC-assist (HV_REFERENCE_TSC_PAGE) structure placed in the TSC - * reference page. + * Hyper-V EOI Assist structure in the VP Assist page. */ -typedef struct GIMHVAPICASSIST +typedef struct GIMHVEOIASSIST { uint32_t fNoEoiRequired : 1; uint32_t u31Reserved0 : 31; -} GIMHVAPICASSIST; +} GIMHVEOIASSIST; /** Pointer to Hyper-V reference TSC. */ -typedef GIMHVAPICASSIST *PGIMHVAPICASSIST; +typedef GIMHVEOIASSIST *PGIMHVEOIASSIST; /** Pointer to a const Hyper-V reference TSC. */ -typedef GIMHVAPICASSIST const *PCGIMHVAPICASSIST; -AssertCompileSize(GIMHVAPICASSIST, 4); +typedef GIMHVEOIASSIST const *PCGIMHVEOIASSIST; +AssertCompileSize(GIMHVEOIASSIST, 4); /** * Hypercall parameter type. @@ -974,9 +983,9 @@ typedef struct GIMHVMSGHDR uint16_t uRsvd; union { - uint64_t uOriginatorId; + uint64_t uOriginationId; uint64_t uPartitionId; - uint64_t uPortId; + uint32_t uPortId; } msgid; } GIMHVMSGHDR; /** Pointer to a synthetic interrupt message header. */ @@ -991,15 +1000,34 @@ AssertCompileSize(GIMHVMSGHDR, GIM_HV_MSG_SIZE - GIM_HV_MSG_MAX_PAYLOAD_SIZE); */ typedef struct GIMHVMSG { - GIMHVMSGHDR MsgHdr; - uint64_t aPayload[GIM_HV_MSG_MAX_PAYLOAD_UNITS]; + /** Header. */ + GIMHVMSGHDR Header; + + /** Payload. */ + union + { + /** Timer expiration payload. */ + struct + { + /** Index of the synthetic timer */ + uint32_t idxStimer; + uint32_t uRsvd0; + /** The absolute expiration time in 100-ns units. */ + uint64_t uExpirationTime; + /** The absolute message delivery time in 100-ns units. */ + uint64_t uDeliveryTime; + } timer; + /* Generic payload units view. */ + uint64_t aPayload[GIM_HV_MSG_MAX_PAYLOAD_UNITS]; + /* Generic payload size view. */ + uint8_t abPayload[GIM_HV_MSG_MAX_PAYLOAD_SIZE]; + } u; } GIMHVMSG; /** Pointer to a synthetic interrupt message. */ typedef GIMHVMSG *PGIMHVMSG; AssertCompileSize(GIMHVMSG, GIM_HV_MSG_SIZE); /** @} */ - /** @name Hyper-V hypercall parameters. * @{ */ /** @@ -1292,12 +1320,18 @@ typedef struct GIMHVSTIMER TMTIMERHANDLE hTimer; /** Virtual CPU ID this timer belongs to (for reverse mapping). */ VMCPUID idCpu; - /** The index of this timer in the auStimers array (for reverse mapping). */ + /** The index of this timer in the aStimers array (for reverse mapping). */ uint32_t idxStimer; /** Synthetic timer config MSR. */ uint64_t uStimerConfigMsr; /** Synthetic timer count MSR. */ uint64_t uStimerCountMsr; + /** The absolute expiration time in 100-ns units. */ + uint64_t uExpirationTime; + /** Whether a message is pending for delivery. */ + bool fMsgPending; + /** Padding. */ + bool afAlignment0[7]; } GIMHVSTIMER; /** Pointer to per-VCPU Hyper-V synthetic timer. */ typedef GIMHVSTIMER *PGIMHVSTIMER; @@ -1319,8 +1353,8 @@ typedef struct GIMHVCPU uint64_t auSintMsrs[GIM_HV_SINT_COUNT]; /** Synethtic interrupt events flag page MSR. */ uint64_t uSiefpMsr; - /** APIC-assist page MSR. */ - uint64_t uApicAssistPageMsr; + /** VP assist MSR. */ + uint64_t uVpAssistMsr; /** Synthetic interrupt control MSR. */ uint64_t uSControlMsr; /** Synthetic timers. */ @@ -1361,8 +1395,8 @@ VMMR3_INT_DECL(int) gimR3HvDisableSiefPage(PVMCPU pVCpu); VMMR3_INT_DECL(int) gimR3HvEnableSiefPage(PVMCPU pVCpu, RTGCPHYS GCPhysSiefPage); VMMR3_INT_DECL(int) gimR3HvEnableSimPage(PVMCPU pVCpu, RTGCPHYS GCPhysSimPage); VMMR3_INT_DECL(int) gimR3HvDisableSimPage(PVMCPU pVCpu); -VMMR3_INT_DECL(int) gimR3HvDisableApicAssistPage(PVMCPU pVCpu); -VMMR3_INT_DECL(int) gimR3HvEnableApicAssistPage(PVMCPU pVCpu, RTGCPHYS GCPhysTscPage); +VMMR3_INT_DECL(int) gimR3HvDisableVpAssistPage(PVMCPU pVCpu); +VMMR3_INT_DECL(int) gimR3HvEnableVpAssistPage(PVMCPU pVCpu, RTGCPHYS GCPhysTscPage); VMMR3_INT_DECL(int) gimR3HvDisableTscPage(PVM pVM); VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool fUseThisTscSeq, uint32_t uTscSeq); VMMR3_INT_DECL(int) gimR3HvDisableHypercallPage(PVM pVM); @@ -1385,13 +1419,12 @@ VMM_INT_DECL(bool) gimHvShouldTrapXcptUD(PVMCPU pVCpu); VMM_INT_DECL(VBOXSTRICTRC) gimHvXcptUD(PVMCPUCC pVCpu, PCPUMCTX pCtx, PDISSTATE pDis, uint8_t *pcbInstr); VMM_INT_DECL(VBOXSTRICTRC) gimHvHypercall(PVMCPUCC pVCpu, PCPUMCTX pCtx); VMM_INT_DECL(VBOXSTRICTRC) gimHvHypercallEx(PVMCPUCC pVCpu, PCPUMCTX pCtx, unsigned uDisOpcode, uint8_t cbInstr); +VMM_INT_DECL(void) gimHvDeliverTimerMsg(PVMCPUCC pVCpu, PGIMHVSTIMER pHvStimer); #if !defined(VBOX_VMM_TARGET_ARMV8) VMM_INT_DECL(VBOXSTRICTRC) gimHvReadMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue); VMM_INT_DECL(VBOXSTRICTRC) gimHvWriteMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uRawValue); #endif -VMM_INT_DECL(void) gimHvStartStimer(PVMCPUCC pVCpu, PCGIMHVSTIMER pHvStimer); - RT_C_DECLS_END #endif /* !VMM_INCLUDED_SRC_include_GIMHvInternal_h */