/**
 * Read the current CPU timestamp counter.
 *
 * @returns Gets the CPU tsc.
 * @param   pVCpu       The VMCPU to operate on.
 */
DECLINLINE(uint64_t) tmCpuTickGetInternal(PVMCPU pVCpu, bool fCheckTimers)
{
    uint64_t u64;

    if (RT_LIKELY(pVCpu->tm.s.fTSCTicking))
    {
        PVM pVM = pVCpu->CTX_SUFF(pVM);
        if (pVM->tm.s.fTSCVirtualized)
        {
            if (pVM->tm.s.fTSCUseRealTSC)
                u64 = ASMReadTSC();
            else
                u64 = tmCpuTickGetRawVirtual(pVM, fCheckTimers);
            u64 -= pVCpu->tm.s.offTSCRawSrc;
        }
        else
            u64 = ASMReadTSC();

        /* Never return a value lower than what the guest has already seen. */
        if (u64 < pVCpu->tm.s.u64TSCLastSeen)
        {
            STAM_COUNTER_INC(&pVM->tm.s.StatTSCUnderflow);
            pVCpu->tm.s.u64TSCLastSeen += 64;   /* @todo choose a good increment here */
            u64 = pVCpu->tm.s.u64TSCLastSeen;
        }
    }
    else
        u64 = pVCpu->tm.s.u64TSC;
    return u64;
}
RTR3DECL(bool) RTThreadYield(void)
{
    uint64_t u64TS = ASMReadTSC();
    Sleep(0);
    u64TS = ASMReadTSC() - u64TS;
    bool fRc = u64TS > 1500;
    LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
    return fRc;
}
/**
 * Gets the next deadline in host CPU clock ticks and the TSC offset if we can
 * use the raw TSC.
 *
 * @returns The number of host CPU clock ticks to the next timer deadline.
 * @param   pVCpu           The current CPU.
 * @param   poffRealTSC     The offset against the TSC of the current CPU.
 * @thread  EMT(pVCpu).
 * @remarks Superset of TMCpuTickCanUseRealTSC.
 */
VMM_INT_DECL(uint64_t) TMCpuTickGetDeadlineAndTscOffset(PVMCPU pVCpu, bool *pfOffsettedTsc, uint64_t *poffRealTSC)
{
    PVM         pVM = pVCpu->CTX_SUFF(pVM);
    uint64_t    cTicksToDeadline;

    /*
     * We require:
     *     1. A fixed TSC, this is checked at init time.
     *     2. That the TSC is ticking (we shouldn't be here if it isn't)
     *     3. Either that we're using the real TSC as time source or
     *          a) we don't have any lag to catch up, and
     *          b) the virtual sync clock hasn't been halted by an expired timer, and
     *          c) we're not using warp drive (accelerated virtual guest time).
     */
    if (    pVM->tm.s.fMaybeUseOffsettedHostTSC
        &&  RT_LIKELY(pVCpu->tm.s.fTSCTicking)
        &&  (   pVM->tm.s.fTSCUseRealTSC
             || (   !pVM->tm.s.fVirtualSyncCatchUp
                 && RT_LIKELY(pVM->tm.s.fVirtualSyncTicking)
                 && !pVM->tm.s.fVirtualWarpDrive))
       )
    {
        *pfOffsettedTsc = true;
        if (!pVM->tm.s.fTSCUseRealTSC)
        {
            /* The source is the timer synchronous virtual clock. */
            Assert(pVM->tm.s.fTSCVirtualized);

            uint64_t cNsToDeadline;
            uint64_t u64NowVirtSync = TMVirtualSyncGetWithDeadlineNoCheck(pVM, &cNsToDeadline);
            uint64_t u64Now = u64NowVirtSync != TMCLOCK_FREQ_VIRTUAL /* what's the use of this? */
                            ? ASMMultU64ByU32DivByU32(u64NowVirtSync, pVM->tm.s.cTSCTicksPerSecond, TMCLOCK_FREQ_VIRTUAL)
                            : u64NowVirtSync;
            u64Now -= pVCpu->tm.s.offTSCRawSrc;
            *poffRealTSC = u64Now - ASMReadTSC();
            cTicksToDeadline = tmCpuCalcTicksToDeadline(cNsToDeadline);
        }
        else
        {
            /* The source is the real TSC. */
            if (pVM->tm.s.fTSCVirtualized)
                *poffRealTSC = pVCpu->tm.s.offTSCRawSrc;
            else
                *poffRealTSC = 0;
            cTicksToDeadline = tmCpuCalcTicksToDeadline(TMVirtualSyncGetNsToDeadline(pVM));
        }
    }
    else
    {
#ifdef VBOX_WITH_STATISTICS
        tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
#endif
        *pfOffsettedTsc  = false;
        *poffRealTSC     = 0;
        cTicksToDeadline = tmCpuCalcTicksToDeadline(TMVirtualSyncGetNsToDeadline(pVM));
    }
    return cTicksToDeadline;
}
/**
 * Checks if AMD-V / VT-x can use an offsetted hardware TSC or not.
 *
 * @returns true/false accordingly.
 * @param   pVCpu       The VMCPU to operate on.
 * @param   poffRealTSC     The offset against the TSC of the current CPU.
 *                          Can be NULL.
 * @thread EMT.
 */
VMM_INT_DECL(bool) TMCpuTickCanUseRealTSC(PVMCPU pVCpu, uint64_t *poffRealTSC)
{
    PVM pVM = pVCpu->CTX_SUFF(pVM);

    /*
     * We require:
     *     1. A fixed TSC, this is checked at init time.
     *     2. That the TSC is ticking (we shouldn't be here if it isn't)
     *     3. Either that we're using the real TSC as time source or
     *          a) we don't have any lag to catch up, and
     *          b) the virtual sync clock hasn't been halted by an expired timer, and
     *          c) we're not using warp drive (accelerated virtual guest time).
     */
    if (    pVM->tm.s.fMaybeUseOffsettedHostTSC
        &&  RT_LIKELY(pVCpu->tm.s.fTSCTicking)
        &&  (   pVM->tm.s.fTSCUseRealTSC
             || (   !pVM->tm.s.fVirtualSyncCatchUp
                 && RT_LIKELY(pVM->tm.s.fVirtualSyncTicking)
                 && !pVM->tm.s.fVirtualWarpDrive))
       )
    {
        if (!pVM->tm.s.fTSCUseRealTSC)
        {
            /* The source is the timer synchronous virtual clock. */
            Assert(pVM->tm.s.fTSCVirtualized);

            if (poffRealTSC)
            {
                uint64_t u64Now = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
                                - pVCpu->tm.s.offTSCRawSrc;
                /** @todo When we start collecting statistics on how much time we spend executing
                 * guest code before exiting, we should check this against the next virtual sync
                 * timer timeout. If it's lower than the avg. length, we should trap rdtsc to increase
                 * the chance that we'll get interrupted right after the timer expired. */
                *poffRealTSC = u64Now - ASMReadTSC();
            }
        }
        else if (poffRealTSC)
        {
            /* The source is the real TSC. */
            if (pVM->tm.s.fTSCVirtualized)
                *poffRealTSC = pVCpu->tm.s.offTSCRawSrc;
            else
                *poffRealTSC = 0;
        }
        /** @todo count this? */
        return true;
    }

#ifdef VBOX_WITH_STATISTICS
    tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
#endif
    return false;
}
示例#5
0
RTDECL(bool) RTThreadYield(void)
{
#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
    uint64_t u64TS = ASMReadTSC();
#endif
#ifdef RT_OS_DARWIN
    pthread_yield_np();
#elif defined(RT_OS_SOLARIS) || defined(RT_OS_HAIKU)
    sched_yield();
#else
    pthread_yield();
#endif
#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
    u64TS = ASMReadTSC() - u64TS;
    bool fRc = u64TS > 1500;
    LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
#else
    bool fRc = true; /* PORTME: Add heuristics for determining whether the cpus was yielded. */
#endif
    return fRc;
}
示例#6
0
/**
 * Gets the next deadline in host CPU clock ticks and the TSC offset if we can
 * use the raw TSC.
 *
 * @returns The number of host CPU clock ticks to the next timer deadline.
 * @param   pVM             The cross context VM structure.
 * @param   pVCpu           The cross context virtual CPU structure of the calling EMT.
 * @param   poffRealTsc     The offset against the TSC of the current host CPU,
 *                          if pfOffsettedTsc is set to true.
 * @param   pfOffsettedTsc  Where to return whether TSC offsetting can be used.
 * @param   pfParavirtTsc   Where to return whether paravirt TSC is enabled.
 *
 * @thread  EMT(pVCpu).
 * @see     TMCpuTickCanUseRealTSC().
 */
VMM_INT_DECL(uint64_t) TMCpuTickGetDeadlineAndTscOffset(PVM pVM, PVMCPU pVCpu, uint64_t *poffRealTsc,
                                                        bool *pfOffsettedTsc, bool *pfParavirtTsc)
{
    Assert(pVCpu->tm.s.fTSCTicking || DBGFIsStepping(pVCpu));

    *pfParavirtTsc = pVM->tm.s.fParavirtTscEnabled;

    /*
     * Same logic as in TMCpuTickCanUseRealTSC.
     */
    if (pVM->tm.s.enmTSCMode == TMTSCMODE_REAL_TSC_OFFSET)
    {
        /** @todo We should negate both deltas!  It's soo weird that we do the
         *        exact opposite of what the hardware implements. */
#ifdef IN_RING3
        *poffRealTsc     = 0 - pVCpu->tm.s.offTSCRawSrc - SUPGetTscDelta();
#else
        *poffRealTsc     = 0 - pVCpu->tm.s.offTSCRawSrc - SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet);
#endif
        *pfOffsettedTsc  = true;
        return tmCpuCalcTicksToDeadline(pVCpu, TMVirtualSyncGetNsToDeadline(pVM));
    }

    /*
     * Same logic as in TMCpuTickCanUseRealTSC.
     */
    if (   pVM->tm.s.enmTSCMode == TMTSCMODE_DYNAMIC
        && !pVM->tm.s.fVirtualSyncCatchUp
        && RT_LIKELY(pVM->tm.s.fVirtualSyncTicking)
        && !pVM->tm.s.fVirtualWarpDrive)
    {
        /* The source is the timer synchronous virtual clock. */
        uint64_t cNsToDeadline;
        uint64_t u64NowVirtSync = TMVirtualSyncGetWithDeadlineNoCheck(pVM, &cNsToDeadline);
        uint64_t u64Now = u64NowVirtSync != TMCLOCK_FREQ_VIRTUAL /* what's the use of this? */
                        ? ASMMultU64ByU32DivByU32(u64NowVirtSync, pVM->tm.s.cTSCTicksPerSecond, TMCLOCK_FREQ_VIRTUAL)
                        : u64NowVirtSync;
        u64Now -= pVCpu->tm.s.offTSCRawSrc;
        *poffRealTsc     = u64Now - ASMReadTSC();
        *pfOffsettedTsc  = u64Now >= pVCpu->tm.s.u64TSCLastSeen;
        return tmCpuCalcTicksToDeadline(pVCpu, cNsToDeadline);
    }

#ifdef VBOX_WITH_STATISTICS
    tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
#endif
    *pfOffsettedTsc  = false;
    *poffRealTsc     = 0;
    return tmCpuCalcTicksToDeadline(pVCpu, TMVirtualSyncGetNsToDeadline(pVM));
}
示例#7
0
/**
 * Perform lazy initialization.
 *
 * @returns IPRT status code.
 * @param   pvUser1     Ignored.
 * @param   pvUser2     Ignored.
 */
static DECLCALLBACK(int) rtRandInitOnce(void *pvUser1, void *pvUser2)
{
    NOREF(pvUser1);
    NOREF(pvUser2);

    RTRAND hRand;
    int rc = RTRandAdvCreateSystemFaster(&hRand);
    if (RT_FAILURE(rc))
        rc = RTRandAdvCreateParkMiller(&hRand);
    if (RT_SUCCESS(rc))
    {
#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
        RTRandAdvSeed(hRand, ASMReadTSC() >> 8);
#else
        RTRandAdvSeed(hRand, RTTimeNanoTS() >> 8);
#endif
        g_hRand = hRand;
    }
    else
/**
 * Resumes the CPU timestamp counter ticking.
 *
 * @returns VBox status code.
 * @param   pVM         The VM to operate on.
 * @param   pVCpu       The VMCPU to operate on.
 * @internal
 */
int tmCpuTickResume(PVM pVM, PVMCPU pVCpu)
{
    if (!pVCpu->tm.s.fTSCTicking)
    {
        pVCpu->tm.s.fTSCTicking = true;
        if (pVM->tm.s.fTSCVirtualized)
        {
            /** @todo Test that pausing and resuming doesn't cause lag! (I.e. that we're
             *        unpaused before the virtual time and stopped after it. */
            if (pVM->tm.s.fTSCUseRealTSC)
                pVCpu->tm.s.offTSCRawSrc = ASMReadTSC() - pVCpu->tm.s.u64TSC;
            else
                pVCpu->tm.s.offTSCRawSrc = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
                                         - pVCpu->tm.s.u64TSC;
        }
        return VINF_SUCCESS;
    }
    AssertFailed();
    return VERR_TM_TSC_ALREADY_TICKING;
}
示例#9
0
int main(int argc, char **argv)
{
    int rcRet = 0;
    int i;
    int rc;
    int cIterations = argc > 1 ? RTStrToUInt32(argv[1]) : 32;
    if (cIterations == 0)
        cIterations = 64;

    /*
     * Init.
     */
    RTR3InitExe(argc, &argv, 0);
    PSUPDRVSESSION pSession;
    rc = SUPR3Init(&pSession);
    rcRet += rc != 0;
    RTPrintf("tstInt: SUPR3Init -> rc=%Rrc\n", rc);
    char szFile[RTPATH_MAX];
    if (!rc)
    {
        rc = RTPathExecDir(szFile, sizeof(szFile) - sizeof("/VMMR0.r0"));
    }
    char szAbsFile[RTPATH_MAX];
    if (RT_SUCCESS(rc))
    {
        strcat(szFile, "/VMMR0.r0");
        rc = RTPathAbs(szFile, szAbsFile, sizeof(szAbsFile));
    }
    if (RT_SUCCESS(rc))
    {
        /*
         * Load VMM code.
         */
        rc = SUPR3LoadVMM(szAbsFile);
        if (RT_SUCCESS(rc))
        {
            /*
             * Create a fake 'VM'.
             */
            PVMR0 pVMR0 = NIL_RTR0PTR;
            PVM pVM = NULL;
            const unsigned cPages = RT_ALIGN_Z(sizeof(*pVM), PAGE_SIZE) >> PAGE_SHIFT;
            PSUPPAGE paPages = (PSUPPAGE)RTMemAllocZ(cPages * sizeof(SUPPAGE));
            if (paPages)
                rc = SUPR3LowAlloc(cPages, (void **)&pVM, &pVMR0, &paPages[0]);
            else
                rc = VERR_NO_MEMORY;
            if (RT_SUCCESS(rc))
            {
                pVM->pVMRC = 0;
                pVM->pVMR3 = pVM;
                pVM->pVMR0 = pVMR0;
                pVM->paVMPagesR3 = paPages;
                pVM->pSession = pSession;
                pVM->enmVMState = VMSTATE_CREATED;

                rc = SUPR3SetVMForFastIOCtl(pVMR0);
                if (!rc)
                {

                    /*
                     * Call VMM code with invalid function.
                     */
                    for (i = cIterations; i > 0; i--)
                    {
                        rc = SUPR3CallVMMR0(pVMR0, NIL_VMCPUID, VMMR0_DO_SLOW_NOP, NULL);
                        if (rc != VINF_SUCCESS)
                        {
                            RTPrintf("tstInt: SUPR3CallVMMR0 -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
                            rcRet++;
                            break;
                        }
                    }
                    RTPrintf("tstInt: Performed SUPR3CallVMMR0 %d times (rc=%Rrc)\n", cIterations, rc);

                    /*
                     * The fast path.
                     */
                    if (rc == VINF_SUCCESS)
                    {
                        RTTimeNanoTS();
                        uint64_t StartTS = RTTimeNanoTS();
                        uint64_t StartTick = ASMReadTSC();
                        uint64_t MinTicks = UINT64_MAX;
                        for (i = 0; i < 1000000; i++)
                        {
                            uint64_t OneStartTick = ASMReadTSC();
                            rc = SUPR3CallVMMR0Fast(pVMR0, VMMR0_DO_NOP, 0);
                            uint64_t Ticks = ASMReadTSC() - OneStartTick;
                            if (Ticks < MinTicks)
                                MinTicks = Ticks;

                            if (RT_UNLIKELY(rc != VINF_SUCCESS))
                            {
                                RTPrintf("tstInt: SUPR3CallVMMR0Fast -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
                                rcRet++;
                                break;
                            }
                        }
                        uint64_t Ticks = ASMReadTSC() - StartTick;
                        uint64_t NanoSecs = RTTimeNanoTS() - StartTS;

                        RTPrintf("tstInt: SUPR3CallVMMR0Fast - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n",
                                 i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks);

                        /*
                         * The ordinary path.
                         */
                        RTTimeNanoTS();
                        StartTS = RTTimeNanoTS();
                        StartTick = ASMReadTSC();
                        MinTicks = UINT64_MAX;
                        for (i = 0; i < 1000000; i++)
                        {
                            uint64_t OneStartTick = ASMReadTSC();
                            rc = SUPR3CallVMMR0Ex(pVMR0, NIL_VMCPUID, VMMR0_DO_SLOW_NOP, 0, NULL);
                            uint64_t OneTicks = ASMReadTSC() - OneStartTick;
                            if (OneTicks < MinTicks)
                                MinTicks = OneTicks;

                            if (RT_UNLIKELY(rc != VINF_SUCCESS))
                            {
                                RTPrintf("tstInt: SUPR3CallVMMR0Ex -> rc=%Rrc i=%d Expected VINF_SUCCESS!\n", rc, i);
                                rcRet++;
                                break;
                            }
                        }
                        Ticks = ASMReadTSC() - StartTick;
                        NanoSecs = RTTimeNanoTS() - StartTS;

                        RTPrintf("tstInt: SUPR3CallVMMR0Ex   - %d iterations in %llu ns / %llu ticks. %llu ns / %#llu ticks per iteration. Min %llu ticks.\n",
                                 i, NanoSecs, Ticks, NanoSecs / i, Ticks / i, MinTicks);
                    }
                }
                else
                {
                    RTPrintf("tstInt: SUPR3SetVMForFastIOCtl failed: %Rrc\n", rc);
                    rcRet++;
                }
            }
            else
            {
                RTPrintf("tstInt: SUPR3ContAlloc(%#zx,,) failed\n", sizeof(*pVM));
                rcRet++;
            }

            /*
             * Unload VMM.
             */
            rc = SUPR3UnloadVMM();
            if (rc)
            {
                RTPrintf("tstInt: SUPR3UnloadVMM failed with rc=%Rrc\n", rc);
                rcRet++;
            }
        }
        else
        {
            RTPrintf("tstInt: SUPR3LoadVMM failed with rc=%Rrc\n", rc);
            rcRet++;
        }

        /*
         * Terminate.
         */
        rc = SUPR3Term(false /*fForced*/);
        rcRet += rc != 0;
        RTPrintf("tstInt: SUPR3Term -> rc=%Rrc\n", rc);
    }
/**
 * Checks if AMD-V / VT-x can use an offsetted hardware TSC or not.
 *
 * @returns true/false accordingly.
 * @param   pVM             The cross context VM structure.
 * @param   pVCpu           The cross context virtual CPU structure.
 * @param   poffRealTsc     The offset against the TSC of the current host CPU,
 *                          if pfOffsettedTsc is set to true.
 * @param   pfParavirtTsc   Where to return whether paravirt TSC is enabled.
 *
 * @thread  EMT(pVCpu).
 * @see     TMCpuTickGetDeadlineAndTscOffset().
 */
VMM_INT_DECL(bool) TMCpuTickCanUseRealTSC(PVM pVM, PVMCPU pVCpu, uint64_t *poffRealTsc, bool *pfParavirtTsc)
{
    Assert(pVCpu->tm.s.fTSCTicking || DBGFIsStepping(pVCpu));

    *pfParavirtTsc = pVM->tm.s.fParavirtTscEnabled;

    /*
     * In real TSC mode it's easy, we just need the delta & offTscRawSrc and
     * the CPU will add them to RDTSC and RDTSCP at runtime.
     *
     * In tmCpuTickGetInternal we do:
     *          SUPReadTsc() - pVCpu->tm.s.offTSCRawSrc;
     * Where SUPReadTsc() does:
     *          ASMReadTSC() - pGipCpu->i64TscDelta;
     * Which means tmCpuTickGetInternal actually does:
     *          ASMReadTSC() - pGipCpu->i64TscDelta - pVCpu->tm.s.offTSCRawSrc;
     * So, the offset to be ADDED to RDTSC[P] is:
     *          offRealTsc = -(pGipCpu->i64TscDelta + pVCpu->tm.s.offTSCRawSrc)
     */
    if (pVM->tm.s.enmTSCMode == TMTSCMODE_REAL_TSC_OFFSET)
    {
        /** @todo We should negate both deltas!  It's soo weird that we do the
         *        exact opposite of what the hardware implements. */
#ifdef IN_RING3
        *poffRealTsc = 0 - pVCpu->tm.s.offTSCRawSrc - SUPGetTscDelta();
#else
        *poffRealTsc = 0 - pVCpu->tm.s.offTSCRawSrc - SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet);
#endif
        return true;
    }

    /*
     * We require:
     *     1. A fixed TSC, this is checked at init time.
     *     2. That the TSC is ticking (we shouldn't be here if it isn't)
     *     3. Either that we're using the real TSC as time source or
     *          a) we don't have any lag to catch up, and
     *          b) the virtual sync clock hasn't been halted by an expired timer, and
     *          c) we're not using warp drive (accelerated virtual guest time).
     */
    if (   pVM->tm.s.enmTSCMode == TMTSCMODE_DYNAMIC
        && !pVM->tm.s.fVirtualSyncCatchUp
        && RT_LIKELY(pVM->tm.s.fVirtualSyncTicking)
        && !pVM->tm.s.fVirtualWarpDrive)
    {
        /* The source is the timer synchronous virtual clock. */
        uint64_t u64Now = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
                        - pVCpu->tm.s.offTSCRawSrc;
        /** @todo When we start collecting statistics on how much time we spend executing
         * guest code before exiting, we should check this against the next virtual sync
         * timer timeout. If it's lower than the avg. length, we should trap rdtsc to increase
         * the chance that we'll get interrupted right after the timer expired. */
        if (u64Now >= pVCpu->tm.s.u64TSCLastSeen)
        {
            *poffRealTsc = u64Now - ASMReadTSC();
            return true;    /** @todo count this? */
        }
    }

#ifdef VBOX_WITH_STATISTICS
    tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
#endif
    return false;
}
示例#11
0
/**
 * The slow case for SUPReadTsc where we need to apply deltas.
 *
 * Must only be called when deltas are applicable, so please do not call it
 * directly.
 *
 * @returns TSC with delta applied.
 * @param   pGip        Pointer to the GIP.
 *
 * @remarks May be called with interrupts disabled in ring-0!  This is why the
 *          ring-0 code doesn't attempt to figure the delta.
 *
 * @internal
 */
SUPDECL(uint64_t) SUPReadTscWithDelta(PSUPGLOBALINFOPAGE pGip)
{
    uint64_t            uTsc;
    uint16_t            iGipCpu;
    AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
    AssertCompile(RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx) >= RTCPUSET_MAX_CPUS);
    Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO);

    /*
     * Read the TSC and get the corresponding aCPUs index.
     */
#ifdef IN_RING3
    if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
    {
        /* RDTSCP gives us all we need, no loops/cli. */
        uint32_t iCpuSet;
        uTsc      = ASMReadTscWithAux(&iCpuSet);
        iCpuSet  &= RTCPUSET_MAX_CPUS - 1;
        iGipCpu   = pGip->aiCpuFromCpuSetIdx[iCpuSet];
    }
    else if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
    {
        /* Storing the IDTR is normally very quick, but we need to loop. */
        uint32_t cTries = 0;
        for (;;)
        {
            uint16_t cbLim = ASMGetIdtrLimit();
            uTsc = ASMReadTSC();
            if (RT_LIKELY(ASMGetIdtrLimit() == cbLim))
            {
                uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8);
                iCpuSet &= RTCPUSET_MAX_CPUS - 1;
                iGipCpu  = pGip->aiCpuFromCpuSetIdx[iCpuSet];
                break;
            }
            if (cTries >= 16)
            {
                iGipCpu = UINT16_MAX;
                break;
            }
            cTries++;
        }
    }
    else
    {
        /* Get APIC ID via the slow CPUID instruction, requires looping. */
        uint32_t cTries = 0;
        for (;;)
        {
            uint8_t idApic = ASMGetApicId();
            uTsc = ASMReadTSC();
            if (RT_LIKELY(ASMGetApicId() == idApic))
            {
                iGipCpu = pGip->aiCpuFromApicId[idApic];
                break;
            }
            if (cTries >= 16)
            {
                iGipCpu = UINT16_MAX;
                break;
            }
            cTries++;
        }
    }
#elif defined(IN_RING0)
    /* Ring-0: Use use RTMpCpuId(), no loops. */
    RTCCUINTREG uFlags  = ASMIntDisableFlags();
    int         iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
    if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
        iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
    else
        iGipCpu = UINT16_MAX;
    uTsc = ASMReadTSC();
    ASMSetFlags(uFlags);

# elif defined(IN_RC)
    /* Raw-mode context: We can get the host CPU set index via VMCPU, no loops. */
    RTCCUINTREG uFlags  = ASMIntDisableFlags(); /* Are already disable, but play safe. */
    uint32_t    iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet;
    if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
        iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
    else
        iGipCpu = UINT16_MAX;
    uTsc = ASMReadTSC();
    ASMSetFlags(uFlags);
#else
# error "IN_RING3, IN_RC or IN_RING0 must be defined!"
#endif

    /*
     * If the delta is valid, apply it.
     */
    if (RT_LIKELY(iGipCpu < pGip->cCpus))
    {
        int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
        if (RT_LIKELY(iTscDelta != INT64_MAX))
            return uTsc - iTscDelta;

# ifdef IN_RING3
        /*
         * The delta needs calculating, call supdrv to get the TSC.
         */
        int rc = SUPR3ReadTsc(&uTsc, NULL);
        if (RT_SUCCESS(rc))
            return uTsc;
        AssertMsgFailed(("SUPR3ReadTsc -> %Rrc\n", rc));
        uTsc = ASMReadTSC();
# endif /* IN_RING3 */
    }

    /*
     * This shouldn't happen, especially not in ring-3 and raw-mode context.
     * But if it does, return something that's half useful.
     */
    AssertMsgFailed(("iGipCpu=%d (%#x) cCpus=%d fGetGipCpu=%#x\n", iGipCpu, iGipCpu, pGip->cCpus, pGip->fGetGipCpu));
    return uTsc;
}
示例#12
0
/**
 * The GC entry point.
 *
 * @returns VBox status code.
 * @param   pVM         The VM to operate on.
 * @param   uOperation  Which operation to execute (VMMGCOPERATION).
 * @param   uArg        Argument to that operation.
 */
VMMRCDECL(int) VMMGCEntry(PVM pVM, unsigned uOperation, unsigned uArg, ...)
{
    /* todo */
    switch (uOperation)
    {
        /*
         * Init RC modules.
         */
        case VMMGC_DO_VMMGC_INIT:
        {
            /*
             * Validate the svn revision (uArg).
             */
            if (uArg != VMMGetSvnRev())
                return VERR_VMM_RC_VERSION_MISMATCH;

            /*
             * Initialize the runtime.
             * (The program timestamp is found in the elipsis.)
             */
            va_list va;
            va_start(va, uArg);
            uint64_t u64TS = va_arg(va, uint64_t);
            va_end(va);

            int rc = RTRCInit(u64TS);
            Log(("VMMGCEntry: VMMGC_DO_VMMGC_INIT - uArg=%u (svn revision) u64TS=%RX64; rc=%Rrc\n", uArg, u64TS, rc));
            AssertRCReturn(rc, rc);

            rc = PGMRegisterStringFormatTypes();
            AssertRCReturn(rc, rc);

            rc = PGMRCDynMapInit(pVM);
            AssertRCReturn(rc, rc);
            return VINF_SUCCESS;
        }

        /*
         * Testcase which is used to test interrupt forwarding.
         * It spins for a while with interrupts enabled.
         */
        case VMMGC_DO_TESTCASE_HYPER_INTERRUPT:
        {
            uint32_t volatile i = 0;
            ASMIntEnable();
            while (i < _2G32)
                i++;
            ASMIntDisable();
            return 0;
        }

        /*
         * Testcase which simply returns, this is used for
         * profiling of the switcher.
         */
        case VMMGC_DO_TESTCASE_NOP:
            return 0;

        /*
         * Testcase executes a privileged instruction to force a world switch. (in both SVM & VMX)
         */
        case VMMGC_DO_TESTCASE_HWACCM_NOP:
            ASMRdMsr_Low(MSR_IA32_SYSENTER_CS);
            return 0;

        /*
         * Delay for ~100us.
         */
        case VMMGC_DO_TESTCASE_INTERRUPT_MASKING:
        {
            uint64_t u64MaxTicks = (SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) != ~(uint64_t)0
                                    ? SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage)
                                    : _2G)
                                   / 10000;
            uint64_t u64StartTSC = ASMReadTSC();
            uint64_t u64TicksNow;
            uint32_t volatile i = 0;

            do
            {
                /* waste some time and protect against getting stuck. */
                for (uint32_t volatile j = 0; j < 1000; j++, i++)
                    if (i > _2G32)
                        return VERR_GENERAL_FAILURE;

                /* check if we're done.*/
                u64TicksNow = ASMReadTSC() - u64StartTSC;
            } while (u64TicksNow < u64MaxTicks);

            return VINF_SUCCESS;
        }

        /*
         * Trap testcases and unknown operations.
         */
        default:
            if (    uOperation >= VMMGC_DO_TESTCASE_TRAP_FIRST
                &&  uOperation < VMMGC_DO_TESTCASE_TRAP_LAST)
                return vmmGCTest(pVM, uOperation, uArg);
            return VERR_INVALID_PARAMETER;
    }
}
示例#13
0
/* execute the switch. */
VMMR3DECL(int) VMMDoHwAccmTest(PVM pVM)
{
    uint32_t i;
    int      rc;
    PCPUMCTX pHyperCtx, pGuestCtx;
    RTGCPHYS CR3Phys = 0x0; /* fake address */
    PVMCPU   pVCpu = &pVM->aCpus[0];

    if (!HWACCMR3IsAllowed(pVM))
    {
        RTPrintf("VMM: Hardware accelerated test not available!\n");
        return VERR_ACCESS_DENIED;
    }

    /*
     * These forced actions are not necessary for the test and trigger breakpoints too.
     */
    VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TRPM_SYNC_IDT);
    VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_SELM_SYNC_TSS);

    /* Enable mapping of the hypervisor into the shadow page table. */
    uint32_t cb;
    rc = PGMR3MappingsSize(pVM, &cb);
    AssertRCReturn(rc, rc);

    /* Pretend the mappings are now fixed; to force a refresh of the reserved PDEs. */
    rc = PGMR3MappingsFix(pVM, MM_HYPER_AREA_ADDRESS, cb);
    AssertRCReturn(rc, rc);

    pHyperCtx = CPUMGetHyperCtxPtr(pVCpu);

    pHyperCtx->cr0 = X86_CR0_PE | X86_CR0_WP | X86_CR0_PG | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE | X86_CR0_MP;
    pHyperCtx->cr4 = X86_CR4_PGE | X86_CR4_OSFSXR | X86_CR4_OSXMMEEXCPT;
    PGMChangeMode(pVCpu, pHyperCtx->cr0, pHyperCtx->cr4, pHyperCtx->msrEFER);
    PGMSyncCR3(pVCpu, pHyperCtx->cr0, CR3Phys, pHyperCtx->cr4, true);

    VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TO_R3);
    VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TIMER);
    VM_FF_CLEAR(pVM, VM_FF_TM_VIRTUAL_SYNC);
    VM_FF_CLEAR(pVM, VM_FF_REQUEST);

    /*
     * Setup stack for calling VMMGCEntry().
     */
    RTRCPTR RCPtrEP;
    rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP);
    if (RT_SUCCESS(rc))
    {
        RTPrintf("VMM: VMMGCEntry=%RRv\n", RCPtrEP);

        pHyperCtx = CPUMGetHyperCtxPtr(pVCpu);

        /* Fill in hidden selector registers for the hypervisor state. */
        SYNC_SEL(pHyperCtx, cs);
        SYNC_SEL(pHyperCtx, ds);
        SYNC_SEL(pHyperCtx, es);
        SYNC_SEL(pHyperCtx, fs);
        SYNC_SEL(pHyperCtx, gs);
        SYNC_SEL(pHyperCtx, ss);
        SYNC_SEL(pHyperCtx, tr);

        /*
         * Profile switching.
         */
        RTPrintf("VMM: profiling switcher...\n");
        Log(("VMM: profiling switcher...\n"));
        uint64_t TickMin = ~0;
        uint64_t tsBegin = RTTimeNanoTS();
        uint64_t TickStart = ASMReadTSC();
        for (i = 0; i < 1000000; i++)
        {
            CPUMSetHyperState(pVCpu, pVM->vmm.s.pfnCallTrampolineRC, pVCpu->vmm.s.pbEMTStackBottomRC, 0, 0);
            CPUMPushHyper(pVCpu, 0);
            CPUMPushHyper(pVCpu, VMMGC_DO_TESTCASE_HWACCM_NOP);
            CPUMPushHyper(pVCpu, pVM->pVMRC);
            CPUMPushHyper(pVCpu, 3 * sizeof(RTRCPTR));    /* stack frame size */
            CPUMPushHyper(pVCpu, RCPtrEP);                /* what to call */

            pHyperCtx = CPUMGetHyperCtxPtr(pVCpu);
            pGuestCtx = CPUMQueryGuestCtxPtr(pVCpu);

            /* Copy the hypervisor context to make sure we have a valid guest context. */
            *pGuestCtx = *pHyperCtx;
            pGuestCtx->cr3 = CR3Phys;

            VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TO_R3);
            VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TIMER);
            VM_FF_CLEAR(pVM, VM_FF_TM_VIRTUAL_SYNC);

            uint64_t TickThisStart = ASMReadTSC();
            rc = SUPR3CallVMMR0Fast(pVM->pVMR0, VMMR0_DO_HWACC_RUN, 0);
            uint64_t TickThisElapsed = ASMReadTSC() - TickThisStart;
            if (RT_FAILURE(rc))
            {
                Log(("VMM: R0 returned fatal %Rrc in iteration %d\n", rc, i));
                VMMR3FatalDump(pVM, pVCpu, rc);
                return rc;
            }
            if (TickThisElapsed < TickMin)
                TickMin = TickThisElapsed;
        }
        uint64_t TickEnd = ASMReadTSC();
        uint64_t tsEnd = RTTimeNanoTS();

        uint64_t Elapsed = tsEnd - tsBegin;
        uint64_t PerIteration = Elapsed / (uint64_t)i;
        uint64_t cTicksElapsed = TickEnd - TickStart;
        uint64_t cTicksPerIteration = cTicksElapsed / (uint64_t)i;

        RTPrintf("VMM: %8d cycles     in %11llu ns (%11lld ticks),  %10llu ns/iteration (%11lld ticks)  Min %11lld ticks\n",
                 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin);
        Log(("VMM: %8d cycles     in %11llu ns (%11lld ticks),  %10llu ns/iteration (%11lld ticks)  Min %11lld ticks\n",
             i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin));

        rc = VINF_SUCCESS;
    }
    else
        AssertMsgFailed(("Failed to resolved VMMGC.gc::VMMGCEntry(), rc=%Rrc\n", rc));

    return rc;
}
示例#14
0
/* execute the switch. */
VMMR3DECL(int) VMMDoTest(PVM pVM)
{
#if 1
    PVMCPU pVCpu = &pVM->aCpus[0];

#ifdef NO_SUPCALLR0VMM
    RTPrintf("NO_SUPCALLR0VMM\n");
    return VINF_SUCCESS;
#endif

    /*
     * Setup stack for calling VMMGCEntry().
     */
    RTRCPTR RCPtrEP;
    int rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "VMMGCEntry", &RCPtrEP);
    if (RT_SUCCESS(rc))
    {
        RTPrintf("VMM: VMMGCEntry=%RRv\n", RCPtrEP);

        /*
         * Test various crashes which we must be able to recover from.
         */
        vmmR3DoTrapTest(pVM, 0x3, 0, VINF_EM_DBG_HYPER_ASSERTION,  0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3");
        vmmR3DoTrapTest(pVM, 0x3, 1, VINF_EM_DBG_HYPER_ASSERTION,  0xf0f0f0f0, "vmmGCTestTrap3_FaultEIP", "int3 WP");

#if defined(DEBUG_bird) /* guess most people would like to skip these since they write to com1. */
        vmmR3DoTrapTest(pVM, 0x8, 0, VERR_TRPM_PANIC,       0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG]");
        SELMR3Relocate(pVM); /* this resets the busy flag of the Trap 08 TSS */
        bool f;
        rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "DoubleFault", &f);
#if !defined(DEBUG_bird)
        if (RT_SUCCESS(rc) && f)
#endif
        {
            /* see triple fault warnings in SELM and VMMGC.cpp. */
            vmmR3DoTrapTest(pVM, 0x8, 1, VERR_TRPM_PANIC,       0x00000000, "vmmGCTestTrap8_FaultEIP", "#DF [#PG] WP");
            SELMR3Relocate(pVM); /* this resets the busy flag of the Trap 08 TSS */
        }
#endif

        vmmR3DoTrapTest(pVM, 0xd, 0, VERR_TRPM_DONT_PANIC,  0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP");
        ///@todo find a better \#GP case, on intel ltr will \#PF (busy update?) and not \#GP.
        //vmmR3DoTrapTest(pVM, 0xd, 1, VERR_TRPM_DONT_PANIC,  0xf0f0f0f0, "vmmGCTestTrap0d_FaultEIP", "ltr #GP WP");

        vmmR3DoTrapTest(pVM, 0xe, 0, VERR_TRPM_DONT_PANIC,  0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL)");
        vmmR3DoTrapTest(pVM, 0xe, 1, VERR_TRPM_DONT_PANIC,  0x00000000, "vmmGCTestTrap0e_FaultEIP", "#PF (NULL) WP");
        vmmR3DoTrapTest(pVM, 0xe, 2, VINF_SUCCESS,          0x00000000, NULL,                       "#PF w/Tmp Handler");
        /* This test is no longer relevant as fs and gs are loaded with NULL
           selectors and we will always return to HC if a #GP occurs while
           returning to guest code.
        vmmR3DoTrapTest(pVM, 0xe, 4, VINF_SUCCESS,          0x00000000, NULL,                       "#PF w/Tmp Handler and bad fs");
        */

        /*
         * Set a debug register and perform a context switch.
         */
        rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
        if (rc != VINF_SUCCESS)
        {
            RTPrintf("VMM: Nop test failed, rc=%Rrc not VINF_SUCCESS\n", rc);
            return rc;
        }

        /* a harmless breakpoint */
        RTPrintf("VMM: testing hardware bp at 0x10000 (not hit)\n");
        DBGFADDRESS Addr;
        DBGFR3AddrFromFlat(pVM, &Addr, 0x10000);
        RTUINT iBp0;
        rc = DBGFR3BpSetReg(pVM, &Addr, 0,  ~(uint64_t)0, X86_DR7_RW_EO, 1, &iBp0);
        AssertReleaseRC(rc);
        rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
        if (rc != VINF_SUCCESS)
        {
            RTPrintf("VMM: DR0=0x10000 test failed with rc=%Rrc!\n", rc);
            return rc;
        }

        /* a bad one at VMMGCEntry */
        RTPrintf("VMM: testing hardware bp at VMMGCEntry (hit)\n");
        DBGFR3AddrFromFlat(pVM, &Addr, RCPtrEP);
        RTUINT iBp1;
        rc = DBGFR3BpSetReg(pVM, &Addr, 0,  ~(uint64_t)0, X86_DR7_RW_EO, 1, &iBp1);
        AssertReleaseRC(rc);
        rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
        if (rc != VINF_EM_DBG_HYPER_BREAKPOINT)
        {
            RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Rrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc);
            return rc;
        }

        /* resume the breakpoint */
        RTPrintf("VMM: resuming hyper after breakpoint\n");
        CPUMSetHyperEFlags(pVCpu, CPUMGetHyperEFlags(pVCpu) | X86_EFL_RF);
        rc = VMMR3ResumeHyper(pVM, pVCpu);
        if (rc != VINF_SUCCESS)
        {
            RTPrintf("VMM: failed to resume on hyper breakpoint, rc=%Rrc = KNOWN BUG\n", rc); /** @todo fix VMMR3ResumeHyper */
            return rc;
        }

        /* engage the breakpoint again and try single stepping. */
        RTPrintf("VMM: testing hardware bp at VMMGCEntry + stepping\n");
        rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
        if (rc != VINF_EM_DBG_HYPER_BREAKPOINT)
        {
            RTPrintf("VMM: DR1=VMMGCEntry test failed with rc=%Rrc! expected VINF_EM_RAW_BREAKPOINT_HYPER\n", rc);
            return rc;
        }

        RTGCUINTREG OldPc = CPUMGetHyperEIP(pVCpu);
        RTPrintf("%RGr=>", OldPc);
        unsigned i;
        for (i = 0; i < 8; i++)
        {
            CPUMSetHyperEFlags(pVCpu, CPUMGetHyperEFlags(pVCpu) | X86_EFL_TF | X86_EFL_RF);
            rc = VMMR3ResumeHyper(pVM, pVCpu);
            if (rc != VINF_EM_DBG_HYPER_STEPPED)
            {
                RTPrintf("\nVMM: failed to step on hyper breakpoint, rc=%Rrc\n", rc);
                return rc;
            }
            RTGCUINTREG Pc = CPUMGetHyperEIP(pVCpu);
            RTPrintf("%RGr=>", Pc);
            if (Pc == OldPc)
            {
                RTPrintf("\nVMM: step failed, PC: %RGr -> %RGr\n", OldPc, Pc);
                return VERR_GENERAL_FAILURE;
            }
            OldPc = Pc;
        }
        RTPrintf("ok\n");

        /* done, clear it */
        if (    RT_FAILURE(DBGFR3BpClear(pVM, iBp0))
            ||  RT_FAILURE(DBGFR3BpClear(pVM, iBp1)))
        {
            RTPrintf("VMM: Failed to clear breakpoints!\n");
            return VERR_GENERAL_FAILURE;
        }
        rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_NOP, 0);
        if (rc != VINF_SUCCESS)
        {
            RTPrintf("VMM: NOP failed, rc=%Rrc\n", rc);
            return rc;
        }

        /*
         * Interrupt masking.
         */
        RTPrintf("VMM: interrupt masking...\n"); RTStrmFlush(g_pStdOut); RTThreadSleep(250);
        for (i = 0; i < 10000; i++)
        {
            uint64_t StartTick = ASMReadTSC();
            rc = vmmR3DoGCTest(pVM, VMMGC_DO_TESTCASE_INTERRUPT_MASKING, 0);
            if (rc != VINF_SUCCESS)
            {
                RTPrintf("VMM: Interrupt masking failed: rc=%Rrc\n", rc);
                return rc;
            }
            uint64_t Ticks = ASMReadTSC() - StartTick;
            if (Ticks < (SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000))
                RTPrintf("Warning: Ticks=%RU64 (< %RU64)\n", Ticks, SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) / 10000);
        }

        /*
         * Interrupt forwarding.
         */
        CPUMSetHyperState(pVCpu, pVM->vmm.s.pfnCallTrampolineRC, pVCpu->vmm.s.pbEMTStackBottomRC, 0, 0);
        CPUMPushHyper(pVCpu, 0);
        CPUMPushHyper(pVCpu, VMMGC_DO_TESTCASE_HYPER_INTERRUPT);
        CPUMPushHyper(pVCpu, pVM->pVMRC);
        CPUMPushHyper(pVCpu, 3 * sizeof(RTRCPTR));    /* stack frame size */
        CPUMPushHyper(pVCpu, RCPtrEP);                /* what to call */
        Log(("trampoline=%x\n", pVM->vmm.s.pfnCallTrampolineRC));

        /*
         * Switch and do da thing.
         */
        RTPrintf("VMM: interrupt forwarding...\n"); RTStrmFlush(g_pStdOut); RTThreadSleep(250);
        i = 0;
        uint64_t    tsBegin = RTTimeNanoTS();
        uint64_t    TickStart = ASMReadTSC();
        Assert(CPUMGetHyperCR3(pVCpu) && CPUMGetHyperCR3(pVCpu) == PGMGetHyperCR3(pVCpu));
        do
        {
            rc = SUPR3CallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0);
            if (RT_LIKELY(rc == VINF_SUCCESS))
                rc = pVCpu->vmm.s.iLastGZRc;
            if (RT_FAILURE(rc))
            {
                Log(("VMM: GC returned fatal %Rra in iteration %d\n", rc, i));
                VMMR3FatalDump(pVM, pVCpu, rc);
                return rc;
            }
            i++;
            if (!(i % 32))
                Log(("VMM: iteration %d, esi=%08x edi=%08x ebx=%08x\n",
                       i, CPUMGetHyperESI(pVCpu), CPUMGetHyperEDI(pVCpu), CPUMGetHyperEBX(pVCpu)));
        } while (rc == VINF_EM_RAW_INTERRUPT_HYPER);
        uint64_t    TickEnd = ASMReadTSC();
        uint64_t    tsEnd = RTTimeNanoTS();

        uint64_t    Elapsed = tsEnd - tsBegin;
        uint64_t    PerIteration = Elapsed / (uint64_t)i;
        uint64_t    cTicksElapsed = TickEnd - TickStart;
        uint64_t    cTicksPerIteration = cTicksElapsed / (uint64_t)i;

        RTPrintf("VMM: %8d interrupts in %11llu ns (%11llu ticks),  %10llu ns/iteration (%11llu ticks)\n",
                 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration);
        Log(("VMM: %8d interrupts in %11llu ns (%11llu ticks),  %10llu ns/iteration (%11llu ticks)\n",
             i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration));

        /*
         * These forced actions are not necessary for the test and trigger breakpoints too.
         */
        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TRPM_SYNC_IDT);
        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_SELM_SYNC_TSS);

        /*
         * Profile switching.
         */
        RTPrintf("VMM: profiling switcher...\n");
        Log(("VMM: profiling switcher...\n"));
        uint64_t TickMin = ~0;
        tsBegin = RTTimeNanoTS();
        TickStart = ASMReadTSC();
        Assert(CPUMGetHyperCR3(pVCpu) && CPUMGetHyperCR3(pVCpu) == PGMGetHyperCR3(pVCpu));
        for (i = 0; i < 1000000; i++)
        {
            CPUMSetHyperState(pVCpu, pVM->vmm.s.pfnCallTrampolineRC, pVCpu->vmm.s.pbEMTStackBottomRC, 0, 0);
            CPUMPushHyper(pVCpu, 0);
            CPUMPushHyper(pVCpu, VMMGC_DO_TESTCASE_NOP);
            CPUMPushHyper(pVCpu, pVM->pVMRC);
            CPUMPushHyper(pVCpu, 3 * sizeof(RTRCPTR));    /* stack frame size */
            CPUMPushHyper(pVCpu, RCPtrEP);                /* what to call */

            uint64_t TickThisStart = ASMReadTSC();
            rc = SUPR3CallVMMR0Fast(pVM->pVMR0, VMMR0_DO_RAW_RUN, 0);
            if (RT_LIKELY(rc == VINF_SUCCESS))
                rc = pVCpu->vmm.s.iLastGZRc;
            uint64_t TickThisElapsed = ASMReadTSC() - TickThisStart;
            if (RT_FAILURE(rc))
            {
                Log(("VMM: GC returned fatal %Rra in iteration %d\n", rc, i));
                VMMR3FatalDump(pVM, pVCpu, rc);
                return rc;
            }
            if (TickThisElapsed < TickMin)
                TickMin = TickThisElapsed;
        }
        TickEnd = ASMReadTSC();
        tsEnd = RTTimeNanoTS();

        Elapsed = tsEnd - tsBegin;
        PerIteration = Elapsed / (uint64_t)i;
        cTicksElapsed = TickEnd - TickStart;
        cTicksPerIteration = cTicksElapsed / (uint64_t)i;

        RTPrintf("VMM: %8d cycles     in %11llu ns (%11lld ticks),  %10llu ns/iteration (%11lld ticks)  Min %11lld ticks\n",
                 i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin);
        Log(("VMM: %8d cycles     in %11llu ns (%11lld ticks),  %10llu ns/iteration (%11lld ticks)  Min %11lld ticks\n",
             i, Elapsed, cTicksElapsed, PerIteration, cTicksPerIteration, TickMin));

        rc = VINF_SUCCESS;
    }
    else
        AssertMsgFailed(("Failed to resolved VMMGC.gc::VMMGCEntry(), rc=%Rrc\n", rc));
#endif
    return rc;
}