VBoxNetBaseService::~VBoxNetBaseService()
{
    /*
     * Close the interface connection.
     */
    if (m)
    {
        shutdown();
        if (m->m_hIf != INTNET_HANDLE_INVALID)
        {
            INTNETIFCLOSEREQ CloseReq;
            CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
            CloseReq.Hdr.cbReq = sizeof(CloseReq);
            CloseReq.pSession = m->m_pSession;
            CloseReq.hIf = m->m_hIf;
            m->m_hIf = INTNET_HANDLE_INVALID;
            int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_CLOSE, 0, &CloseReq.Hdr);
            AssertRC(rc);
        }

        if (m->m_pSession != NIL_RTR0PTR)
        {
            SUPR3Term(false /*fForced*/);
            m->m_pSession = NIL_RTR0PTR;
        }

        RTCritSectDelete(&m->m_csThis);

        delete m;
        m = NULL;
    }
}
int main(int argc, char **argv)
{
    int rc;
    RTR3InitExe(argc, &argv, 0);
    rc = SUPR3Init(NULL);
    if (RT_SUCCESS(rc))
    {
        SUPPAGINGMODE enmMode = SUPR3GetPagingMode();
        switch (enmMode)
        {
            case SUPPAGINGMODE_INVALID:
                RTPrintf("SUPPAGINGMODE_INVALID\n");
                break;
            case SUPPAGINGMODE_32_BIT:
                RTPrintf("SUPPAGINGMODE_32_BIT\n");
                break;
            case SUPPAGINGMODE_32_BIT_GLOBAL:
                RTPrintf("SUPPAGINGMODE_32_BIT_GLOBAL\n");
                break;
            case SUPPAGINGMODE_PAE:
                RTPrintf("SUPPAGINGMODE_PAE\n");
                break;
            case SUPPAGINGMODE_PAE_GLOBAL:
                RTPrintf("SUPPAGINGMODE_PAE_GLOBAL\n");
                break;
            case SUPPAGINGMODE_PAE_NX:
                RTPrintf("SUPPAGINGMODE_PAE_NX\n");
                break;
            case SUPPAGINGMODE_PAE_GLOBAL_NX:
                RTPrintf("SUPPAGINGMODE_PAE_GLOBAL_NX\n");
                break;
            case SUPPAGINGMODE_AMD64:
                RTPrintf("SUPPAGINGMODE_AMD64\n");
                break;
            case SUPPAGINGMODE_AMD64_GLOBAL:
                RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL\n");
                break;
            case SUPPAGINGMODE_AMD64_NX:
                RTPrintf("SUPPAGINGMODE_AMD64_NX\n");
                break;
            case SUPPAGINGMODE_AMD64_GLOBAL_NX:
                RTPrintf("SUPPAGINGMODE_AMD64_GLOBAL_NX\n");
                break;
            default:
                RTPrintf("Unknown mode %d\n", enmMode);
                rc = VERR_INTERNAL_ERROR;
                break;
        }

        int rc2 = SUPR3Term(false /*fForced*/);
        RTPrintf("SUPR3Term -> rc=%Rrc\n", rc2);
    }
    else
        RTPrintf("SUPR3Init -> rc=%Rrc\n", rc);

    return !RT_SUCCESS(rc);
}
示例#3
0
int main(int argc, char **argv)
{
    int rc;
    RTR3InitExe(argc, &argv, 0);
    rc = SUPR3Init(NULL);
    RTPrintf("tstInit: SUPR3Init -> rc=%Rrc\n", rc);
    if (!rc)
    {
        rc = SUPR3Term(false /*fForced*/);
        RTPrintf("tstInit: SUPR3Term -> rc=%Rrc\n", rc);
    }

    return rc;
}
示例#4
0
int main(int argc, char **argv)
{
    RTR3InitExe(argc, &argv, 0);

    /*
     * Parse args
     */
    static const RTGETOPTDEF g_aOptions[] =
    {
        { "--iterations",       'i', RTGETOPT_REQ_INT32 },
        { "--hex",              'h', RTGETOPT_REQ_NOTHING },
        { "--decimal",          'd', RTGETOPT_REQ_NOTHING },
        { "--spin",             's', RTGETOPT_REQ_NOTHING }
    };

    uint32_t cIterations = 40;
    bool fHex = true;
    bool fSpin = false;
    int ch;
    RTGETOPTUNION ValueUnion;
    RTGETOPTSTATE GetState;
    RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch (ch)
        {
            case 'i':
                cIterations = ValueUnion.u32;
                break;

            case 'd':
                fHex = false;
                break;

            case 'h':
                fHex = true;
                break;

            case 's':
                fSpin = true;
                break;

            default:
                return RTGetOptPrintError(ch, &ValueUnion);
        }
    }

    /*
     * Init
     */
    PSUPDRVSESSION pSession = NIL_RTR0PTR;
    int rc = SUPR3Init(&pSession);
    if (RT_SUCCESS(rc))
    {
        if (g_pSUPGlobalInfoPage)
        {
            RTPrintf("tstGIP-2: u32UpdateHz=%RU32  u32UpdateIntervalNS=%RU32  u64NanoTSLastUpdateHz=%RX64  u32Mode=%d (%s) u32Version=%#x\n",
                     g_pSUPGlobalInfoPage->u32UpdateHz,
                     g_pSUPGlobalInfoPage->u32UpdateIntervalNS,
                     g_pSUPGlobalInfoPage->u64NanoTSLastUpdateHz,
                     g_pSUPGlobalInfoPage->u32Mode,
                     g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_SYNC_TSC       ? "sync"
                     : g_pSUPGlobalInfoPage->u32Mode == SUPGIPMODE_ASYNC_TSC    ? "async"
                     :                                                            "???",
                     g_pSUPGlobalInfoPage->u32Version);
            RTPrintf(fHex
                     ? "tstGIP-2:     it: u64NanoTS        delta     u64TSC           UpIntTSC H  TransId           CpuHz TSC Interval History...\n"
                     : "tstGIP-2:     it: u64NanoTS        delta     u64TSC             UpIntTSC H    TransId           CpuHz TSC Interval History...\n");
            static SUPGIPCPU s_aaCPUs[2][RT_ELEMENTS(g_pSUPGlobalInfoPage->aCPUs)];
            for (uint32_t i = 0; i < cIterations; i++)
            {
                /* copy the data */
                memcpy(&s_aaCPUs[i & 1][0], &g_pSUPGlobalInfoPage->aCPUs[0], sizeof(g_pSUPGlobalInfoPage->aCPUs));

                /* display it & find something to spin on. */
                uint32_t u32TransactionId = 0;
                uint32_t volatile *pu32TransactionId = NULL;
                for (unsigned iCpu = 0; iCpu < RT_ELEMENTS(g_pSUPGlobalInfoPage->aCPUs); iCpu++)
                    if (    g_pSUPGlobalInfoPage->aCPUs[iCpu].u64CpuHz > 0
                        &&  g_pSUPGlobalInfoPage->aCPUs[iCpu].u64CpuHz != _4G + 1)
                    {
                        PSUPGIPCPU pPrevCpu = &s_aaCPUs[!(i & 1)][iCpu];
                        PSUPGIPCPU pCpu = &s_aaCPUs[i & 1][iCpu];
                        RTPrintf(fHex
                                 ? "tstGIP-2: %4d/%d: %016llx %09llx %016llx %08x %d %08x %15llu %08x %08x %08x %08x %08x %08x %08x %08x (%d)\n"
                                 : "tstGIP-2: %4d/%d: %016llu %09llu %016llu %010u %d %010u %15llu %08x %08x %08x %08x %08x %08x %08x %08x (%d)\n",
                                 i, iCpu,
                                 pCpu->u64NanoTS,
                                 i ? pCpu->u64NanoTS - pPrevCpu->u64NanoTS : 0,
                                 pCpu->u64TSC,
                                 pCpu->u32UpdateIntervalTSC,
                                 pCpu->iTSCHistoryHead,
                                 pCpu->u32TransactionId,
                                 pCpu->u64CpuHz,
                                 pCpu->au32TSCHistory[0],
                                 pCpu->au32TSCHistory[1],
                                 pCpu->au32TSCHistory[2],
                                 pCpu->au32TSCHistory[3],
                                 pCpu->au32TSCHistory[4],
                                 pCpu->au32TSCHistory[5],
                                 pCpu->au32TSCHistory[6],
                                 pCpu->au32TSCHistory[7],
                                 pCpu->cErrors);
                        if (!pu32TransactionId)
                        {
                            pu32TransactionId = &g_pSUPGlobalInfoPage->aCPUs[iCpu].u32TransactionId;
                            u32TransactionId = pCpu->u32TransactionId;
                        }
                    }

                /* wait a bit / spin */
                if (!fSpin)
                    RTThreadSleep(9);
                else
                    while (u32TransactionId == *pu32TransactionId)
                        /* nop */;
            }
        }
        else
        {
            RTPrintf("tstGIP-2: g_pSUPGlobalInfoPage is NULL\n");
            rc = -1;
        }

        SUPR3Term(false /*fForced*/);
    }
    else
        RTPrintf("tstGIP-2: SUPR3Init failed: %Rrc\n", rc);
    return !!rc;
}
示例#5
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);
    }
示例#6
0
int main(int argc, char **argv)
{
    RTTEST hTest;
    RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, 0 /*fRtInit*/, "tstSupTscDelta", &hTest);
    if (rcExit != RTEXITCODE_SUCCESS)
        return rcExit;

    /*
     * Parse args
     */
    static const RTGETOPTDEF g_aOptions[] =
    {
        { "--iterations",       'i', RTGETOPT_REQ_INT32 },
        { "--delay",            'd', RTGETOPT_REQ_INT32 },
    };

    uint32_t cIterations = 0; /* Currently 0 so that it doesn't upset testing. */
    uint32_t cMsSleepBetweenIterations = 10;

    int           ch;
    RTGETOPTUNION ValueUnion;
    RTGETOPTSTATE GetState;
    RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch (ch)
        {
        case 'd':
            cMsSleepBetweenIterations = ValueUnion.u32;
            break;
        case 'i':
            cIterations = ValueUnion.u32;
            break;

        default:
            return RTGetOptPrintError(ch, &ValueUnion);
        }
    }
    if (!cIterations)
        return RTTestSkipAndDestroy(hTest, "Nothing to do. The --iterations argument is 0 or not given.");

    /*
     * Init
     */
    PSUPDRVSESSION pSession = NIL_RTR0PTR;
    int rc = SUPR3Init(&pSession);
    if (RT_SUCCESS(rc))
    {
        PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
        if (pGip)
        {
            if (pGip->enmUseTscDelta < SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
                return RTTestSkipAndDestroy(hTest, "No deltas to play with: enmUseTscDelta=%d\n", pGip->enmUseTscDelta);

            /*
             * Init stats.
             */
            struct
            {
                int64_t iLowest;
                int64_t iHighest;
                int64_t iTotal;
                uint64_t uAbsMin;
                uint64_t uAbsMax;
                uint64_t uAbsTotal;
            } aCpuStats[RTCPUSET_MAX_CPUS];
            RT_ZERO(aCpuStats);
            for (uint32_t i = 0; i < pGip->cCpus; i++)
            {
                aCpuStats[i].iLowest  = INT64_MAX;
                aCpuStats[i].iHighest = INT64_MIN;
                aCpuStats[i].uAbsMin  = UINT64_MAX;
            }

            /*
             * Do the work.
             */
            for (uint32_t iIteration = 0; ; iIteration++)
            {
                /*
                 * Display the current deltas and gather statistics.
                 */
                RTPrintf("tstSupTscDelta: Iteration #%u results:", iIteration);
                for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
                {
                    int64_t iTscDelta = pGip->aCPUs[iCpu].i64TSCDelta;

                    /* print */
                    if ((iCpu % 4) == 0)
                        RTPrintf("\ntstSupTscDelta:");
                    if (pGip->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_ONLINE)
                        RTPrintf("  %02x: offline     ", iCpu);
                    else if (iTscDelta != INT64_MAX)
                        RTPrintf("  %02x: %-12lld", iCpu, iTscDelta);
                    else
                        RTPrintf("  %02x: INT64_MAX   ", iCpu);

                    /* stats */
                    if (   iTscDelta != INT64_MAX
                            && pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
                    {
                        if (aCpuStats[iCpu].iLowest > iTscDelta)
                            aCpuStats[iCpu].iLowest = iTscDelta;
                        if (aCpuStats[iCpu].iHighest < iTscDelta)
                            aCpuStats[iCpu].iHighest = iTscDelta;
                        aCpuStats[iCpu].iTotal += iTscDelta;

                        uint64_t uAbsTscDelta = iTscDelta >= 0 ? (uint64_t)iTscDelta : (uint64_t)-iTscDelta;
                        if (aCpuStats[iCpu].uAbsMin > uAbsTscDelta)
                            aCpuStats[iCpu].uAbsMin = uAbsTscDelta;
                        if (aCpuStats[iCpu].uAbsMax < uAbsTscDelta)
                            aCpuStats[iCpu].uAbsMax = uAbsTscDelta;
                        aCpuStats[iCpu].uAbsTotal += uAbsTscDelta;
                    }
                }
                if (((pGip->cCpus - 1) % 4) != 0)
                    RTPrintf("\n");

                /*
                 * Done?
                 */
                if (iIteration + 1 >= cIterations)
                    break;

                /*
                 * Force a new measurement.
                 */
                RTThreadSleep(cMsSleepBetweenIterations);
                for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
                    if (pGip->aCPUs[iCpu].enmState == SUPGIPCPUSTATE_ONLINE)
                    {
                        rc = SUPR3TscDeltaMeasure(pGip->aCPUs[iCpu].idCpu, false /*fAsync*/, true /*fForce*/, 64, 16 /*ms*/);
                        if (RT_FAILURE(rc))
                            RTTestFailed(hTest, "SUPR3TscDeltaMeasure failed on %#x: %Rrc", pGip->aCPUs[iCpu].idCpu, rc);
                    }
            }

            /*
             * Display statistics that we've gathered.
             */
            RTPrintf("tstSupTscDelta: Results:\n");
            int64_t  iLowest  = INT64_MAX;
            int64_t  iHighest = INT64_MIN;
            int64_t  iTotal   = 0;
            uint32_t cTotal   = 0;
            for (uint32_t iCpu = 0; iCpu < pGip->cCpus; iCpu++)
            {
                if (pGip->aCPUs[iCpu].enmState != SUPGIPCPUSTATE_ONLINE)
                    RTPrintf("tstSupTscDelta:  %02x: offline\n", iCpu);
                else
                {
                    RTPrintf("tstSupTscDelta:  %02x: lowest=%-12lld  highest=%-12lld  average=%-12lld  spread=%-12lld\n",
                             iCpu,
                             aCpuStats[iCpu].iLowest,
                             aCpuStats[iCpu].iHighest,
                             aCpuStats[iCpu].iTotal / cIterations,
                             aCpuStats[iCpu].iHighest - aCpuStats[iCpu].iLowest);
                    RTPrintf(  "tstSupTscDelta:      absmin=%-12llu   absmax=%-12llu   absavg=%-12llu  idCpu=%#4x  idApic=%#4x\n",
                               aCpuStats[iCpu].uAbsMin,
                               aCpuStats[iCpu].uAbsMax,
                               aCpuStats[iCpu].uAbsTotal / cIterations,
                               pGip->aCPUs[iCpu].idCpu,
                               pGip->aCPUs[iCpu].idApic);
                    if (iLowest > aCpuStats[iCpu].iLowest)
                        iLowest = aCpuStats[iCpu].iLowest;
                    if (iHighest < aCpuStats[iCpu].iHighest)
                        iHighest = aCpuStats[iCpu].iHighest;
                    iTotal += aCpuStats[iCpu].iHighest;
                    cTotal += cIterations;
                }
            }
            RTPrintf("tstSupTscDelta: all: lowest=%-12lld  highest=%-12lld  average=%-12lld  spread=%-12lld\n",
                     iLowest, iHighest, iTotal / cTotal, iHighest - iLowest);
        }
        else
            RTTestFailed(hTest, "g_pSUPGlobalInfoPage is NULL");

        SUPR3Term(false /*fForced*/);
    }
    else
        RTTestFailed(hTest, "SUPR3Init failed: %Rrc", rc);
    return RTTestSummaryAndDestroy(hTest);
}
示例#7
0
文件: tstGIP-2.cpp 项目: mcenirm/vbox
int main(int argc, char **argv)
{
    RTR3InitExe(argc, &argv, 0);

    /*
     * Parse args
     */
    static const RTGETOPTDEF g_aOptions[] =
    {
        { "--iterations",       'i', RTGETOPT_REQ_INT32 },
        { "--hex",              'h', RTGETOPT_REQ_NOTHING },
        { "--decimal",          'd', RTGETOPT_REQ_NOTHING },
        { "--spin",             's', RTGETOPT_REQ_NOTHING },
        { "--reference",        'r', RTGETOPT_REQ_UINT64 },  /* reference value of CpuHz, display the
                                                              * CpuHz deviation in a separate column. */
    };

    uint32_t cIterations = 40;
    bool fHex = true;
    bool fSpin = false;
    int ch;
    uint64_t uCpuHzRef = 0;
    uint64_t uCpuHzOverallDeviation = 0;
    int64_t  iCpuHzMaxDeviation = 0;
    int32_t cCpuHzOverallDevCnt = 0;
    RTGETOPTUNION ValueUnion;
    RTGETOPTSTATE GetState;
    RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch (ch)
        {
            case 'i':
                cIterations = ValueUnion.u32;
                break;

            case 'd':
                fHex = false;
                break;

            case 'h':
                fHex = true;
                break;

            case 's':
                fSpin = true;
                break;

            case 'r':
                uCpuHzRef = ValueUnion.u64;
                break;

            default:
                return RTGetOptPrintError(ch, &ValueUnion);
        }
    }

    /*
     * Init
     */
    PSUPDRVSESSION pSession = NIL_RTR0PTR;
    int rc = SUPR3Init(&pSession);
    if (RT_SUCCESS(rc))
    {
        if (g_pSUPGlobalInfoPage)
        {
            RTPrintf("tstGIP-2: cCpus=%d  u32UpdateHz=%RU32  u32UpdateIntervalNS=%RU32  u64NanoTSLastUpdateHz=%RX64  u64CpuHz=%RU64  uCpuHzRef=%RU64  u32Mode=%d (%s) u32Version=%#x\n",
                     g_pSUPGlobalInfoPage->cCpus,
                     g_pSUPGlobalInfoPage->u32UpdateHz,
                     g_pSUPGlobalInfoPage->u32UpdateIntervalNS,
                     g_pSUPGlobalInfoPage->u64NanoTSLastUpdateHz,
                     g_pSUPGlobalInfoPage->u64CpuHz,
                     uCpuHzRef,
                     g_pSUPGlobalInfoPage->u32Mode,
                     SUPGetGIPModeName(g_pSUPGlobalInfoPage),
                     g_pSUPGlobalInfoPage->u32Version);
            RTPrintf(fHex
                     ? "tstGIP-2:     it: u64NanoTS        delta     u64TSC           UpIntTSC H  TransId      CpuHz      %sTSC Interval History...\n"
                     : "tstGIP-2:     it: u64NanoTS        delta     u64TSC             UpIntTSC H    TransId      CpuHz      %sTSC Interval History...\n",
                     uCpuHzRef ? "  CpuHz deviation  " : "");
            static SUPGIPCPU s_aaCPUs[2][256];
            for (uint32_t i = 0; i < cIterations; i++)
            {
                /* copy the data */
                memcpy(&s_aaCPUs[i & 1][0], &g_pSUPGlobalInfoPage->aCPUs[0], g_pSUPGlobalInfoPage->cCpus * sizeof(g_pSUPGlobalInfoPage->aCPUs[0]));

                /* display it & find something to spin on. */
                uint32_t u32TransactionId = 0;
                uint32_t volatile *pu32TransactionId = NULL;
                for (unsigned iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
                    if (    g_pSUPGlobalInfoPage->aCPUs[iCpu].u64CpuHz > 0
                        &&  g_pSUPGlobalInfoPage->aCPUs[iCpu].u64CpuHz != _4G + 1)
                    {
                        char szCpuHzDeviation[32];
                        PSUPGIPCPU pPrevCpu = &s_aaCPUs[!(i & 1)][iCpu];
                        PSUPGIPCPU pCpu = &s_aaCPUs[i & 1][iCpu];
                        if (uCpuHzRef)
                        {
                            int64_t iCpuHzDeviation = pCpu->u64CpuHz - uCpuHzRef;
                            uint64_t uCpuHzDeviation = RT_ABS(iCpuHzDeviation);
                            if (uCpuHzDeviation > 999999999)
                                RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%17s  ", "?");
                            else
                            {
                                /* Wait until the history validation code takes effect. */
                                if (pCpu->u32TransactionId > 23 + (8 * 2) + 1)
                                {
                                    if (RT_ABS(iCpuHzDeviation) > RT_ABS(iCpuHzMaxDeviation))
                                        iCpuHzMaxDeviation = iCpuHzDeviation;
                                    uCpuHzOverallDeviation += uCpuHzDeviation;
                                    cCpuHzOverallDevCnt++;
                                }
                                uint32_t uPct = (uint32_t)(uCpuHzDeviation * 100000 / uCpuHzRef + 5);
                                RTStrPrintf(szCpuHzDeviation, sizeof(szCpuHzDeviation), "%10RI64%3d.%02d%%  ",
                                            iCpuHzDeviation, uPct / 1000, (uPct % 1000) / 10);
                            }
                        }
                        else
                            szCpuHzDeviation[0] = '\0';
                        RTPrintf(fHex
                                 ? "tstGIP-2: %4d/%d: %016llx %09llx %016llx %08x %d %08x %15llu %s%08x %08x %08x %08x %08x %08x %08x %08x (%d)\n"
                                 : "tstGIP-2: %4d/%d: %016llu %09llu %016llu %010u %d %010u %15llu %s%08x %08x %08x %08x %08x %08x %08x %08x (%d)\n",
                                 i, iCpu,
                                 pCpu->u64NanoTS,
                                 i ? pCpu->u64NanoTS - pPrevCpu->u64NanoTS : 0,
                                 pCpu->u64TSC,
                                 pCpu->u32UpdateIntervalTSC,
                                 pCpu->iTSCHistoryHead,
                                 pCpu->u32TransactionId,
                                 pCpu->u64CpuHz,
                                 szCpuHzDeviation,
                                 pCpu->au32TSCHistory[0],
                                 pCpu->au32TSCHistory[1],
                                 pCpu->au32TSCHistory[2],
                                 pCpu->au32TSCHistory[3],
                                 pCpu->au32TSCHistory[4],
                                 pCpu->au32TSCHistory[5],
                                 pCpu->au32TSCHistory[6],
                                 pCpu->au32TSCHistory[7],
                                 pCpu->cErrors);
                        if (!pu32TransactionId)
                        {
                            pu32TransactionId = &g_pSUPGlobalInfoPage->aCPUs[iCpu].u32TransactionId;
                            u32TransactionId = pCpu->u32TransactionId;
                        }
                    }

                /* wait a bit / spin */
                if (!fSpin)
                    RTThreadSleep(9);
                else
                {
                    if (pu32TransactionId)
                    {
                        uint32_t uTmp;
                        while (   u32TransactionId == (uTmp = *pu32TransactionId)
                               || (uTmp & 1))
                            ASMNopPause();
                    }
                    else
                        RTThreadSleep(1);
                }
            }

            /*
             * Display TSC deltas.
             *
             * First iterative over the APIC ID array to get mostly consistent CPUID to APIC ID mapping.
             * Then iterate over the offline CPUs. It is possible that there's a race between the online/offline
             * states between the two iterations, but that cannot be helped from ring-3 anyway and not a biggie.
             */
            RTPrintf("tstGIP-2: TSC deltas:\n");
            RTPrintf("tstGIP-2:  idApic: i64TSCDelta\n");
            for (unsigned i = 0; i < RT_ELEMENTS(g_pSUPGlobalInfoPage->aiCpuFromApicId); i++)
            {
                uint16_t iCpu = g_pSUPGlobalInfoPage->aiCpuFromApicId[i];
                if (iCpu != UINT16_MAX)
                {
                    RTPrintf("tstGIP-2: %7d: %lld\n", g_pSUPGlobalInfoPage->aCPUs[iCpu].idApic,
                             g_pSUPGlobalInfoPage->aCPUs[iCpu].i64TSCDelta);
                }
            }

            for (unsigned iCpu = 0; iCpu < g_pSUPGlobalInfoPage->cCpus; iCpu++)
                if (g_pSUPGlobalInfoPage->aCPUs[iCpu].idApic == UINT16_MAX)
                    RTPrintf("tstGIP-2: offline: %lld\n", g_pSUPGlobalInfoPage->aCPUs[iCpu].i64TSCDelta);

            RTPrintf("tstGIP-2: enmUseTscDelta=%d  fGetGipCpu=%#x\n",
                     g_pSUPGlobalInfoPage->enmUseTscDelta, g_pSUPGlobalInfoPage->fGetGipCpu);
            if (   uCpuHzRef
                && cCpuHzOverallDevCnt)
            {
                uint32_t uPct    = (uint32_t)(uCpuHzOverallDeviation * 100000 / cCpuHzOverallDevCnt / uCpuHzRef + 5);
                RTPrintf("tstGIP-2: Average CpuHz deviation: %d.%02d%%\n",
                         uPct / 1000, (uPct % 1000) / 10);

                uint32_t uMaxPct = (uint32_t)(RT_ABS(iCpuHzMaxDeviation) * 100000 / uCpuHzRef + 5);
                RTPrintf("tstGIP-2: Maximum CpuHz deviation: %d.%02d%% (%RI64 ticks)\n",
                         uMaxPct / 1000, (uMaxPct % 1000) / 10, iCpuHzMaxDeviation);
            }
        }
        else
        {
            RTPrintf("tstGIP-2: g_pSUPGlobalInfoPage is NULL\n");
            rc = -1;
        }

        SUPR3Term(false /*fForced*/);
    }
    else
        RTPrintf("tstGIP-2: SUPR3Init failed: %Rrc\n", rc);
    return !!rc;
}
int main(int argc, char **argv)
{
    int rc;
    int rcRet = 0;

    RTR3InitExe(argc, &argv, 0);
    rc = SUPR3Init(NULL);
    RTPrintf("tstContiguous: SUPR3Init -> rc=%Rrc\n", rc);
    rcRet += rc != 0;
    if (!rc)
    {
        /*
         * Allocate a bit of contiguous memory.
         */
        RTHCPHYS HCPhys;
        void *pv = SUPR3ContAlloc(8, NIL_RTR0PTR, &HCPhys);
        rcRet += pv == NULL || HCPhys == 0;
        if (pv && HCPhys)
        {
            memset(pv, 0xff, PAGE_SIZE * 8);
            pv = SUPR3ContAlloc(5, NIL_RTR0PTR, &HCPhys);
            rcRet += pv == NULL || HCPhys == 0;
            if (pv && HCPhys)
            {
                memset(pv, 0x7f, PAGE_SIZE * 5);
                rc = SUPR3ContFree(pv, 5);
                rcRet += rc != 0;
                if (rc)
                    RTPrintf("tstContiguous: SUPR3ContFree failed! rc=%Rrc\n", rc);

                void *apv[128];
                for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
                {
                    apv[i] = SUPR3ContAlloc(1 + (i % 11), NIL_RTR0PTR, &HCPhys);
                    if (!apv[i])
                    {
                        RTPrintf("tstContiguous: i=%d: failed to allocate %d pages\n", i, 1 + (i % 11));
                        rcRet++;
                    }
                }
                for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
                    if (apv[i])
                    {
                        rc = SUPR3ContFree(apv[i], 1 + (i % 11));
                        rcRet += rc != 0;
                        if (rc)
                            RTPrintf("tstContiguous: i=%d SUPR3ContFree failed! rc=%Rrc\n", i, rc);
                    }
            }
            else
                RTPrintf("tstContiguous: SUPR3ContAlloc (2nd) failed!\n");
        }
        else
            RTPrintf("tstContiguous: SUPR3ContAlloc failed!\n");

        rc = SUPR3Term(false /*fForced*/);
        RTPrintf("tstContiguous: SUPR3Term -> rc=%Rrc\n", rc);
        rcRet += rc != 0;
    }

    return rcRet ? 1 : 0;
}