/** * Enables the Hyper-V TSC page. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param GCPhysTscPage Where to map the TSC page. * @param fUseThisTscSeq Whether to set the TSC sequence number to the one * specified in @a uTscSeq. * @param uTscSeq The TSC sequence value to use. Ignored if * @a fUseThisTscSeq is false. */ VMMR3_INT_DECL(int) gimR3HvEnableTscPage(PVM pVM, RTGCPHYS GCPhysTscPage, bool fUseThisTscSeq, uint32_t uTscSeq) { PPDMDEVINSR3 pDevIns = pVM->gim.s.pDevInsR3; PGIMMMIO2REGION pRegion = &pVM->gim.s.u.Hv.aMmio2Regions[GIM_HV_REF_TSC_PAGE_REGION_IDX]; AssertPtrReturn(pDevIns, VERR_GIM_DEVICE_NOT_REGISTERED); int rc; if (pRegion->fMapped) { /* * Is it already enabled at the given guest-address? */ if (pRegion->GCPhysPage == GCPhysTscPage) return VINF_SUCCESS; /* * If it's mapped at a different address, unmap the previous address. */ rc = gimR3HvDisableTscPage(pVM); AssertRC(rc); } /* * Map the TSC-page at the specified address. */ Assert(!pRegion->fMapped); rc = GIMR3Mmio2Map(pVM, pRegion, GCPhysTscPage); if (RT_SUCCESS(rc)) { Assert(pRegion->GCPhysPage == GCPhysTscPage); /* * Update the TSC scale. Windows guests expect a non-zero TSC sequence, otherwise * they fallback to using the reference count MSR which is not ideal in terms of VM-exits. * * Also, Hyper-V normalizes the time in 10 MHz, see: * http://technet.microsoft.com/it-it/sysinternals/dn553408%28v=vs.110%29 */ PGIMHVREFTSC pRefTsc = (PGIMHVREFTSC)pRegion->pvPageR3; Assert(pRefTsc); uint64_t const u64TscKHz = TMCpuTicksPerSecond(pVM) / UINT64_C(1000); uint32_t u32TscSeq = 1; if ( fUseThisTscSeq && uTscSeq < UINT32_C(0xfffffffe)) u32TscSeq = uTscSeq + 1; pRefTsc->u32TscSequence = u32TscSeq; pRefTsc->u64TscScale = ((INT64_C(10000) << 32) / u64TscKHz) << 32; pRefTsc->i64TscOffset = 0; LogRel(("GIM: HyperV: Enabled TSC page at %#RGp - u64TscScale=%#RX64 u64TscKHz=%#RX64 (%'RU64) Seq=%#RU32\n", GCPhysTscPage, pRefTsc->u64TscScale, u64TscKHz, u64TscKHz, pRefTsc->u32TscSequence)); TMR3CpuTickParavirtEnable(pVM); return VINF_SUCCESS; } else LogRelFunc(("GIMR3Mmio2Map failed. rc=%Rrc\n", rc)); return VERR_GIM_OPERATION_FAILED; }
/** * Enables the KVM VCPU system-time structure. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param pVCpu Pointer to the VMCPU. * * @remarks Don't do any release assertions here, these can be triggered by * guest R0 code. */ VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVM pVM, PVMCPU pVCpu) { PGIMKVM pKvm = &pVM->gim.s.u.Kvm; PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu; /* * Validate the mapping address first. */ if (!PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime)) { LogRel(("GIM: KVM: VCPU%3d: Invalid physical addr requested for mapping system-time struct. GCPhysSystemTime=%#RGp\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime)); return VERR_GIM_OPERATION_FAILED; } /* * Construct the system-time struct. */ GIMKVMSYSTEMTIME SystemTime; RT_ZERO(SystemTime); SystemTime.u32Version = pKvmCpu->u32SystemTimeVersion; SystemTime.u64NanoTS = pKvmCpu->uVirtNanoTS; SystemTime.u64Tsc = pKvmCpu->uTsc; SystemTime.fFlags = pKvmCpu->fSystemTimeFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE; /* * How the guest calculates the system time (nanoseconds): * * tsc = rdtsc - SysTime.u64Tsc * if (SysTime.i8TscShift >= 0) * tsc <<= i8TscShift; * else * tsc >>= -i8TscShift; * time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS */ uint64_t u64TscFreq = pKvm->cTscTicksPerSecond; SystemTime.i8TscShift = 0; while (u64TscFreq > 2 * RT_NS_1SEC_64) { u64TscFreq >>= 1; SystemTime.i8TscShift--; } uint32_t uTscFreqLo = (uint32_t)u64TscFreq; while (uTscFreqLo <= RT_NS_1SEC) { uTscFreqLo <<= 1; SystemTime.i8TscShift++; } SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo); /* * Update guest memory with the system-time struct. */ Assert(!(SystemTime.u32Version & UINT32_C(1))); int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME)); if (RT_SUCCESS(rc)) { LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 " "fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, SystemTime.u32TscScale, SystemTime.i8TscShift, SystemTime.u32Version, SystemTime.fFlags, pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS)); TMR3CpuTickParavirtEnable(pVM); } else LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, rc)); return rc; }