/**
 * Callback function for consuming queued events.
 *
 * @param   pvTarget    queue?
 * @param   rcIn        ?
 * @param   pvRefcon    Pointer to the keyboard cache entry.
 * @param   pvSender    ?
 */
static void darwinQueueCallback(void *pvTarget, IOReturn rcIn, void *pvRefcon, void *pvSender)
{
    struct KeyboardCacheData *pKeyboardEntry = (struct KeyboardCacheData *)pvRefcon;
    if (!pKeyboardEntry->ppHidQueueInterface)
        return;
    NOREF(pvTarget);
    NOREF(rcIn);
    NOREF(pvSender);

    /*
     * Consume the events.
     */
    g_fOldHIDModifierMask = g_fHIDModifierMask;
    for (;;)
    {
#ifdef DEBUG_PRINTF
        RTPrintf("dbg-ev: "); RTStrmFlush(g_pStdOut);
#endif
        IOHIDEventStruct Event;
        AbsoluteTime ZeroTime = {0,0};
        IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->getNextEvent(pKeyboardEntry->ppHidQueueInterface,
                                                                           &Event, ZeroTime, 0);
        if (rc != kIOReturnSuccess)
            break;

        /* Translate the cookie value to a modifier mask. */
        uint32_t fMask = 0;
        unsigned i = pKeyboardEntry->cCookies;
        while (i-- > 0)
        {
            if (pKeyboardEntry->aCookies[i].Cookie == Event.elementCookie)
            {
                fMask = pKeyboardEntry->aCookies[i].fMask;
                break;
            }
        }

        /*
         * Adjust the modifier mask.
         *
         * Note that we don't bother to deal with anyone pressing the same modifier
         * on 2 or more keyboard. That's not worth the effort involved.
         */
        if (Event.value)
            g_fHIDModifierMask |= fMask;
        else
            g_fHIDModifierMask &= ~fMask;
#ifdef DEBUG_PRINTF
        RTPrintf("t=%d c=%#x v=%#x cblv=%d lv=%p m=%#X\n", Event.type, Event.elementCookie, Event.value, Event.longValueSize, Event.value, fMask); RTStrmFlush(g_pStdOut);
#endif
    }
#ifdef DEBUG_PRINTF
    RTPrintf("dbg-ev: done\n"); RTStrmFlush(g_pStdOut);
#endif
}
static int tstRTCreateProcEx3Child(void)
{
    int rc = RTR3InitExeNoArguments(0);
    if (RT_FAILURE(rc))
        return RTMsgInitFailure(rc);

    RTStrmPrintf(g_pStdOut, "w"); RTStrmFlush(g_pStdOut);
    RTStrmPrintf(g_pStdErr, "o"); RTStrmFlush(g_pStdErr);
    RTStrmPrintf(g_pStdOut, "r"); RTStrmFlush(g_pStdOut);
    RTStrmPrintf(g_pStdErr, "k"); RTStrmFlush(g_pStdErr);
    RTStrmPrintf(g_pStdOut, "s");

    return RTEXITCODE_SUCCESS;
}
/**
 * Loads the modules.
 *
 * @returns RTEXITCODE_SUCCESS on success.
 */
static RTEXITCODE LoadModules(void)
{
    for (uint32_t i = 0; i < RT_ELEMENTS(g_aModules); i++)
    {
        if (g_aModules[i].fPreload)
        {
            char            szPath[RTPATH_MAX];
            int rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
            if (RT_SUCCESS(rc))
                rc = RTPathAppend(szPath, sizeof(szPath), g_aModules[i].pszName);
            if (RT_FAILURE(rc))
                return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAppPrivateArch or RTPathAppend returned %Rrc", rc);

            void           *pvImageBase;
            RTERRINFOSTATIC ErrInfo;
            RTErrInfoInitStatic(&ErrInfo);
            rc = SUPR3LoadModule(szPath, g_aModules[i].pszName, &g_aModules[i].pvImageBase, &ErrInfo.Core);
            if (RT_FAILURE(rc))
                return RTMsgErrorExit(RTEXITCODE_FAILURE, "SUPR3LoadModule failed for %s (%s): %s (rc=%Rrc)",
                                      g_aModules[i].pszName, szPath, ErrInfo.Core.pszMsg, rc);
            if (g_cVerbose >= 1)
                RTMsgInfo("Loaded '%s' ('%s') at %p\n", szPath, g_aModules[i].pszName, g_aModules[i].pvImageBase);
        }
    }

    RTStrmFlush(g_pStdOut);
    return RTEXITCODE_SUCCESS;
}
Beispiel #4
0
/**
 * Waits a little while for a debuggger to attach.
 *
 * @returns True is a debugger have attached.
 * @param   pVM         Pointer to the VM.
 * @param   enmEvent    Event.
 */
bool dbgfR3WaitForAttach(PVM pVM, DBGFEVENTTYPE enmEvent)
{
    /*
     * First a message.
     */
#ifndef RT_OS_L4

# if !defined(DEBUG) || defined(DEBUG_sandervl) || defined(DEBUG_frank) || defined(IEM_VERIFICATION_MODE)
    int cWait = 10;
# else
    int cWait = HWACCMIsEnabled(pVM)
             && (   enmEvent == DBGFEVENT_ASSERTION_HYPER
                 || enmEvent == DBGFEVENT_FATAL_ERROR)
             && !RTEnvExist("VBOX_DBGF_WAIT_FOR_ATTACH")
              ? 10
              : 150;
# endif
    RTStrmPrintf(g_pStdErr, "DBGF: No debugger attached, waiting %d second%s for one to attach (event=%d)\n",
                 cWait / 10, cWait != 10 ? "s" : "", enmEvent);
    RTStrmFlush(g_pStdErr);
    while (cWait > 0)
    {
        RTThreadSleep(100);
        if (pVM->dbgf.s.fAttached)
        {
            RTStrmPrintf(g_pStdErr, "Attached!\n");
            RTStrmFlush(g_pStdErr);
            return true;
        }

        /* next */
        if (!(cWait % 10))
        {
            RTStrmPrintf(g_pStdErr, "%d.", cWait / 10);
            RTStrmFlush(g_pStdErr);
        }
        cWait--;
    }
#endif

    RTStrmPrintf(g_pStdErr, "Stopping the VM!\n");
    RTStrmFlush(g_pStdErr);
    return false;
}
static int tstHandleTableTest1(uint32_t uBase, uint32_t cMax, uint32_t cDelta, uint32_t cUnitsPerDot, bool fCallbacks, uint32_t fFlags)
{
    const char *pszWithCtx = fFlags & RTHANDLETABLE_FLAGS_CONTEXT ? "WithCtx" : "";
    uint32_t cRetainerCalls = 0;
    int rc;

    RTPrintf("tstHandleTable: TESTING RTHandleTableCreateEx(, 0");
    if (fFlags & RTHANDLETABLE_FLAGS_LOCKED)    RTPrintf(" | LOCKED");
    if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)   RTPrintf(" | CONTEXT");
    RTPrintf(", %#x, %#x,,)...\n", uBase, cMax);

    RTHANDLETABLE hHT;
    rc = RTHandleTableCreateEx(&hHT, fFlags, uBase, cMax,
                               fCallbacks ? tstHandleTableTest1Retain : NULL,
                               fCallbacks ? &cRetainerCalls : NULL);
    if (RT_FAILURE(rc))
    {
        RTPrintf("\ntstHandleTable: FAILURE - RTHandleTableCreateEx failed, %Rrc!\n", rc);
        return 1;
    }

    /* fill it */
    RTPrintf("tstHandleTable: TESTING   RTHandleTableAlloc%s..", pszWithCtx);
    RTStrmFlush(g_pStdOut);
    uint32_t i = uBase;
    for (;; i++)
    {
        uint32_t h;
        if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
            rc = RTHandleTableAllocWithCtx(hHT, (void *)((uintptr_t)&i + (uintptr_t)i * 4), NULL, &h);
        else
            rc = RTHandleTableAlloc(hHT, (void *)((uintptr_t)&i + (uintptr_t)i * 4), &h);
        if (RT_SUCCESS(rc))
        {
            if (h != i)
            {
                RTPrintf("\ntstHandleTable: FAILURE (%d) - h=%d, expected %d!\n", __LINE__, h, i);
                g_cErrors++;
            }
        }
        else if (rc == VERR_NO_MORE_HANDLES)
        {
            if (i < cMax)
            {
                RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, expected > 65534!\n", __LINE__, i);
                g_cErrors++;
            }
            break;
        }
        else
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, rc=%Rrc!\n", __LINE__, i, rc);
            g_cErrors++;
        }
        if (!(i % cUnitsPerDot))
        {
            RTPrintf(".");
            RTStrmFlush(g_pStdOut);
        }
    }
    uint32_t const c = i;
    RTPrintf(" c=%#x\n", c);
    if (fCallbacks && cRetainerCalls != 0)
    {
        RTPrintf("tstHandleTable: FAILURE (%d) - cRetainerCalls=%#x expected 0!\n", __LINE__, cRetainerCalls);
        g_cErrors++;
    }

    /* look up all the entries */
    RTPrintf("tstHandleTable: TESTING   RTHandleTableLookup%s..", pszWithCtx);
    RTStrmFlush(g_pStdOut);
    cRetainerCalls = 0;
    for (i = uBase; i < c; i++)
    {
        void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)i * 4);
        void *pvObj;
        if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
            pvObj = RTHandleTableLookupWithCtx(hHT, i, NULL);
        else
            pvObj = RTHandleTableLookup(hHT, i);
        if (!pvObj)
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup%s failed!\n", __LINE__, i, pszWithCtx);
            g_cErrors++;
        }
        else if (pvObj != pvExpect)
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, pvObj=%p expected %p\n", __LINE__, i, pvObj, pvExpect);
            g_cErrors++;
        }
        if (!(i % cUnitsPerDot))
        {
            RTPrintf(".");
            RTStrmFlush(g_pStdOut);
        }
    }
    RTPrintf("\n");
    if (fCallbacks && cRetainerCalls != c - uBase)
    {
        RTPrintf("tstHandleTable: FAILURE (%d) - cRetainerCalls=%#x expected %#x!\n", __LINE__, cRetainerCalls, c - uBase);
        g_cErrors++;
    }

    /* remove all the entries (in order) */
    RTPrintf("tstHandleTable: TESTING   RTHandleTableFree%s..", pszWithCtx);
    RTStrmFlush(g_pStdOut);
    cRetainerCalls = 0;
    for (i = uBase; i < c; i++)
    {
        void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)i * 4);
        void *pvObj;
        if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
            pvObj = RTHandleTableFreeWithCtx(hHT, i, NULL);
        else
            pvObj = RTHandleTableFree(hHT, i);
        if (!pvObj)
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup%s failed!\n", __LINE__, i, pszWithCtx);
            g_cErrors++;
        }
        else if (pvObj != pvExpect)
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, pvObj=%p expected %p\n", __LINE__, i, pvObj, pvExpect);
            g_cErrors++;
        }
        else if (   fFlags & RTHANDLETABLE_FLAGS_CONTEXT
                    ?  RTHandleTableLookupWithCtx(hHT, i, NULL)
                    :  RTHandleTableLookup(hHT, i))
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup%s succeeded after free!\n", __LINE__, i, pszWithCtx);
            g_cErrors++;
        }
        if (!(i % cUnitsPerDot))
        {
            RTPrintf(".");
            RTStrmFlush(g_pStdOut);
        }
    }
    RTPrintf("\n");
    if (fCallbacks && cRetainerCalls != c - uBase)
    {
        RTPrintf("tstHandleTable: FAILURE (%d) - cRetainerCalls=%#x expected %#x!\n", __LINE__, cRetainerCalls, c - uBase);
        g_cErrors++;
    }

    /* do a mix of alloc, lookup and free where there is a constant of cDelta handles in the table. */
    RTPrintf("tstHandleTable: TESTING   Alloc,Lookup,Free mix [cDelta=%#x]..", cDelta);
    RTStrmFlush(g_pStdOut);
    for (i = uBase; i < c * 2; i++)
    {
        /* alloc */
        uint32_t hExpect = ((i - uBase) % (c - uBase)) + uBase;
        uint32_t h;
        if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
            rc = RTHandleTableAllocWithCtx(hHT, (void *)((uintptr_t)&i + (uintptr_t)hExpect * 4), NULL, &h);
        else
            rc = RTHandleTableAlloc(hHT, (void *)((uintptr_t)&i + (uintptr_t)hExpect * 4), &h);
        if (RT_FAILURE(rc))
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableAlloc%s: rc=%Rrc!\n", __LINE__, i, pszWithCtx, rc);
            g_cErrors++;
        }
        else if (h != hExpect)
        {
            RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableAlloc%s: h=%u hExpect=%u! - abort sub-test\n", __LINE__, i, pszWithCtx, h, hExpect);
            g_cErrors++;
            break;
        }

        if (i >= cDelta + uBase)
        {
            /* lookup */
            for (uint32_t j = i - cDelta; j <= i; j++)
            {
                uint32_t hLookup = ((j - uBase) % (c - uBase)) + uBase;
                void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)hLookup * 4);
                void *pvObj;
                if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
                    pvObj = RTHandleTableLookupWithCtx(hHT, hLookup, NULL);
                else
                    pvObj = RTHandleTableLookup(hHT, hLookup);
                if (pvObj != pvExpect)
                {
                    RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, j=%d, RTHandleTableLookup%s(,%u,): pvObj=%p expected %p!\n",
                             __LINE__, i, j, pszWithCtx, hLookup, pvObj, pvExpect);
                    g_cErrors++;
                }
                else if (   (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
                            &&  RTHandleTableLookupWithCtx(hHT, hLookup, &i))
                {
                    RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, j=%d, RTHandleTableLookupWithCtx: succeeded with bad context\n",
                             __LINE__, i, j);
                    g_cErrors++;
                }
            }

            /* free */
            uint32_t hFree = ((i - uBase - cDelta) % (c - uBase)) + uBase;
            void *pvExpect = (void *)((uintptr_t)&i + (uintptr_t)hFree * 4);
            void *pvObj;
            if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT)
                pvObj = RTHandleTableFreeWithCtx(hHT, hFree, NULL);
            else
                pvObj = RTHandleTableFree(hHT, hFree);
            if (pvObj != pvExpect)
            {
                RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableFree%s: pvObj=%p expected %p!\n",
                         __LINE__, i, pszWithCtx, pvObj, pvExpect);
                g_cErrors++;
            }
            else if (fFlags & RTHANDLETABLE_FLAGS_CONTEXT
                     ?      RTHandleTableLookupWithCtx(hHT, hFree, NULL)
                     ||  RTHandleTableFreeWithCtx(hHT, hFree, NULL)
                     :      RTHandleTableLookup(hHT, hFree)
                     ||  RTHandleTableFree(hHT, hFree))
            {
                RTPrintf("\ntstHandleTable: FAILURE (%d) - i=%d, RTHandleTableLookup/Free%s: succeeded after free\n",
                         __LINE__, i, pszWithCtx);
                g_cErrors++;
            }
        }
        if (!(i % (cUnitsPerDot * 2)))
        {
            RTPrintf(".");
            RTStrmFlush(g_pStdOut);
        }
    }
    RTPrintf("\n");

    /* finally, destroy the table (note that there are 128 entries in it). */
    cRetainerCalls = 0;
    uint32_t cDeleteCalls = 0;
    rc = RTHandleTableDestroy(hHT,
                              fCallbacks ? tstHandleTableTest1Delete : NULL,
                              fCallbacks ? &cDeleteCalls : NULL);
    if (RT_FAILURE(rc))
    {
        RTPrintf("tstHandleTable: FAILURE (%d) - RTHandleTableDestroy failed, %Rrc!\n", __LINE__, rc);
        g_cErrors++;
    }

    return 0;
}
Beispiel #6
0
/**
 * Entry point.
 */
extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
{
    RT_NOREF1(envp);

    /*
     * Init runtime and the test environment.
     */
    RTTEST hTest;
    RTEXITCODE rcExit = RTTestInitExAndCreate(argc, &argv, RTR3INIT_FLAGS_SUPLIB, "tstVMM", &hTest);
    if (rcExit != RTEXITCODE_SUCCESS)
        return rcExit;

    /*
     * Parse arguments.
     */
    static const RTGETOPTDEF s_aOptions[] =
    {
        { "--cpus",          'c', RTGETOPT_REQ_UINT8 },
        { "--test",          't', RTGETOPT_REQ_STRING },
        { "--stat",          's', RTGETOPT_REQ_NOTHING },
    };
    enum
    {
        kTstVMMTest_VMM,  kTstVMMTest_TM, kTstVMMTest_MSRs, kTstVMMTest_KnownMSRs, kTstVMMTest_MSRExperiments
    } enmTestOpt = kTstVMMTest_VMM;

    int ch;
    RTGETOPTUNION ValueUnion;
    RTGETOPTSTATE GetState;
    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch (ch)
        {
            case 'c':
                g_cCpus = ValueUnion.u8;
                break;

            case 't':
                if (!strcmp("vmm", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_VMM;
                else if (!strcmp("tm", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_TM;
                else if (!strcmp("msr", ValueUnion.psz) || !strcmp("msrs", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_MSRs;
                else if (!strcmp("known-msr", ValueUnion.psz) || !strcmp("known-msrs", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_KnownMSRs;
                else if (!strcmp("msr-experiments", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_MSRExperiments;
                else
                {
                    RTPrintf("tstVMM: unknown test: '%s'\n", ValueUnion.psz);
                    return 1;
                }
                break;

            case 's':
                g_fStat = true;
                break;

            case 'h':
                RTPrintf("usage: tstVMM [--cpus|-c cpus] [-s] [--test <vmm|tm|msrs|known-msrs>]\n");
                return 1;

            case 'V':
                RTPrintf("$Revision$\n");
                return 0;

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

    /*
     * Create the test VM.
     */
    RTPrintf(TESTCASE ": Initializing...\n");
    PVM pVM;
    PUVM pUVM;
    int rc = VMR3Create(g_cCpus, NULL, NULL, NULL, tstVMMConfigConstructor, NULL, &pVM, &pUVM);
    if (RT_SUCCESS(rc))
    {
        PDMR3LdrEnumModules(pVM, tstVMMLdrEnum, NULL);
        RTStrmFlush(g_pStdOut);
        RTThreadSleep(256);

        /*
         * Do the requested testing.
         */
        switch (enmTestOpt)
        {
            case kTstVMMTest_VMM:
            {
                RTTestSub(hTest, "VMM");
                rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)VMMDoTest, 1, pVM);
                if (RT_FAILURE(rc))
                    RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc);
                if (g_fStat)
                    STAMR3Dump(pUVM, "*");
                break;
            }

            case kTstVMMTest_TM:
            {
                RTTestSub(hTest, "TM");
                for (VMCPUID idCpu = 1; idCpu < g_cCpus; idCpu++)
                {
                    rc = VMR3ReqCallNoWaitU(pUVM, idCpu, (PFNRT)tstTMWorker, 2, pVM, hTest);
                    if (RT_FAILURE(rc))
                        RTTestFailed(hTest, "VMR3ReqCall failed: rc=%Rrc\n", rc);
                }

                rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)tstTMWorker, 2, pVM, hTest);
                if (RT_FAILURE(rc))
                    RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc);
                if (g_fStat)
                    STAMR3Dump(pUVM, "*");
                break;
            }

            case kTstVMMTest_MSRs:
            {
                RTTestSub(hTest, "MSRs");
                if (g_cCpus == 1)
                {
                    rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)VMMDoBruteForceMsrs, 1, pVM);
                    if (RT_FAILURE(rc))
                        RTTestFailed(hTest, "VMMDoBruteForceMsrs failed: rc=%Rrc\n", rc);
                }
                else
                    RTTestFailed(hTest, "The MSR test can only be run with one VCpu!\n");
                break;
            }

            case kTstVMMTest_KnownMSRs:
            {
                RTTestSub(hTest, "Known MSRs");
                if (g_cCpus == 1)
                {
                    rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)VMMDoKnownMsrs, 1, pVM);
                    if (RT_FAILURE(rc))
                        RTTestFailed(hTest, "VMMDoKnownMsrs failed: rc=%Rrc\n", rc);
                }
                else
                    RTTestFailed(hTest, "The MSR test can only be run with one VCpu!\n");
                break;
            }

            case kTstVMMTest_MSRExperiments:
            {
                RTTestSub(hTest, "MSR Experiments");
                if (g_cCpus == 1)
                {
                    rc = VMR3ReqCallWaitU(pUVM, 0 /*idDstCpu*/, (PFNRT)VMMDoMsrExperiments, 1, pVM);
                    if (RT_FAILURE(rc))
                        RTTestFailed(hTest, "VMMDoMsrExperiments failed: rc=%Rrc\n", rc);
                }
                else
                    RTTestFailed(hTest, "The MSR test can only be run with one VCpu!\n");
                break;
            }

        }

        /*
         * Cleanup.
         */
        rc = VMR3PowerOff(pUVM);
        if (RT_FAILURE(rc))
            RTTestFailed(hTest, "VMR3PowerOff failed: rc=%Rrc\n", rc);
        rc = VMR3Destroy(pUVM);
        if (RT_FAILURE(rc))
            RTTestFailed(hTest, "VMR3Destroy failed: rc=%Rrc\n", rc);
        VMR3ReleaseUVM(pUVM);
    }
    else
        RTTestFailed(hTest, "VMR3Create failed: rc=%Rrc\n", rc);

    return RTTestSummaryAndDestroy(hTest);
}
/**
 * Process the testcases found in the filter.
 *
 * @param   pszFilter   The filter (winnt) to pass to RTDirOpenFiltered for
 *                      selecting the testcases.
 * @param   pszDir      The directory we're processing.
 */
static void Process(const char *pszFilter, const char *pszDir)
{
    /*
     * Open and enumerate the directory.
     */
    PRTDIR pDir;
    int rc = RTDirOpenFiltered(&pDir, pszFilter, RTDIRFILTER_WINNT, 0);
    if (RT_SUCCESS(rc))
    {
        for (;;)
        {
            RTDIRENTRY DirEntry;
            rc = RTDirRead(pDir, &DirEntry, NULL);
            if (RT_FAILURE(rc))
            {
                if (rc == VERR_NO_MORE_FILES)
                    rc = VINF_SUCCESS;
                else
                    RTPrintf("tstRunTestcases: reading '%s' -> %Rrc\n", pszFilter, rc);
                break;
            }

            /*
             * Construct the testcase name.
             */
            char *pszTestcase;
            RTStrAPrintf(&pszTestcase, "%s/%s", pszDir, DirEntry.szName);
            if (!pszTestcase)
            {
                RTPrintf("tstRunTestcases: out of memory!\n");
                rc = VERR_NO_MEMORY;
                break;
            }
            if (IsTestcaseIncluded(pszTestcase))
            {
                /*
                 * Execute the testcase.
                 */
                RTPrintf("*** %s: Executing...\n", pszTestcase);  RTStrmFlush(g_pStdOut);
                const char *papszArgs[2];
                papszArgs[0] = pszTestcase;
                papszArgs[1] = NULL;
                RTPROCESS Process;
                rc = RTProcCreate(pszTestcase, papszArgs, RTENV_DEFAULT, 0, &Process);
                if (RT_SUCCESS(rc))
                {
                    /*
                     * Wait for the process and collect it's return code.
                     * If it takes too long, we'll terminate it and continue.
                     */
                    RTTIMESPEC Start;
                    RTTimeNow(&Start);
                    RTPROCSTATUS ProcStatus;
                    for (;;)
                    {
                        rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
                        if (rc != VERR_PROCESS_RUNNING)
                            break;
                        RTTIMESPEC Now;
                        if (RTTimeSpecGetMilli(RTTimeSpecSub(RTTimeNow(&Now), &Start)) > 120*1000 /* 1 min */)
                        {
                            RTPrintf("*** %s: FAILED - timed out. killing it.\n", pszTestcase);
                            RTProcTerminate(Process);
                            RTThreadSleep(100);
                            RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
                            g_cFailures++;
                            break;
                        }
                        RTThreadSleep(100);
                    }

                    /*
                     * Examin the exit status.
                     */
                    if (RT_SUCCESS(rc))
                    {
                        if (    ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
                            &&  ProcStatus.iStatus == 0)
                        {
                            RTPrintf("*** %s: PASSED\n", pszTestcase);
                            g_cPasses++;
                        }
                        else
                        {
                            RTPrintf("*** %s: FAILED\n", pszTestcase);
                            g_cFailures++;
                        }
                    }
                    else if (rc != VERR_PROCESS_RUNNING)
                    {
                        RTPrintf("tstRunTestcases: %s: RTProcWait failed -> %Rrc\n", pszTestcase, rc);
                        g_cFailures++;
                    }
                }
                else
                {
                    RTPrintf("tstRunTestcases: %s: failed to start -> %Rrc\n", pszTestcase, rc);
                    g_cFailures++;
                }

            }
            else
            {
                RTPrintf("tstRunTestcases: %s: SKIPPED\n", pszTestcase);
                g_cSkipped++;
            }
            RTStrFree(pszTestcase);
        } /* enumeration loop */

        RTDirClose(pDir);
    }
    else
        RTPrintf("tstRunTestcases: opening '%s' -> %Rrc\n", pszDir, rc);
}
Beispiel #8
0
/**
 *  Entry point.
 */
extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
{
    RT_NOREF1(envp);
    RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
    RTPrintf(TESTCASE ": TESTING...\n");
    RTStrmFlush(g_pStdOut);

    /*
     * Create empty VM.
     */
    PUVM pUVM;
    int rc = VMR3Create(1, NULL, NULL, NULL, tstVMREQConfigConstructor, NULL, NULL, &pUVM);
    if (RT_SUCCESS(rc))
    {
        /*
         * Do testing.
         */
        uint64_t u64StartTS = RTTimeNanoTS();
        RTTHREAD Thread0;
        rc = RTThreadCreate(&Thread0, Thread, pUVM, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "REQ1");
        if (RT_SUCCESS(rc))
        {
            RTTHREAD Thread1;
            rc = RTThreadCreate(&Thread1, Thread, pUVM, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "REQ1");
            if (RT_SUCCESS(rc))
            {
                int rcThread1;
                rc = RTThreadWait(Thread1, RT_INDEFINITE_WAIT, &rcThread1);
                if (RT_FAILURE(rc))
                {
                    RTPrintf(TESTCASE ": RTThreadWait(Thread1,,) failed, rc=%Rrc\n", rc);
                    g_cErrors++;
                }
                if (RT_FAILURE(rcThread1))
                    g_cErrors++;
            }
            else
            {
                RTPrintf(TESTCASE ": RTThreadCreate(&Thread1,,,,) failed, rc=%Rrc\n", rc);
                g_cErrors++;
            }

            int rcThread0;
            rc = RTThreadWait(Thread0, RT_INDEFINITE_WAIT, &rcThread0);
            if (RT_FAILURE(rc))
            {
                RTPrintf(TESTCASE ": RTThreadWait(Thread1,,) failed, rc=%Rrc\n", rc);
                g_cErrors++;
            }
            if (RT_FAILURE(rcThread0))
                g_cErrors++;
        }
        else
        {
            RTPrintf(TESTCASE ": RTThreadCreate(&Thread0,,,,) failed, rc=%Rrc\n", rc);
            g_cErrors++;
        }
        uint64_t u64ElapsedTS = RTTimeNanoTS() - u64StartTS;
        RTPrintf(TESTCASE  ": %llu ns elapsed\n", u64ElapsedTS);
        RTStrmFlush(g_pStdOut);

        /*
         * Print stats.
         */
        STAMR3Print(pUVM, "/VM/Req/*");

        /*
         * Testing va_list fun.
         */
        RTPrintf(TESTCASE ": va_list argument test...\n"); RTStrmFlush(g_pStdOut);
        PassVA(pUVM, "hello %s", "world");
        VMR3AtRuntimeErrorRegister(pUVM, MyAtRuntimeError, (void *)"user argument");
        VMSetRuntimeError(VMR3GetVM(pUVM), 0 /*fFlags*/, "enum", "some %s string", "error");

        /*
         * Cleanup.
         */
        rc = VMR3PowerOff(pUVM);
        if (!RT_SUCCESS(rc))
        {
            RTPrintf(TESTCASE ": error: failed to power off vm! rc=%Rrc\n", rc);
            g_cErrors++;
        }
        rc = VMR3Destroy(pUVM);
        if (!RT_SUCCESS(rc))
        {
            RTPrintf(TESTCASE ": error: failed to destroy vm! rc=%Rrc\n", rc);
            g_cErrors++;
        }
        VMR3ReleaseUVM(pUVM);
    }
    else
    {
        RTPrintf(TESTCASE ": fatal error: failed to create vm! rc=%Rrc\n", rc);
        g_cErrors++;
    }

    /*
     * Summary and return.
     */
    if (!g_cErrors)
        RTPrintf(TESTCASE ": SUCCESS\n");
    else
        RTPrintf(TESTCASE ": FAILURE - %d errors\n", g_cErrors);

    return !!g_cErrors;
}
Beispiel #9
0
int main(int argc, char **argv)
{
    int rc = RTR3InitExe(argc, &argv, 0);
    AssertReleaseRCReturn(rc, 1);

    /*
     * Warmup tests.
     */
    RTPrintf("tstDarwinKeyboard: Warmup...\n");

    RTTimeNanoTS();
    DarwinGrabKeyboard(true);
    DarwinReleaseKeyboard();

    RTTimeNanoTS();
    DarwinGrabKeyboard(true);
    DarwinReleaseKeyboard();

/* Test these too:
unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode);
UInt32   DarwinAdjustModifierMask(UInt32 fModifiers);
unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers);
unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers);
UInt32   DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode);
unsigned DarwinEventToSet1Scancode(EventRef Event, UInt32 *pfCurKeyModifiers);
void     DarwinDisableGlobalHotKeys(bool fDisable);
*/

    /*
     * Grab and release the keyboard a lot of times and time it.
     * We're looking both at performance and for memory and reference leaks here.
     */
    RTPrintf("tstDarwinKeyboard: Profiling Grab and Release");
    RTStrmFlush(g_pStdOut);
    const uint64_t u64Start = RTTimeNanoTS();
    uint64_t u64Grab = 0;
    uint64_t u64Release = 0;
    unsigned i;
    for (i = 0; i < 20; i++)
    {
        uint64_t u64 = RTTimeNanoTS();
        DarwinGrabKeyboard(argc != 1);
        u64Grab += RTTimeNanoTS() - u64;

        u64 = RTTimeNanoTS();
        DarwinReleaseKeyboard();
        u64Release += RTTimeNanoTS() - u64;

        if ((i % 10) == 0)
        {
            RTPrintf(".");
            RTStrmFlush(g_pStdOut);
        }
    }
    const uint64_t u64Elapsed = RTTimeNanoTS() - u64Start;
    RTPrintf("\n"
             "tstDarwinKeyboard: %u times in %RU64 ms - %RU64 ms per call\n",
             i, u64Elapsed / 1000000, (u64Elapsed / i) / 1000000);
    RTPrintf("tstDarwinKeyboard: DarwinGrabKeyboard: %RU64 ms total - %RU64 ms per call\n",
             u64Grab / 1000000, (u64Grab / i) / 1000000);
    RTPrintf("tstDarwinKeyboard: DarwinReleaseKeyboard: %RU64 ms total - %RU64 ms per call\n",
             u64Release / 1000000, (u64Release / i) / 1000000);

    return 0;
}
int main(int argc, char **argv)
{
    /*
     * Init runtime and the test environment.
     */
    int rc = RTR3InitAndSUPLib();
    if (RT_FAILURE(rc))
    {
        RTPrintf("tstVMM: RTR3InitAndSUPLib failed: %Rrc\n", rc);
        return 1;
    }
    RTTEST hTest;
    rc = RTTestCreate("tstVMM", &hTest);
    if (RT_FAILURE(rc))
    {
        RTPrintf("tstVMM: RTTestCreate failed: %Rrc\n", rc);
        return 1;
    }

    /*
     * Parse arguments.
     */
    static const RTGETOPTDEF s_aOptions[] =
    {
        { "--cpus",          'c', RTGETOPT_REQ_UINT8 },
        { "--test",          't', RTGETOPT_REQ_STRING },
    };
    enum
    {
        kTstVMMTest_VMM,  kTstVMMTest_TM
    } enmTestOpt = kTstVMMTest_VMM;

    int ch;
    int i = 1;
    RTGETOPTUNION ValueUnion;
    RTGETOPTSTATE GetState;
    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch (ch)
        {
            case 'c':
                g_cCpus = ValueUnion.u8;
                break;

            case 't':
                if (!strcmp("vmm", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_VMM;
                else if (!strcmp("tm", ValueUnion.psz))
                    enmTestOpt = kTstVMMTest_TM;
                else
                {
                    RTPrintf("tstVMM: unknown test: '%s'\n", ValueUnion.psz);
                    return 1;
                }
                break;

            case 'h':
                RTPrintf("usage: tstVMM [--cpus|-c cpus] [--test <vmm|tm>]\n");
                return 1;

            case 'V':
                RTPrintf("$Revision: $\n");
                return 0;

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

    /*
     * Create the test VM.
     */
    RTPrintf(TESTCASE ": Initializing...\n");
    PVM pVM;
    rc = VMR3Create(g_cCpus, NULL, NULL, NULL, tstVMMConfigConstructor, NULL, &pVM);
    if (RT_SUCCESS(rc))
    {
        PDMR3LdrEnumModules(pVM, tstVMMLdrEnum, NULL);
        RTStrmFlush(g_pStdOut);
        RTThreadSleep(256);

        /*
         * Do the requested testing.
         */
        switch (enmTestOpt)
        {
            case kTstVMMTest_VMM:
            {
                RTTestSub(hTest, "VMM");
                rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)VMMDoTest, 1, pVM);
                if (RT_FAILURE(rc))
                    RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc);
                break;
            }

            case kTstVMMTest_TM:
            {
                RTTestSub(hTest, "TM");
                for (VMCPUID idCpu = 1; idCpu < g_cCpus; idCpu++)
                {
                    rc = VMR3ReqCallNoWait(pVM, idCpu, (PFNRT)tstTMWorker, 2, pVM, hTest);
                    if (RT_FAILURE(rc))
                        RTTestFailed(hTest, "VMR3ReqCall failed: rc=%Rrc\n", rc);
                }

                rc = VMR3ReqCallWait(pVM, 0 /*idDstCpu*/, (PFNRT)tstTMWorker, 2, pVM, hTest);
                if (RT_FAILURE(rc))
                    RTTestFailed(hTest, "VMMDoTest failed: rc=%Rrc\n", rc);
                break;
            }
        }

        STAMR3Dump(pVM, "*");

        /*
         * Cleanup.
         */
        rc = VMR3PowerOff(pVM);
        if (RT_FAILURE(rc))
            RTTestFailed(hTest, "VMR3PowerOff failed: rc=%Rrc\n", rc);
        rc = VMR3Destroy(pVM);
        if (RT_FAILURE(rc))
            RTTestFailed(hTest, "VMR3Destroy failed: rc=%Rrc\n", rc);
    }
    else
        RTTestFailed(hTest, "VMR3Create failed: rc=%Rrc\n", rc);

    return RTTestSummaryAndDestroy(hTest);
}
Beispiel #11
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;
}
int main(int argc, char **argv)
{
    RTR3InitExe(argc, &argv, 0);

    /*
     * Parse arguments.
     */
    static const RTGETOPTDEF    s_aOptions[] =
    {
        { "--iterations",     'i', RTGETOPT_REQ_UINT32 },
        { "--num-pages",      'n', RTGETOPT_REQ_UINT32 },
        { "--page-at-a-time", 'c', RTGETOPT_REQ_UINT32 },
        { "--page-file",      'f', RTGETOPT_REQ_STRING },
        { "--offset",         'o', RTGETOPT_REQ_UINT64 },
    };

    const char     *pszPageFile = NULL;
    uint64_t        offPageFile = 0;
    uint32_t        cIterations = 1;
    uint32_t        cPagesAtATime = 1;
    RTGETOPTUNION   Val;
    RTGETOPTSTATE   State;
    int rc = RTGetOptInit(&State, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0);
    AssertRCReturn(rc, 1);

    while ((rc = RTGetOpt(&State, &Val)))
    {
        switch (rc)
        {
            case 'n':
                g_cPages = Val.u32;
                if (g_cPages * PAGE_SIZE * 4 / (PAGE_SIZE * 4) != g_cPages)
                    return Error("The specified page count is too high: %#x (%#llx bytes)\n", g_cPages, (uint64_t)g_cPages * PAGE_SHIFT);
                if (g_cPages < 1)
                    return Error("The specified page count is too low: %#x\n", g_cPages);
                break;

            case 'i':
                cIterations = Val.u32;
                if (cIterations < 1)
                    return Error("The number of iterations must be 1 or higher\n");
                break;

            case 'c':
                cPagesAtATime = Val.u32;
                if (cPagesAtATime < 1 || cPagesAtATime > 10240)
                    return Error("The specified pages-at-a-time count is out of range: %#x\n", cPagesAtATime);
                break;

            case 'f':
                pszPageFile = Val.psz;
                break;

            case 'o':
                offPageFile = Val.u64;
                break;

            case 'O':
                offPageFile = Val.u64 * PAGE_SIZE;
                break;

            case 'h':
                RTPrintf("syntax: tstCompressionBenchmark [options]\n"
                         "\n"
                         "Options:\n"
                         "  -h, --help\n"
                         "    Show this help page\n"
                         "  -i, --iterations <num>\n"
                         "    The number of iterations.\n"
                         "  -n, --num-pages <pages>\n"
                         "    The number of pages.\n"
                         "  -c, --pages-at-a-time <pages>\n"
                         "    Number of pages at a time.\n"
                         "  -f, --page-file <filename>\n"
                         "    File or device to read the page from. The default\n"
                         "    is to generate some garbage.\n"
                         "  -o, --offset <file-offset>\n"
                         "    Offset into the page file to start reading at.\n");
                return 0;

            case 'V':
                RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
                return 0;

            default:
                return RTGetOptPrintError(rc, &Val);
        }
    }

    g_cbPages = g_cPages * PAGE_SIZE;
    uint64_t cbTotal = (uint64_t)g_cPages * PAGE_SIZE * cIterations;
    uint64_t cbTotalKB = cbTotal / _1K;
    if (cbTotal / cIterations != g_cbPages)
        return Error("cPages * cIterations -> overflow\n");

    /*
     * Gather the test memory.
     */
    if (pszPageFile)
    {
        size_t cbFile;
        rc = RTFileReadAllEx(pszPageFile, offPageFile, g_cbPages, RTFILE_RDALL_O_DENY_NONE, (void **)&g_pabSrc, &cbFile);
        if (RT_FAILURE(rc))
            return Error("Error reading %zu bytes from %s at %llu: %Rrc\n", g_cbPages, pszPageFile, offPageFile, rc);
        if (cbFile != g_cbPages)
            return Error("Error reading %zu bytes from %s at %llu: got %zu bytes\n", g_cbPages, pszPageFile, offPageFile, cbFile);
    }
    else
    {
        g_pabSrc = (uint8_t *)RTMemAlloc(g_cbPages);
        if (g_pabSrc)
        {
            /* Just fill it with something - warn about the low quality of the something. */
            RTPrintf("tstCompressionBenchmark: WARNING! No input file was specified so the source\n"
                     "buffer will be filled with generated data of questionable quality.\n");
#ifdef RT_OS_LINUX
            RTPrintf("To get real RAM on linux: sudo dd if=/dev/mem ... \n");
#endif
            uint8_t *pb    = g_pabSrc;
            uint8_t *pbEnd = &g_pabSrc[g_cbPages];
            for (; pb != pbEnd; pb += 16)
            {
                char szTmp[17];
                RTStrPrintf(szTmp, sizeof(szTmp), "aaaa%08Xzzzz", (uint32_t)(uintptr_t)pb);
                memcpy(pb, szTmp, 16);
            }
        }
    }

    g_pabDecompr = (uint8_t *)RTMemAlloc(g_cbPages);
    g_cbComprAlloc = RT_MAX(g_cbPages * 2, 256 * PAGE_SIZE);
    g_pabCompr   = (uint8_t *)RTMemAlloc(g_cbComprAlloc);
    if (!g_pabSrc || !g_pabDecompr || !g_pabCompr)
        return Error("failed to allocate memory buffers (g_cPages=%#x)\n", g_cPages);

    /*
     * Double loop compressing and uncompressing the data, where the outer does
     * the specified number of iterations while the inner applies the different
     * compression algorithms.
     */
    struct
    {
        /** The time spent decompressing. */
        uint64_t    cNanoDecompr;
        /** The time spent compressing. */
        uint64_t    cNanoCompr;
        /** The size of the compressed data. */
        uint64_t    cbCompr;
        /** First error. */
        int         rc;
        /** The compression style: block or stream. */
        bool        fBlock;
        /** Compression type.  */
        RTZIPTYPE   enmType;
        /** Compression level.  */
        RTZIPLEVEL  enmLevel;
        /** Method name. */
        const char *pszName;
    } aTests[] =
    {
        { 0, 0, 0, VINF_SUCCESS, false, RTZIPTYPE_STORE, RTZIPLEVEL_DEFAULT, "RTZip/Store"      },
        { 0, 0, 0, VINF_SUCCESS, false, RTZIPTYPE_LZF,   RTZIPLEVEL_DEFAULT, "RTZip/LZF"        },
/*      { 0, 0, 0, VINF_SUCCESS, false, RTZIPTYPE_ZLIB,  RTZIPLEVEL_DEFAULT, "RTZip/zlib"       }, - slow plus it randomly hits VERR_GENERAL_FAILURE atm. */
        { 0, 0, 0, VINF_SUCCESS, true,  RTZIPTYPE_STORE, RTZIPLEVEL_DEFAULT, "RTZipBlock/Store" },
        { 0, 0, 0, VINF_SUCCESS, true,  RTZIPTYPE_LZF,   RTZIPLEVEL_DEFAULT, "RTZipBlock/LZF"   },
        { 0, 0, 0, VINF_SUCCESS, true,  RTZIPTYPE_LZJB,  RTZIPLEVEL_DEFAULT, "RTZipBlock/LZJB"  },
        { 0, 0, 0, VINF_SUCCESS, true,  RTZIPTYPE_LZO,   RTZIPLEVEL_DEFAULT, "RTZipBlock/LZO"   },
    };
    RTPrintf("tstCompressionBenchmark: TESTING..");
    for (uint32_t i = 0; i < cIterations; i++)
    {
        for (uint32_t j = 0; j < RT_ELEMENTS(aTests); j++)
        {
            if (RT_FAILURE(aTests[j].rc))
                continue;
            memset(g_pabCompr,   0xaa, g_cbComprAlloc);
            memset(g_pabDecompr, 0xcc, g_cbPages);
            g_cbCompr = 0;
            g_offComprIn = 0;
            RTPrintf("."); RTStrmFlush(g_pStdOut);

            /*
             * Compress it.
             */
            uint64_t NanoTS = RTTimeNanoTS();
            if (aTests[j].fBlock)
            {
                size_t          cbLeft    = g_cbComprAlloc;
                uint8_t const  *pbSrcPage = g_pabSrc;
                uint8_t        *pbDstPage = g_pabCompr;
                for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime)
                {
                    AssertBreakStmt(cbLeft > PAGE_SIZE * 4, aTests[j].rc = rc = VERR_BUFFER_OVERFLOW);
                    uint32_t *pcb = (uint32_t *)pbDstPage;
                    pbDstPage    += sizeof(uint32_t);
                    cbLeft       -= sizeof(uint32_t);
                    size_t  cbSrc = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE;
                    size_t  cbDst;
                    rc = RTZipBlockCompress(aTests[j].enmType, aTests[j].enmLevel, 0 /*fFlags*/,
                                            pbSrcPage, cbSrc,
                                            pbDstPage, cbLeft, &cbDst);
                    if (RT_FAILURE(rc))
                    {
                        Error("RTZipBlockCompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                        aTests[j].rc = rc;
                        break;
                    }
                    *pcb       = (uint32_t)cbDst;
                    cbLeft    -= cbDst;
                    pbDstPage += cbDst;
                    pbSrcPage += cbSrc;
                }
                if (RT_FAILURE(rc))
                    continue;
                g_cbCompr = pbDstPage - g_pabCompr;
            }
            else
            {
                PRTZIPCOMP pZipComp;
                rc = RTZipCompCreate(&pZipComp, NULL, ComprOutCallback, aTests[j].enmType, aTests[j].enmLevel);
                if (RT_FAILURE(rc))
                {
                    Error("Failed to create the compressor for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                    aTests[j].rc = rc;
                    continue;
                }

                uint8_t const  *pbSrcPage = g_pabSrc;
                for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime)
                {
                    size_t cb = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE;
                    rc = RTZipCompress(pZipComp, pbSrcPage, cb);
                    if (RT_FAILURE(rc))
                    {
                        Error("RTZipCompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                        aTests[j].rc = rc;
                        break;
                    }
                    pbSrcPage += cb;
                }
                if (RT_FAILURE(rc))
                    continue;
                rc = RTZipCompFinish(pZipComp);
                if (RT_FAILURE(rc))
                {
                    Error("RTZipCompFinish failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                    aTests[j].rc = rc;
                    break;
                }
                RTZipCompDestroy(pZipComp);
            }
            NanoTS = RTTimeNanoTS() - NanoTS;
            aTests[j].cbCompr    += g_cbCompr;
            aTests[j].cNanoCompr += NanoTS;

            /*
             * Decompress it.
             */
            NanoTS = RTTimeNanoTS();
            if (aTests[j].fBlock)
            {
                uint8_t const  *pbSrcPage = g_pabCompr;
                size_t          cbLeft    = g_cbCompr;
                uint8_t        *pbDstPage = g_pabDecompr;
                for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime)
                {
                    size_t   cbDst = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE;
                    size_t   cbSrc = *(uint32_t *)pbSrcPage;
                    pbSrcPage     += sizeof(uint32_t);
                    cbLeft        -= sizeof(uint32_t);
                    rc = RTZipBlockDecompress(aTests[j].enmType, 0 /*fFlags*/,
                                              pbSrcPage, cbSrc, &cbSrc,
                                              pbDstPage, cbDst, &cbDst);
                    if (RT_FAILURE(rc))
                    {
                        Error("RTZipBlockDecompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                        aTests[j].rc = rc;
                        break;
                    }
                    pbDstPage += cbDst;
                    cbLeft    -= cbSrc;
                    pbSrcPage += cbSrc;
                }
                if (RT_FAILURE(rc))
                    continue;
            }
            else
            {
                PRTZIPDECOMP pZipDecomp;
                rc = RTZipDecompCreate(&pZipDecomp, NULL, DecomprInCallback);
                if (RT_FAILURE(rc))
                {
                    Error("Failed to create the decompressor for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                    aTests[j].rc = rc;
                    continue;
                }

                uint8_t *pbDstPage = g_pabDecompr;
                for (size_t iPage = 0; iPage < g_cPages; iPage += cPagesAtATime)
                {
                    size_t cb = RT_MIN(g_cPages - iPage, cPagesAtATime) * PAGE_SIZE;
                    rc = RTZipDecompress(pZipDecomp, pbDstPage, cb, NULL);
                    if (RT_FAILURE(rc))
                    {
                        Error("RTZipDecompress failed for '%s' (#%u): %Rrc\n", aTests[j].pszName, j, rc);
                        aTests[j].rc = rc;
                        break;
                    }
                    pbDstPage += cb;
                }
                RTZipDecompDestroy(pZipDecomp);
                if (RT_FAILURE(rc))
                    continue;
            }
            NanoTS = RTTimeNanoTS() - NanoTS;
            aTests[j].cNanoDecompr += NanoTS;

            if (memcmp(g_pabDecompr, g_pabSrc, g_cbPages))
            {
                Error("The compressed data doesn't match the source for '%s' (%#u)\n", aTests[j].pszName, j);
                aTests[j].rc = VERR_BAD_EXE_FORMAT;
                continue;
            }
        }
    }
    if (RT_SUCCESS(rc))
        RTPrintf("\n");

    /*
     * Report the results.
     */
    rc = 0;
    RTPrintf("tstCompressionBenchmark: BEGIN RESULTS\n");
    RTPrintf("%-20s           Compression                                             Decompression\n", "");
    RTPrintf("%-20s        In             Out      Ratio         Size                In             Out\n", "Method");
    RTPrintf("%.20s-----------------------------------------------------------------------------------------\n", "---------------------------------------------");
    for (uint32_t j = 0; j < RT_ELEMENTS(aTests); j++)
    {
        if (RT_SUCCESS(aTests[j].rc))
        {
            unsigned uComprSpeedIn    = (unsigned)(cbTotalKB         / (long double)aTests[j].cNanoCompr   * 1000000000.0);
            unsigned uComprSpeedOut   = (unsigned)(aTests[j].cbCompr / (long double)aTests[j].cNanoCompr   * 1000000000.0 / 1024);
            unsigned uRatio           = (unsigned)(aTests[j].cbCompr / cIterations * 100 / g_cbPages);
            unsigned uDecomprSpeedIn  = (unsigned)(aTests[j].cbCompr / (long double)aTests[j].cNanoDecompr * 1000000000.0 / 1024);
            unsigned uDecomprSpeedOut = (unsigned)(cbTotalKB         / (long double)aTests[j].cNanoDecompr * 1000000000.0);
            RTPrintf("%-20s %'9u KB/s  %'9u KB/s  %3u%%  %'11llu bytes   %'9u KB/s  %'9u KB/s",
                     aTests[j].pszName,
                     uComprSpeedIn,   uComprSpeedOut, uRatio, aTests[j].cbCompr / cIterations,
                     uDecomprSpeedIn, uDecomprSpeedOut);
#if 0
            RTPrintf("  [%'14llu / %'14llu ns]\n",
                     aTests[j].cNanoCompr / cIterations,
                     aTests[j].cNanoDecompr / cIterations);
#else
            RTPrintf("\n");
#endif
        }
        else
        {
            RTPrintf("%-20s: %Rrc\n", aTests[j].pszName, aTests[j].rc);
            rc = 1;
        }
    }
    if (pszPageFile)
        RTPrintf("Input: %'10zu pages from '%s' starting at offset %'lld (%#llx)\n"
                 "                                                           %'11zu bytes\n",
                 g_cPages, pszPageFile, offPageFile, offPageFile, g_cbPages);
    else
        RTPrintf("Input: %'10zu pages of generated rubbish               %'11zu bytes\n",
                 g_cPages, g_cbPages);

    /*
     * Count zero pages in the data set.
     */
    size_t cZeroPages = 0;
    for (size_t iPage = 0; iPage < g_cPages; iPage++)
    {
        if (!ASMMemIsAllU32(&g_pabSrc[iPage * PAGE_SIZE], PAGE_SIZE, 0))
            cZeroPages++;
    }
    RTPrintf("       %'10zu zero pages (%u %%)\n", cZeroPages, cZeroPages * 100 / g_cPages);

    /*
     * A little extension to the test, benchmark relevant CRCs.
     */
    RTPrintf("\n"
             "tstCompressionBenchmark: Hash/CRC - All In One\n");
    tstBenchmarkCRCsAllInOne(g_pabSrc, g_cbPages);

    RTPrintf("\n"
             "tstCompressionBenchmark: Hash/CRC - Page by Page\n");
    tstBenchmarkCRCsPageByPage(g_pabSrc, g_cbPages);

    RTPrintf("\n"
             "tstCompressionBenchmark: Hash/CRC - Zero Page Digest\n");
    static uint8_t s_abZeroPg[PAGE_SIZE];
    RT_ZERO(s_abZeroPg);
    tstBenchmarkCRCsAllInOne(s_abZeroPg, PAGE_SIZE);

    RTPrintf("\n"
             "tstCompressionBenchmark: Hash/CRC - Zero Half Page Digest\n");
    tstBenchmarkCRCsAllInOne(s_abZeroPg, PAGE_SIZE / 2);

    RTPrintf("tstCompressionBenchmark: END RESULTS\n");

    return rc;
}
RTDECL(int) RTCrStoreCertExportAsPem(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename)
{
    /*
     * Validate input.
     */
    AssertReturn(!fFlags, VERR_INVALID_FLAGS);

    /*
     * Start the enumeration first as this validates the store handle.
     */
    RTCRSTORECERTSEARCH Search;
    int rc = RTCrStoreCertFindAll(hStore, &Search);
    if (RT_SUCCESS(rc))
    {
        /*
         * Open the file for writing.
         *
         * Note! We must use text and no binary here, because the base-64 API
         *       below will use host specific EOL markers, not CRLF as PEM
         *       specifies.
         */
        PRTSTREAM hStrm;
        rc = RTStrmOpen(pszFilename, "w", &hStrm);
        if (RT_SUCCESS(rc))
        {
            /*
             * Enumerate the certificates in the store, writing them out one by one.
             */
            size_t          cbBase64  = 0;
            char           *pszBase64 = NULL;
            PCRTCRCERTCTX   pCertCtx;
            while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
            {
                const char *pszMarker;
                switch (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK)
                {
                    case RTCRCERTCTX_F_ENC_X509_DER:    pszMarker = "CERTIFICATE";  break;
                    case RTCRCERTCTX_F_ENC_TAF_DER:     pszMarker = "TRUST ANCHOR"; break;
                    default:                            pszMarker = NULL;           break;
                }
                if (pszMarker && pCertCtx->cbEncoded > 0)
                {
                    /*
                     * Do the base64 conversion first.
                     */
                    size_t cchEncoded = RTBase64EncodedLength(pCertCtx->cbEncoded);
                    if (cchEncoded < cbBase64)
                    { /* likely */ }
                    else
                    {
                        size_t cbNew = RT_ALIGN(cchEncoded + 64, 128);
                        void *pvNew = RTMemRealloc(pszBase64, cbNew);
                        if (!pvNew)
                        {
                            rc = VERR_NO_MEMORY;
                            break;
                        }
                        cbBase64  = cbNew;
                        pszBase64 = (char *)pvNew;
                    }
                    rc = RTBase64Encode(pCertCtx->pabEncoded, pCertCtx->cbEncoded, pszBase64, cbBase64, &cchEncoded);
                    if (RT_FAILURE(rc))
                        break;

                    RTStrmPrintf(hStrm, "-----BEGIN %s-----\n", pszMarker);
                    RTStrmWrite(hStrm, pszBase64, cchEncoded);
                    rc = RTStrmPrintf(hStrm, "\n-----END %s-----\n", pszMarker);
                    if (RT_FAILURE(rc))
                        break;
                }

                RTCrCertCtxRelease(pCertCtx);
            }
            if (pCertCtx)
                RTCrCertCtxRelease(pCertCtx);
            RTMemFree(pszBase64);

            /*
             * Flush the output file before closing.
             */
            int rc2 = RTStrmFlush(hStrm);
            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
                rc = rc2;
            RTStrmClearError(hStrm); /** @todo fix RTStrmClose... */
            rc2 = RTStrmClose(hStrm);
            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
                rc = rc2;
        }

        int rc2 = RTCrStoreCertSearchDestroy(hStore, &Search); AssertRC(rc2);
    }
    return rc;
}
RTDECL(RTEXITCODE) RTPathRmCmd(unsigned cArgs, char **papszArgs)
{
    /*
     * Parse the command line.
     */
    static const RTGETOPTDEF s_aOptions[] =
    {
        /* operations */
        { "--dirs-and-more",        'd', RTGETOPT_REQ_NOTHING },
        { "--force",                'f', RTGETOPT_REQ_NOTHING },
        { "--prompt",               'i', RTGETOPT_REQ_NOTHING },
        { "--prompt-once",          'I', RTGETOPT_REQ_NOTHING },
        { "--interactive",      RTPATHRMCMD_OPT_INTERACTIVE, RTGETOPT_REQ_STRING },
        { "--one-file-system",  RTPATHRMCMD_OPT_ONE_FILE_SYSTEM, RTGETOPT_REQ_NOTHING },
        { "--preserve-root",    RTPATHRMCMD_OPT_PRESERVE_ROOT, RTGETOPT_REQ_NOTHING },
        { "--no-preserve-root", RTPATHRMCMD_OPT_NO_PRESERVE_ROOT, RTGETOPT_REQ_NOTHING },
        { "--recursive",            'R', RTGETOPT_REQ_NOTHING },
        { "--recursive",            'r', RTGETOPT_REQ_NOTHING },
        { "--safe-delete",          'P', RTGETOPT_REQ_NOTHING },
        { "--verbose",              'v', RTGETOPT_REQ_NOTHING },

        /* IPRT extensions */
        { "--machine-readable", RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
        { "--machinereadable",  RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING }, /* bad long option style */
    };

    RTGETOPTSTATE GetState;
    int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
                          RTGETOPTINIT_FLAGS_OPTS_FIRST);
    if (RT_FAILURE(rc))
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);

    RTPATHRMCMDOPTS Opts;
    RT_ZERO(Opts);
    Opts.fPreserveRoot  = true;
    Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_NONE;

    RTGETOPTUNION   ValueUnion;
    while (   (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
           && rc != VINF_GETOPT_NOT_OPTION)
    {
        switch (rc)
        {
            case 'd':
                Opts.fDirsAndOther = true;
                break;

            case 'f':
                Opts.fForce = true;
                Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_NONE;
                break;

            case 'i':
                Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ALL;
                break;

            case 'I':
                Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ONCE;
                break;

            case RTPATHRMCMD_OPT_INTERACTIVE:
                if (!strcmp(ValueUnion.psz, "always"))
                    Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ALL;
                else if (!strcmp(ValueUnion.psz, "once"))
                    Opts.enmInteractive = RTPATHRMCMDINTERACTIVE_ONCE;
                else
                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown --interactive option value: '%s'\n", ValueUnion.psz);
                break;

            case RTPATHRMCMD_OPT_ONE_FILE_SYSTEM:
                Opts.fOneFileSystem = true;
                break;

            case RTPATHRMCMD_OPT_PRESERVE_ROOT:
                Opts.fPreserveRoot = true;
                break;

            case RTPATHRMCMD_OPT_NO_PRESERVE_ROOT:
                Opts.fPreserveRoot = false;
                break;

            case 'R':
            case 'r':
                Opts.fRecursive = true;
                Opts.fDirsAndOther = true;
                break;

            case 'P':
                Opts.fSafeDelete = true;
                break;

            case 'v':
                Opts.fVerbose = true;
                break;


            case RTPATHRMCMD_OPT_MACHINE_READABLE:
                Opts.fMachineReadable = true;
                break;

            case 'h':
                RTPrintf("Usage: to be written\nOption dump:\n");
                for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
                    if (RT_C_IS_PRINT(s_aOptions[i].iShort))
                        RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
                    else
                        RTPrintf(" %s\n", s_aOptions[i].pszLong);
                return RTEXITCODE_SUCCESS;

            case 'V':
                RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
                return RTEXITCODE_SUCCESS;

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

    /*
     * Options we don't support.
     */
    if (Opts.fOneFileSystem)
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --one-file-system option is not yet implemented.\n");
    if (Opts.enmInteractive != RTPATHRMCMDINTERACTIVE_NONE)
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "The -i, -I and --interactive options are not implemented yet.\n");

    /*
     * No files means error.
     */
    if (rc != VINF_GETOPT_NOT_OPTION && !Opts.fForce)
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "No files or directories specified.\n");

    /*
     * Machine readable init + header.
     */
    if (Opts.fMachineReadable)
    {
        rc = RTStrmSetMode(g_pStdOut, true /*fBinary*/, false /*fCurrentCodeSet*/);
        if (RT_FAILURE(rc))
            return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmSetMode failed: %Rrc.\n", rc);
        static const char s_achHeader[] = "hdr_id=rm\0hdr_ver=1";
        RTStrmWrite(g_pStdOut, s_achHeader, sizeof(s_achHeader));
    }

    /*
     * Delete the specified files/dirs/whatever.
     */
    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
    while (rc == VINF_GETOPT_NOT_OPTION)
    {
        rc = rtPathRmOne(&Opts, ValueUnion.psz);
        if (RT_FAILURE(rc))
            rcExit = RTEXITCODE_FAILURE;

        /* next */
        rc = RTGetOpt(&GetState, &ValueUnion);
    }
    if (rc != 0)
        rcExit = RTGetOptPrintError(rc, &ValueUnion);

    /*
     * Terminate the machine readable stuff.
     */
    if (Opts.fMachineReadable)
    {
        RTStrmWrite(g_pStdOut, "\0\0\0", 4);
        rc = RTStrmFlush(g_pStdOut);
        if (RT_FAILURE(rc) && rcExit == RTEXITCODE_SUCCESS)
            rcExit = RTEXITCODE_FAILURE;
    }

    return rcExit;
}