VirtualBox

Changeset 86121 in vbox for trunk/src/VBox/VMM/VMMR3


Ignore:
Timestamp:
Sep 14, 2020 4:56:09 PM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
140355
Message:

VMM/GIM: Fix handling KVM system-time struct. to work from both ring-0 and ring-3 (earlier it relied on MSR writes being handled in ring-0).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMR3/GIMKvm.cpp

    r84139 r86121  
    8686
    8787/**
     88 * Updates the KVM VCPU system-time structure in guest memory.
     89 *
     90 * @returns VBox status code.
     91 * @param   pVM     The cross context VM structure.
     92 * @param   pVCpu   The cross context virtual CPU structure.
     93 *
     94 * @remarks This must be called after the system time MSR value has been updated.
     95 */
     96static int gimR3KvmUpdateSystemTime(PVM pVM, PVMCPU pVCpu)
     97{
     98    PGIMKVM    pKvm    = &pVM->gim.s.u.Kvm;
     99    PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;
     100
     101    /*
     102     * Validate the MSR has the enable bit and the guest's system time struct. address.
     103     */
     104    MSR_GIM_KVM_SYSTEM_TIME_IS_ENABLED(pKvmCpu->u64SystemTimeMsr);
     105    if (!PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime))
     106    {
     107        LogRel(("GIM: KVM: VCPU%3d: Invalid physical addr requested for mapping system-time struct. GCPhysSystemTime=%#RGp\n",
     108               pVCpu->idCpu, pKvmCpu->GCPhysSystemTime));
     109        return VERR_GIM_OPERATION_FAILED;
     110    }
     111
     112    VMSTATE const enmVMState = pVM->enmVMState;
     113    bool const fRunning = VMSTATE_IS_RUNNING(enmVMState);
     114    Assert(!(pKvmCpu->u32SystemTimeVersion & UINT32_C(1)));
     115
     116    /*
     117     * Construct a system-time struct.
     118     */
     119    GIMKVMSYSTEMTIME SystemTime;
     120    RT_ZERO(SystemTime);
     121    SystemTime.u32Version  = pKvmCpu->u32SystemTimeVersion + !!fRunning;
     122    SystemTime.u64NanoTS   = pKvmCpu->uVirtNanoTS;
     123    SystemTime.u64Tsc      = pKvmCpu->uTsc;
     124    SystemTime.fFlags      = pKvmCpu->fSystemTimeFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE;
     125
     126    /*
     127     * How the guest calculates the system time (nanoseconds):
     128     *
     129     * tsc = rdtsc - SysTime.u64Tsc
     130     * if (SysTime.i8TscShift >= 0)
     131     *     tsc <<= i8TscShift;
     132     * else
     133     *     tsc >>= -i8TscShift;
     134     * time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS
     135     */
     136    uint64_t u64TscFreq   = pKvm->cTscTicksPerSecond;
     137    SystemTime.i8TscShift = 0;
     138    while (u64TscFreq > 2 * RT_NS_1SEC_64)
     139    {
     140        u64TscFreq >>= 1;
     141        SystemTime.i8TscShift--;
     142    }
     143    uint32_t uTscFreqLo = (uint32_t)u64TscFreq;
     144    while (uTscFreqLo <= RT_NS_1SEC)
     145    {
     146        uTscFreqLo <<= 1;
     147        SystemTime.i8TscShift++;
     148    }
     149    SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo);
     150
     151    /*
     152     * For informational purposes, back-calculate the exact TSC frequency the guest will see.
     153     * Note that the frequency is in kHz, not Hz, since that's what Linux uses.
     154     */
     155    uint64_t uTscKHz = (RT_NS_1MS_64 << 32) / SystemTime.u32TscScale;
     156    if (SystemTime.i8TscShift < 0)
     157        uTscKHz <<= -SystemTime.i8TscShift;
     158    else
     159        uTscKHz >>=  SystemTime.i8TscShift;
     160
     161    /*
     162     * Update guest memory with the system-time struct.
     163     *
     164     * We update the struct with an incremented, odd version field to indicate to the guest
     165     * that the memory is being updated concurrently by the host and it should discard any
     166     * data from this struct when it reads an odd version.
     167     *
     168     * When the VM is not running, we don't need to do this two step update for obvious
     169     * reasons and so we skip it.
     170     */
     171    if (fRunning)
     172        Assert(SystemTime.u32Version & UINT32_C(1));
     173    else
     174        Assert(!(SystemTime.u32Version & UINT32_C(1)));
     175
     176    int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME));
     177    if (RT_SUCCESS(rc))
     178    {
     179        LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 "
     180                "fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64 TscKHz=%RU64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime,
     181                SystemTime.u32TscScale, SystemTime.i8TscShift, SystemTime.u32Version + !!fRunning, SystemTime.fFlags,
     182                pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS, uTscKHz));
     183        TMR3CpuTickParavirtEnable(pVM);
     184    }
     185    else
     186    {
     187        LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n", pVCpu->idCpu,
     188                pKvmCpu->GCPhysSystemTime, rc));
     189    }
     190
     191    if (fRunning)
     192    {
     193        ++SystemTime.u32Version;
     194        Assert(!(SystemTime.u32Version & UINT32_C(1)));
     195        rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime + RT_UOFFSETOF(GIMKVMSYSTEMTIME, u32Version),
     196                                          &SystemTime.u32Version, sizeof(SystemTime.u32Version));
     197        if (RT_FAILURE(rc))
     198        {
     199            LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. while updating version field at %#RGp. rc=%Rrc\n",
     200                    pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, rc));
     201            return rc;
     202        }
     203
     204        /* Update the version so our next write will start with an even value. */
     205        pKvmCpu->u32SystemTimeVersion += 2;
     206    }
     207
     208    return rc;
     209}
     210
     211
     212/**
    88213 * Initializes the KVM GIM provider.
    89214 *
     
    340465            Assert(!TMVirtualIsTicking(pVM));       /* paranoia. */
    341466            Assert(!TMCpuTickIsTicking(pVCpu));
    342             gimR3KvmEnableSystemTime(pVM, pVCpu);
     467            gimR3KvmUpdateSystemTime(pVM, pVCpu);
    343468        }
    344469    }
     
    352477
    353478    return VINF_SUCCESS;
    354 }
    355 
    356 
    357 /**
    358  * Enables the KVM VCPU system-time structure.
    359  *
    360  * @returns VBox status code.
    361  * @param   pVM                The cross context VM structure.
    362  * @param   pVCpu              The cross context virtual CPU structure.
    363  *
    364  * @remarks Don't do any release assertions here, these can be triggered by
    365  *          guest R0 code.
    366  */
    367 VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVM pVM, PVMCPU pVCpu)
    368 {
    369     PGIMKVM    pKvm    = &pVM->gim.s.u.Kvm;
    370     PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;
    371 
    372     /*
    373      * Validate the mapping address first.
    374      */
    375     if (!PGMPhysIsGCPhysNormal(pVM, pKvmCpu->GCPhysSystemTime))
    376     {
    377         LogRel(("GIM: KVM: VCPU%3d: Invalid physical addr requested for mapping system-time struct. GCPhysSystemTime=%#RGp\n",
    378                pVCpu->idCpu, pKvmCpu->GCPhysSystemTime));
    379         return VERR_GIM_OPERATION_FAILED;
    380     }
    381 
    382     /*
    383      * Construct the system-time struct.
    384      */
    385     GIMKVMSYSTEMTIME SystemTime;
    386     RT_ZERO(SystemTime);
    387     SystemTime.u32Version  = pKvmCpu->u32SystemTimeVersion;
    388     SystemTime.u64NanoTS   = pKvmCpu->uVirtNanoTS;
    389     SystemTime.u64Tsc      = pKvmCpu->uTsc;
    390     SystemTime.fFlags      = pKvmCpu->fSystemTimeFlags | GIM_KVM_SYSTEM_TIME_FLAGS_TSC_STABLE;
    391 
    392     /*
    393      * How the guest calculates the system time (nanoseconds):
    394      *
    395      * tsc = rdtsc - SysTime.u64Tsc
    396      * if (SysTime.i8TscShift >= 0)
    397      *     tsc <<= i8TscShift;
    398      * else
    399      *     tsc >>= -i8TscShift;
    400      * time = ((tsc * SysTime.u32TscScale) >> 32) + SysTime.u64NanoTS
    401      */
    402     uint64_t u64TscFreq   = pKvm->cTscTicksPerSecond;
    403     SystemTime.i8TscShift = 0;
    404     while (u64TscFreq > 2 * RT_NS_1SEC_64)
    405     {
    406         u64TscFreq >>= 1;
    407         SystemTime.i8TscShift--;
    408     }
    409     uint32_t uTscFreqLo = (uint32_t)u64TscFreq;
    410     while (uTscFreqLo <= RT_NS_1SEC)
    411     {
    412         uTscFreqLo <<= 1;
    413         SystemTime.i8TscShift++;
    414     }
    415     SystemTime.u32TscScale = ASMDivU64ByU32RetU32(RT_NS_1SEC_64 << 32, uTscFreqLo);
    416 
    417     /* For informational purposes, back-calculate the exact TSC frequency the guest will see.
    418      * Note that the frequency is in kHz, not Hz, since that's what Linux uses.
    419      */
    420     uint64_t uTscKHz = (RT_NS_1MS_64 << 32) / SystemTime.u32TscScale;
    421     if (SystemTime.i8TscShift < 0)
    422         uTscKHz <<= -SystemTime.i8TscShift;
    423     else
    424         uTscKHz >>=  SystemTime.i8TscShift;
    425 
    426     /*
    427      * Update guest memory with the system-time struct. Technically we are cheating
    428      * by only writing the struct once with the version incremented by two; in reality,
    429      * the system-time struct is enabled once for each CPU at boot and not concurrently
    430      * read by other VCPUs at the same time.
    431      */
    432     Assert(!(SystemTime.u32Version & UINT32_C(1)));
    433     int rc = PGMPhysSimpleWriteGCPhys(pVM, pKvmCpu->GCPhysSystemTime, &SystemTime, sizeof(GIMKVMSYSTEMTIME));
    434     if (RT_SUCCESS(rc))
    435     {
    436         LogRel(("GIM: KVM: VCPU%3d: Enabled system-time struct. at %#RGp - u32TscScale=%#RX32 i8TscShift=%d uVersion=%#RU32 "
    437                 "fFlags=%#x uTsc=%#RX64 uVirtNanoTS=%#RX64 TscKHz=%RU64\n", pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, SystemTime.u32TscScale,
    438                 SystemTime.i8TscShift, SystemTime.u32Version, SystemTime.fFlags, pKvmCpu->uTsc, pKvmCpu->uVirtNanoTS, uTscKHz));
    439         TMR3CpuTickParavirtEnable(pVM);
    440     }
    441     else
    442         LogRel(("GIM: KVM: VCPU%3d: Failed to write system-time struct. at %#RGp. rc=%Rrc\n",
    443                 pVCpu->idCpu, pKvmCpu->GCPhysSystemTime, rc));
    444 
    445     return rc;
    446479}
    447480
     
    546579}
    547580
     581
     582/**
     583 * Enables the KVM system time structure.
     584 *
     585 * This can be done concurrently because the guest memory being updated is per-VCPU
     586 * and the struct even has a "version" field which needs to be incremented
     587 * before/after altering guest memory to allow concurrent updates from the host.
     588 * Hence this is not being done in an EMT rendezvous. It -is- done in ring-3 since
     589 * we call into ring-3 only TM code in the end.
     590 *
     591 * @returns VBox status code.
     592 * @param   pVM             The cross context VM structure.
     593 * @param   pVCpu           The cross context virtual CPU structure.
     594 * @param   uMsrSystemTime  The system time MSR value being written.
     595 */
     596VMMR3_INT_DECL(int) gimR3KvmEnableSystemTime(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uMsrSystemTime)
     597{
     598    Assert(uMsrSystemTime & MSR_GIM_KVM_SYSTEM_TIME_ENABLE_BIT);
     599    PGIMKVM    pKvm    = &pVM->gim.s.u.Kvm;
     600    PGIMKVMCPU pKvmCpu = &pVCpu->gim.s.u.KvmCpu;
     601
     602    /*
     603     * Update the system-time struct.
     604     * The system-time structs are usually placed at a different guest address for each VCPU.
     605     *
     606     * The rendezvous ensures that we don't have other VCPUs that might potentially be
     607     * reading the guest memory which we're about to update.
     608     */
     609    pKvmCpu->uTsc                  = TMCpuTickGetNoCheck(pVCpu);
     610    pKvmCpu->uVirtNanoTS           = ASMMultU64ByU32DivByU32(pKvmCpu->uTsc, RT_NS_1SEC, pKvm->cTscTicksPerSecond);
     611    pKvmCpu->u64SystemTimeMsr      = uMsrSystemTime;
     612    pKvmCpu->GCPhysSystemTime      = MSR_GIM_KVM_SYSTEM_TIME_GUEST_GPA(uMsrSystemTime);
     613
     614    int rc = gimR3KvmUpdateSystemTime(pVM, pVCpu);
     615    if (RT_FAILURE(rc))
     616    {
     617        pKvmCpu->u64SystemTimeMsr = 0;
     618        /* We shouldn't throw a #GP(0) here for buggy guests (neither does KVM apparently), see @bugref{8627}. */
     619    }
     620
     621    return rc;
     622}
     623
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette