/**
 * 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;
}
Esempio n. 2
0
/**
 * 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;
}