예제 #1
0
/**
 * Attach command.
 *
 * This is called to let the device attach to a driver for a
 * specified LUN.
 *
 * This is like plugging in the mouse after turning on the
 * system.
 *
 * @returns VBox status code.
 * @param   pThis       The PS/2 auxiliary device instance data.
 * @param   pDevIns     The device instance.
 * @param   iLUN        The logical unit which is being detached.
 * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
 */
int PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
    int         rc;

    /* The LUN must be 1, i.e. mouse. */
    Assert(iLUN == 1);
    AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
                    ("PS/2 mouse does not support hotplugging\n"),
                    VERR_INVALID_PARAMETER);

    LogFlowFunc(("iLUN=%d\n", iLUN));

    rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Mouse Port");
    if (RT_SUCCESS(rc))
    {
        pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
        if (!pThis->Mouse.pDrv)
        {
            AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
            rc = VERR_PDM_MISSING_INTERFACE;
        }
    }
    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
    {
        Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
        rc = VINF_SUCCESS;
    }
    else
        AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));

    return rc;
}
예제 #2
0
/**
 * Copy the device and free resources associated with the backend.
 */
static DECLCALLBACK(void) usbProxyWinClose(PUSBPROXYDEV pProxyDev)
{
    /* Here we just close the device and free up p->priv
     * there is no need to do anything like cancel outstanding requests
     * that will have been done already
     */
    PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
    Assert(pPriv);
    if (!pPriv)
        return;
    Log(("usbProxyWinClose: %p\n", pPriv->hDev));

    if (pPriv->hDev != INVALID_HANDLE_VALUE)
    {
        Assert(pPriv->fClaimed);

        USBSUP_RELEASEDEV in;
        DWORD cbReturned = 0;
        in.bInterfaceNumber = pPriv->bInterfaceNumber;
        if (!DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RELEASE_DEVICE, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
        {
            Log(("usbproxy: usbProxyWinClose: DeviceIoControl %#x failed with %#x!!\n", pPriv->hDev, GetLastError()));
        }
        if (!CloseHandle(pPriv->hDev))
            AssertLogRelMsgFailed(("usbproxy: usbProxyWinClose: CloseHandle %#x failed with %#x!!\n", pPriv->hDev, GetLastError()));
        pPriv->hDev = INVALID_HANDLE_VALUE;
    }

    CloseHandle(pPriv->hEventWakeup);
    RTCritSectDelete(&pPriv->CritSect);

    RTMemFree(pPriv->paQueuedUrbs);
    RTMemFree(pPriv->paHandles);
}
예제 #3
0
파일: string.cpp 프로젝트: caidongyun/tray
/**
 * A variant of Utf8Str::copyFrom that does not throw any exceptions but returns
 * E_OUTOFMEMORY instead.
 *
 * @param   a_pbstr         The source string.
 * @returns S_OK or E_OUTOFMEMORY.
 */
HRESULT Utf8Str::copyFromEx(CBSTR a_pbstr)
{
    if (a_pbstr && *a_pbstr)
    {
        int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr,
                                  RTSTR_MAX,        // size_t cwcString: translate entire string
                                  &m_psz,           // char **ppsz: output buffer
                                  0,                // size_t cch: if 0, func allocates buffer in *ppsz
                                  &m_cch);          // size_t *pcch: receives the size of the output string, excluding the terminator.
        if (RT_SUCCESS(vrc))
            m_cbAllocated = m_cch + 1;
        else
        {
            if (   vrc != VERR_NO_STR_MEMORY
                && vrc != VERR_NO_MEMORY)
            {
                /* ASSUME: input is valid Utf-16. Fake out of memory error. */
                AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len((PCRTUTF16)a_pbstr) * sizeof(RTUTF16), a_pbstr));
            }

            m_cch = 0;
            m_cbAllocated = 0;
            m_psz = NULL;

            return E_OUTOFMEMORY;
        }
    }
    else
    {
        m_cch = 0;
        m_cbAllocated = 0;
        m_psz = NULL;
    }
    return S_OK;
}
예제 #4
0
/**
 * Signal a fatal wait error.
 *
 * @returns Fatal error code to be propagated up the call stack.
 * @param   pUVCpu              The user mode per CPU structure of the calling
 *                              EMT.
 * @param   pszFmt              The error format with a single %Rrc in it.
 * @param   rcFmt               The status code to format.
 */
static int vmR3FatalWaitError(PUVMCPU pUVCpu, const char *pszFmt, int rcFmt)
{
    /** @todo This is wrong ... raise a fatal error / guru meditation
     *        instead. */
    AssertLogRelMsgFailed((pszFmt, rcFmt));
    ASMAtomicUoWriteBool(&pUVCpu->pUVM->vm.s.fTerminateEMT, true);
    if (pUVCpu->pVM)
        VM_FF_SET(pUVCpu->pVM, VM_FF_CHECK_VM_STATE);
    return VERR_VM_FATAL_WAIT_ERROR;
}
예제 #5
0
DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
{
    if (fFatal)
        supR3HardenedFatalV(pszFormat, va);

    va_list vaCopy;
    va_copy(vaCopy, va);
    AssertLogRelMsgFailed(("%N", pszFormat, &vaCopy)); /** @todo figure out why this ain't working, or at seems to be that way... */
    va_end(vaCopy);

    RTLogRelPrintfV(pszFormat, va);
    return rc;
}
static void vboxguestwinTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits,
                                                      uint32_t u32Exp)
{
    ULONG u32Bits2 = u32Bits;
    uint32_t u32Result = vboxugestwinAtomicBitsTestAndClear(&u32Bits2, u32Mask);
    if (   u32Result != u32Exp
        || (u32Bits2 & u32Mask)
        || (u32Bits2 & u32Result)
        || ((u32Bits2 | u32Result) != u32Bits)
       )
        AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
                               __PRETTY_FUNCTION__, u32Mask, u32Bits, u32Bits2,
                               u32Result));
}
예제 #7
0
파일: string.cpp 프로젝트: caidongyun/tray
void Bstr::copyFromN(const char *a_pszSrc, size_t a_cchMax)
{
    /*
     * Initialie m_bstr first in case of throws further down in the code, then
     * check for empty input (m_bstr == NULL means empty, there are no NULL
     * strings).
     */
    m_bstr = NULL;
    if (!a_cchMax || !a_pszSrc || !*a_pszSrc)
        return;

    /*
     * Calculate the length and allocate a BSTR string buffer of the right
     * size, i.e. optimize heap usage.
     */
    size_t cwc;
    int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc);
    if (RT_SUCCESS(vrc))
    {
        m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR)));
        if (RT_LIKELY(m_bstr))
        {
            PRTUTF16 pwsz = (PRTUTF16)m_bstr;
            vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL);
            if (RT_SUCCESS(vrc))
                return;

            /* This should not happen! */
            AssertRC(vrc);
            cleanup();
        }
    }
    else /* ASSUME: input is valid Utf-8. Fake out of memory error. */
        AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc));
    throw std::bad_alloc();
}
/**
 * Relocates the RC address space.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   offDelta    The relocation delta.
 */
void dbgfR3AsRelocate(PUVM pUVM, RTGCUINTPTR offDelta)
{
    /*
     * We will relocate the raw-mode context modules by offDelta if they have
     * been injected into the the DBGF_AS_RC map.
     */
    if (   pUVM->dbgf.s.afAsAliasPopuplated[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_RC)]
        && offDelta != 0)
    {
        RTDBGAS hAs = pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_RC)];

        /* Take a snapshot of the modules as we might have overlapping
           addresses between the previous and new mapping. */
        RTDbgAsLockExcl(hAs);
        uint32_t cModules = RTDbgAsModuleCount(hAs);
        if (cModules > 0 && cModules < _4K)
        {
            struct DBGFASRELOCENTRY
            {
                RTDBGMOD    hDbgMod;
                RTRCPTR     uOldAddr;
            } *paEntries = (struct DBGFASRELOCENTRY *)RTMemTmpAllocZ(sizeof(paEntries[0]) * cModules);
            if (paEntries)
            {
                /* Snapshot. */
                for (uint32_t i = 0; i < cModules; i++)
                {
                    paEntries[i].hDbgMod = RTDbgAsModuleByIndex(hAs, i);
                    AssertLogRelMsg(paEntries[i].hDbgMod != NIL_RTDBGMOD, ("iModule=%#x\n", i));

                    RTDBGASMAPINFO  aMappings[1] = { { 0, 0 } };
                    uint32_t        cMappings = 1;
                    int rc = RTDbgAsModuleQueryMapByIndex(hAs, i, &aMappings[0], &cMappings, 0 /*fFlags*/);
                    if (RT_SUCCESS(rc) && cMappings == 1 && aMappings[0].iSeg == NIL_RTDBGSEGIDX)
                        paEntries[i].uOldAddr = (RTRCPTR)aMappings[0].Address;
                    else
                        AssertLogRelMsgFailed(("iModule=%#x rc=%Rrc cMappings=%#x.\n", i, rc, cMappings));
                }

                /* Unlink them. */
                for (uint32_t i = 0; i < cModules; i++)
                {
                    int rc = RTDbgAsModuleUnlink(hAs, paEntries[i].hDbgMod);
                    AssertLogRelMsg(RT_SUCCESS(rc), ("iModule=%#x rc=%Rrc hDbgMod=%p\n", i, rc, paEntries[i].hDbgMod));
                }

                /* Link them at the new locations. */
                for (uint32_t i = 0; i < cModules; i++)
                {
                    RTRCPTR uNewAddr = paEntries[i].uOldAddr + offDelta;
                    int rc = RTDbgAsModuleLink(hAs, paEntries[i].hDbgMod, uNewAddr,
                                               RTDBGASLINK_FLAGS_REPLACE);
                    AssertLogRelMsg(RT_SUCCESS(rc),
                                    ("iModule=%#x rc=%Rrc hDbgMod=%p %RRv -> %RRv\n", i, rc, paEntries[i].hDbgMod,
                                     paEntries[i].uOldAddr, uNewAddr));
                    RTDbgModRelease(paEntries[i].hDbgMod);
                }

                RTMemTmpFree(paEntries);
            }
            else
                AssertLogRelMsgFailed(("No memory for %#x modules.\n", cModules));
        }
        else
            AssertLogRelMsgFailed(("cModules=%#x\n", cModules));
        RTDbgAsUnlockExcl(hAs);
    }
}
예제 #9
0
/**
 * The emulation thread main function, with Virtual CPU ID for debugging.
 *
 * @returns Thread exit code.
 * @param   ThreadSelf  The handle to the executing thread.
 * @param   pUVCpu      Pointer to the user mode per-VCpu structure.
 * @param   idCpu       The virtual CPU ID, for backtrace purposes.
 */
int vmR3EmulationThreadWithId(RTTHREAD ThreadSelf, PUVMCPU pUVCpu, VMCPUID idCpu)
{
    PUVM    pUVM = pUVCpu->pUVM;
    int     rc;

    AssertReleaseMsg(VALID_PTR(pUVM) && pUVM->u32Magic == UVM_MAGIC,
                     ("Invalid arguments to the emulation thread!\n"));

    rc = RTTlsSet(pUVM->vm.s.idxTLS, pUVCpu);
    AssertReleaseMsgRCReturn(rc, ("RTTlsSet %x failed with %Rrc\n", pUVM->vm.s.idxTLS, rc), rc);

    if (   pUVM->pVmm2UserMethods
        && pUVM->pVmm2UserMethods->pfnNotifyEmtInit)
        pUVM->pVmm2UserMethods->pfnNotifyEmtInit(pUVM->pVmm2UserMethods, pUVM, pUVCpu);

    /*
     * The request loop.
     */
    rc = VINF_SUCCESS;
    Log(("vmR3EmulationThread: Emulation thread starting the days work... Thread=%#x pUVM=%p\n", ThreadSelf, pUVM));
    VMSTATE enmBefore = VMSTATE_CREATED; /* (only used for logging atm.) */
    for (;;)
    {
        /*
         * During early init there is no pVM, so make a special path
         * for that to keep things clearly separate.
         */
        if (!pUVM->pVM)
        {
            /*
             * Check for termination first.
             */
            if (pUVM->vm.s.fTerminateEMT)
            {
                rc = VINF_EM_TERMINATE;
                break;
            }

            /*
             * Only the first VCPU may initialize the VM during early init
             * and must therefore service all VMCPUID_ANY requests.
             * See also VMR3Create
             */
            if (    (pUVM->vm.s.pNormalReqs || pUVM->vm.s.pPriorityReqs)
                &&  pUVCpu->idCpu == 0)
            {
                /*
                 * Service execute in any EMT request.
                 */
                rc = VMR3ReqProcessU(pUVM, VMCPUID_ANY, false /*fPriorityOnly*/);
                Log(("vmR3EmulationThread: Req rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), pUVM->pVM ? VMR3GetStateName(pUVM->pVM->enmVMState) : "CREATING"));
            }
            else if (pUVCpu->vm.s.pNormalReqs || pUVCpu->vm.s.pPriorityReqs)
            {
                /*
                 * Service execute in specific EMT request.
                 */
                rc = VMR3ReqProcessU(pUVM, pUVCpu->idCpu, false /*fPriorityOnly*/);
                Log(("vmR3EmulationThread: Req (cpu=%u) rc=%Rrc, VM state %s -> %s\n", pUVCpu->idCpu, rc, VMR3GetStateName(enmBefore), pUVM->pVM ? VMR3GetStateName(pUVM->pVM->enmVMState) : "CREATING"));
            }
            else
            {
                /*
                 * Nothing important is pending, so wait for something.
                 */
                rc = VMR3WaitU(pUVCpu);
                if (RT_FAILURE(rc))
                {
                    AssertLogRelMsgFailed(("VMR3WaitU failed with %Rrc\n", rc));
                    break;
                }
            }
        }
        else
        {
            /*
             * Pending requests which needs servicing?
             *
             * We check for state changes in addition to status codes when
             * servicing requests. (Look after the ifs.)
             */
            PVM pVM = pUVM->pVM;
            enmBefore = pVM->enmVMState;
            if (pUVM->vm.s.fTerminateEMT)
            {
                rc = VINF_EM_TERMINATE;
                break;
            }

            if (VM_FF_ISPENDING(pVM, VM_FF_EMT_RENDEZVOUS))
            {
                rc = VMMR3EmtRendezvousFF(pVM, &pVM->aCpus[idCpu]);
                Log(("vmR3EmulationThread: Rendezvous rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
            }
            else if (pUVM->vm.s.pNormalReqs || pUVM->vm.s.pPriorityReqs)
            {
                /*
                 * Service execute in any EMT request.
                 */
                rc = VMR3ReqProcessU(pUVM, VMCPUID_ANY, false /*fPriorityOnly*/);
                Log(("vmR3EmulationThread: Req rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
            }
            else if (pUVCpu->vm.s.pNormalReqs || pUVCpu->vm.s.pPriorityReqs)
            {
                /*
                 * Service execute in specific EMT request.
                 */
                rc = VMR3ReqProcessU(pUVM, pUVCpu->idCpu, false /*fPriorityOnly*/);
                Log(("vmR3EmulationThread: Req (cpu=%u) rc=%Rrc, VM state %s -> %s\n", pUVCpu->idCpu, rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
            }
            else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
            {
                /*
                 * Service the debugger request.
                 */
                rc = DBGFR3VMMForcedAction(pVM);
                Log(("vmR3EmulationThread: Dbg rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
            }
            else if (VM_FF_TESTANDCLEAR(pVM, VM_FF_RESET))
            {
                /*
                 * Service a delayed reset request.
                 */
                rc = VMR3Reset(pVM);
                VM_FF_CLEAR(pVM, VM_FF_RESET);
                Log(("vmR3EmulationThread: Reset rc=%Rrc, VM state %s -> %s\n", rc, VMR3GetStateName(enmBefore), VMR3GetStateName(pVM->enmVMState)));
            }
            else
            {
                /*
                 * Nothing important is pending, so wait for something.
                 */
                rc = VMR3WaitU(pUVCpu);
                if (RT_FAILURE(rc))
                {
                    AssertLogRelMsgFailed(("VMR3WaitU failed with %Rrc\n", rc));
                    break;
                }
            }

            /*
             * Check for termination requests, these have extremely high priority.
             */
            if (    rc == VINF_EM_TERMINATE
                ||  pUVM->vm.s.fTerminateEMT)
                break;
        }

        /*
         * Some requests (both VMR3Req* and the DBGF) can potentially resume
         * or start the VM, in that case we'll get a change in VM status
         * indicating that we're now running.
         */
        if (    RT_SUCCESS(rc)
            &&  pUVM->pVM)
        {
            PVM     pVM   = pUVM->pVM;
            PVMCPU  pVCpu = &pVM->aCpus[idCpu];
            if (    pVM->enmVMState == VMSTATE_RUNNING
                &&  VMCPUSTATE_IS_STARTED(VMCPU_GET_STATE(pVCpu)))
            {
                rc = EMR3ExecuteVM(pVM, pVCpu);
                Log(("vmR3EmulationThread: EMR3ExecuteVM() -> rc=%Rrc, enmVMState=%d\n", rc, pVM->enmVMState));
                if (EMGetState(pVCpu) == EMSTATE_GURU_MEDITATION)
                    vmR3SetGuruMeditation(pVM);
            }
        }

    } /* forever */


    /*
     * Cleanup and exit.
     */
    Log(("vmR3EmulationThread: Terminating emulation thread! Thread=%#x pUVM=%p rc=%Rrc enmBefore=%d enmVMState=%d\n",
         ThreadSelf, pUVM, rc, enmBefore, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_TERMINATED));
    if (   idCpu == 0
        && pUVM->pVM)
    {
        PVM pVM = pUVM->pVM;
        vmR3SetTerminated(pVM);
        pUVM->pVM = NULL;

        /** @todo SMP: This isn't 100% safe. We should wait for the other
         *        threads to finish before destroy the VM. */
        int rc2 = SUPR3CallVMMR0Ex(pVM->pVMR0, 0 /*idCpu*/, VMMR0_DO_GVMM_DESTROY_VM, 0, NULL);
        AssertLogRelRC(rc2);
    }

    if (   pUVM->pVmm2UserMethods
        && pUVM->pVmm2UserMethods->pfnNotifyEmtTerm)
        pUVM->pVmm2UserMethods->pfnNotifyEmtTerm(pUVM->pVmm2UserMethods, pUVM, pUVCpu);

    pUVCpu->vm.s.NativeThreadEMT = NIL_RTNATIVETHREAD;
    Log(("vmR3EmulationThread: EMT is terminated.\n"));
    return rc;
}
예제 #10
0
/**
 * Receive and process a byte sent by the keyboard controller.
 *
 * @param   pThis               The PS/2 auxiliary device instance data.
 * @param   cmd                 The command (or data) byte.
 */
int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
{
    uint8_t u8Val;
    bool    fHandled = true;

    LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
//LogRel(("aux: cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));

    if (pThis->enmMode == AUX_MODE_RESET)
        /* In reset mode, do not respond at all. */
        return VINF_SUCCESS;

    /* If there's anything left in the command response queue, trash it. */
    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);

    if (pThis->enmMode == AUX_MODE_WRAP)
    {
        /* In wrap mode, bounce most data right back.*/
        if (cmd == ACMD_RESET || cmd == ACMD_RESET_WRAP)
            ;   /* Handle as regular commands. */
        else
        {
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, cmd);
            return VINF_SUCCESS;
        }
    }

#ifndef IN_RING3
    /* Reset, Enable, and Set Default commands must be run in R3. */
    if (cmd == ACMD_RESET || cmd == ACMD_ENABLE || cmd == ACMD_SET_DEFAULT)
        return VINF_IOM_R3_IOPORT_WRITE;
#endif

    switch (cmd)
    {
        case ACMD_SET_SCALE_11:
            pThis->u8State &= ~AUX_STATE_SCALING;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_SCALE_21:
            pThis->u8State |= AUX_STATE_SCALING;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_REQ_STATUS:
            /* Report current status, sample rate, and resolution. */
            u8Val  = (pThis->u8State & AUX_STATE_EXTERNAL) | (pThis->fCurrB & PS2M_STD_BTN_MASK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_STREAM:
            pThis->u8State &= ~AUX_STATE_REMOTE;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_READ_REMOTE:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->cmdQ, false);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESET_WRAP:
            pThis->enmMode = AUX_MODE_STD;
            /* NB: Stream mode reporting remains disabled! */
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_WRAP:
            pThis->enmMode = AUX_MODE_WRAP;
            pThis->u8State &= ~AUX_STATE_ENABLED;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_REMOTE:
            pThis->u8State |= AUX_STATE_REMOTE;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_READ_ID:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_ENABLE:
            pThis->u8State |= AUX_STATE_ENABLED;
#ifdef IN_RING3
            ps2mSetDriverState(pThis, true);
#else
            AssertLogRelMsgFailed(("Invalid ACMD_ENABLE outside R3!\n"));
#endif
            ps2kClearQueue((GeneriQ *)&pThis->evtQ);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_DISABLE:
            pThis->u8State &= ~AUX_STATE_ENABLED;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_SET_DEFAULT:
            ps2mSetDefaults(pThis);
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESEND:
            pThis->u8CurrCmd = 0;
            break;
        case ACMD_RESET:
            ps2mSetDefaults(pThis);
            /// @todo reset more?
            pThis->u8CurrCmd = cmd;
            pThis->enmMode   = AUX_MODE_RESET;
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            if (pThis->fDelayReset)
                /* Slightly delay reset completion; it might take hundreds of ms. */
                TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
            else
#ifdef IN_RING3
                ps2mReset(pThis);
#else
                AssertLogRelMsgFailed(("Invalid ACMD_RESET outside R3!\n"));
#endif
            break;
        /* The following commands need a parameter. */
        case ACMD_SET_RES:
        case ACMD_SET_SAMP_RATE:
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
            pThis->u8CurrCmd = cmd;
            break;
        default:
            /* Sending a command instead of a parameter starts the new command. */
            switch (pThis->u8CurrCmd)
            {
                case ACMD_SET_RES:
                    if (cmd < 4)    /* Valid resolutions are 0-3. */
                    {
                        pThis->u8Resolution = cmd;
                        pThis->u8State &= ~AUX_STATE_RES_ERR;
                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
                        pThis->u8CurrCmd = 0;
                    }
                    else
                    {
                        /* Bad resolution. Reply with Resend or Error. */
                        if (pThis->u8State & AUX_STATE_RES_ERR)
                        {
                            pThis->u8State &= ~AUX_STATE_RES_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
                            pThis->u8CurrCmd = 0;
                        }
                        else
                        {
                            pThis->u8State |= AUX_STATE_RES_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
                            /* NB: Current command remains unchanged. */
                        }
                    }
                    break;
                case ACMD_SET_SAMP_RATE:
                    if (ps2mIsRateSupported(cmd))
                    {
                        pThis->u8State &= ~AUX_STATE_RATE_ERR;
                        ps2mSetRate(pThis, cmd);
                        ps2mRateProtocolKnock(pThis, cmd);
                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
                        pThis->u8CurrCmd = 0;
                    }
                    else
                    {
                        /* Bad rate. Reply with Resend or Error. */
                        if (pThis->u8State & AUX_STATE_RATE_ERR)
                        {
                            pThis->u8State &= ~AUX_STATE_RATE_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
                            pThis->u8CurrCmd = 0;
                        }
                        else
                        {
                            pThis->u8State |= AUX_STATE_RATE_ERR;
                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
                            /* NB: Current command remains unchanged. */
                        }
                    }
                    break;
                default:
                    fHandled = false;
            }
            /* Fall through only to handle unrecognized commands. */
            if (fHandled)
                break;

        case ACMD_INVALID_1:
        case ACMD_INVALID_2:
        case ACMD_INVALID_3:
        case ACMD_INVALID_4:
        case ACMD_INVALID_5:
        case ACMD_INVALID_6:
        case ACMD_INVALID_7:
        case ACMD_INVALID_8:
        case ACMD_INVALID_9:
        case ACMD_INVALID_10:
            Log(("Unsupported command 0x%02X!\n", cmd));
            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
            pThis->u8CurrCmd = 0;
            break;
    }
    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
//    KBCUpdateInterrupts(pThis->pParent);
    return VINF_SUCCESS;
}
예제 #11
0
/*static*/
DECLCALLBACK(int) VirtualBox::ClientWatcher::worker(RTTHREAD hThreadSelf, void *pvUser)
{
    LogFlowFuncEnter();
    NOREF(hThreadSelf);

    VirtualBox::ClientWatcher *that = (VirtualBox::ClientWatcher *)pvUser;
    Assert(that);

    typedef std::vector<ComObjPtr<Machine> > MachineVector;
    typedef std::vector<ComObjPtr<SessionMachine> > SessionMachineVector;

    SessionMachineVector machines;
    MachineVector spawnedMachines;

    size_t cnt = 0;
    size_t cntSpawned = 0;

    VirtualBoxBase::initializeComForThread();

#if defined(RT_OS_WINDOWS)

    int vrc;

    /* Initialize all the subworker data. */
    that->maSubworkers[0].hThread = hThreadSelf;
    for (uint32_t iSubworker = 1; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
        that->maSubworkers[iSubworker].hThread    = NIL_RTTHREAD;
    for (uint32_t iSubworker = 0; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
    {
        that->maSubworkers[iSubworker].pSelf      = that;
        that->maSubworkers[iSubworker].iSubworker = iSubworker;
    }

    do
    {
        /* VirtualBox has been early uninitialized, terminate. */
        AutoCaller autoCaller(that->mVirtualBox);
        if (!autoCaller.isOk())
            break;

        bool fPidRace = false;          /* We poll if the PID of a spawning session hasn't been established yet.  */
        bool fRecentDeath = false;      /* We slowly poll if a session has recently been closed to do reaping. */
        for (;;)
        {
            /* release the caller to let uninit() ever proceed */
            autoCaller.release();

            /* Kick of the waiting. */
            uint32_t const cSubworkers = (that->mcWaitHandles + CW_MAX_HANDLES_PER_THREAD - 1) / CW_MAX_HANDLES_PER_THREAD;
            uint32_t const cMsWait     = fPidRace ? 500 : fRecentDeath ? 5000 : INFINITE;
            LogFlowFunc(("UPDATE: Waiting. %u handles, %u subworkers, %u ms wait\n", that->mcWaitHandles, cSubworkers, cMsWait));

            that->mcMsWait = cMsWait;
            ASMAtomicWriteU32(&that->mcActiveSubworkers, cSubworkers);
            RTThreadUserReset(hThreadSelf);

            for (uint32_t iSubworker = 1; iSubworker < cSubworkers; iSubworker++)
            {
                if (that->maSubworkers[iSubworker].hThread != NIL_RTTHREAD)
                {
                    vrc = RTThreadUserSignal(that->maSubworkers[iSubworker].hThread);
                    AssertLogRelMsg(RT_SUCCESS(vrc), ("RTThreadUserSignal -> %Rrc\n", vrc));
                }
                else
                {
                    vrc = RTThreadCreateF(&that->maSubworkers[iSubworker].hThread,
                                          VirtualBox::ClientWatcher::subworkerThread, &that->maSubworkers[iSubworker],
                                          _128K, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Watcher%u", iSubworker);
                    AssertLogRelMsgStmt(RT_SUCCESS(vrc), ("%Rrc iSubworker=%u\n", vrc, iSubworker),
                                        that->maSubworkers[iSubworker].hThread = NIL_RTTHREAD);
                }
                if (RT_FAILURE(vrc))
                    that->subworkerWait(&that->maSubworkers[iSubworker], 1);
            }

            /* Wait ourselves. */
            that->subworkerWait(&that->maSubworkers[0], cMsWait);

            /* Make sure all waiters are done waiting. */
            BOOL fRc = SetEvent(that->mUpdateReq);
            Assert(fRc); NOREF(fRc);

            vrc = RTThreadUserWait(hThreadSelf, RT_INDEFINITE_WAIT);
            AssertLogRelMsg(RT_SUCCESS(vrc), ("RTThreadUserWait -> %Rrc\n", vrc));
            Assert(that->mcActiveSubworkers == 0);

            /* Consume pending update request before proceeding with processing the wait results. */
            fRc = ResetEvent(that->mUpdateReq);
            Assert(fRc);

            bool update = ASMAtomicXchgBool(&that->mfUpdateReq, false);
            if (update)
                LogFlowFunc(("UPDATE: Update request pending\n"));
            update |= fPidRace;

            /* Process the wait results. */
            autoCaller.add();
            if (!autoCaller.isOk())
                break;
            fRecentDeath = false;
            for (uint32_t iSubworker = 0; iSubworker < cSubworkers; iSubworker++)
            {
                DWORD dwWait = that->maSubworkers[iSubworker].dwWait;
                LogFlowFunc(("UPDATE: subworker #%u: dwWait=%#x\n", iSubworker, dwWait));
                if (   (dwWait > WAIT_OBJECT_0    && dwWait < WAIT_OBJECT_0    + CW_MAX_HANDLES_PER_THREAD)
                    || (dwWait > WAIT_ABANDONED_0 && dwWait < WAIT_ABANDONED_0 + CW_MAX_HANDLES_PER_THREAD) )
                {
                    uint32_t idxHandle = iSubworker * CW_MAX_HANDLES_PER_THREAD;
                    if (dwWait > WAIT_OBJECT_0    && dwWait < WAIT_OBJECT_0    + CW_MAX_HANDLES_PER_THREAD)
                        idxHandle += dwWait - WAIT_OBJECT_0;
                    else
                        idxHandle += dwWait - WAIT_ABANDONED_0;

                    uint32_t const idxMachine = idxHandle - (iSubworker + 1);
                    if (idxMachine < cnt)
                    {
                        /* Machine mutex is released or abandond due to client process termination. */
                        LogFlowFunc(("UPDATE: Calling i_checkForDeath on idxMachine=%u (idxHandle=%u) dwWait=%#x\n",
                                     idxMachine, idxHandle, dwWait));
                        fRecentDeath |= (machines[idxMachine])->i_checkForDeath();
                    }
                    else if (idxMachine < cnt + cntSpawned)
                    {
                        /* Spawned VM process has terminated normally. */
                        Assert(dwWait < WAIT_ABANDONED_0);
                        LogFlowFunc(("UPDATE: Calling i_checkForSpawnFailure on idxMachine=%u/%u idxHandle=%u dwWait=%#x\n",
                                     idxMachine, idxMachine - cnt, idxHandle, dwWait));
                        fRecentDeath |= (spawnedMachines[idxMachine - cnt])->i_checkForSpawnFailure();
                    }
                    else
                        AssertFailed();
                    update = true;
                }
                else
                    Assert(dwWait == WAIT_OBJECT_0 || dwWait == WAIT_TIMEOUT);
            }

            if (update)
            {
                LogFlowFunc(("UPDATE: Update pending (cnt=%u cntSpawned=%u)...\n", cnt, cntSpawned));

                /* close old process handles */
                that->winResetHandleArray((uint32_t)cntSpawned);

                // get reference to the machines list in VirtualBox
                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();

                // lock the machines list for reading
                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);

                /* obtain a new set of opened machines */
                cnt = 0;
                machines.clear();
                uint32_t idxHandle = 0;

                for (MachinesOList::iterator it = allMachines.begin();
                     it != allMachines.end();
                     ++it)
                {
                    AssertMsgBreak(idxHandle < CW_MAX_CLIENTS, ("CW_MAX_CLIENTS reached"));

                    ComObjPtr<SessionMachine> sm;
                    if ((*it)->i_isSessionOpenOrClosing(sm))
                    {
                        AutoCaller smCaller(sm);
                        if (smCaller.isOk())
                        {
                            AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
                            Machine::ClientToken *ct = sm->i_getClientToken();
                            if (ct)
                            {
                                HANDLE ipcSem = ct->getToken();
                                machines.push_back(sm);
                                if (!(idxHandle % CW_MAX_HANDLES_PER_THREAD))
                                    idxHandle++;
                                that->mahWaitHandles[idxHandle++] = ipcSem;
                                ++cnt;
                            }
                        }
                    }
                }

                LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));

                /* obtain a new set of spawned machines */
                fPidRace = false;
                cntSpawned = 0;
                spawnedMachines.clear();

                for (MachinesOList::iterator it = allMachines.begin();
                     it != allMachines.end();
                     ++it)
                {
                    AssertMsgBreak(idxHandle < CW_MAX_CLIENTS, ("CW_MAX_CLIENTS reached"));

                    if ((*it)->i_isSessionSpawning())
                    {
                        ULONG pid;
                        HRESULT hrc = (*it)->COMGETTER(SessionPID)(&pid);
                        if (SUCCEEDED(hrc))
                        {
                            if (pid != NIL_RTPROCESS)
                            {
                                HANDLE hProc = OpenProcess(SYNCHRONIZE, FALSE, pid);
                                AssertMsg(hProc != NULL, ("OpenProcess (pid=%d) failed with %d\n", pid, GetLastError()));
                                if (hProc != NULL)
                                {
                                    spawnedMachines.push_back(*it);
                                    if (!(idxHandle % CW_MAX_HANDLES_PER_THREAD))
                                        idxHandle++;
                                    that->mahWaitHandles[idxHandle++] = hProc;
                                    ++cntSpawned;
                                }
                            }
                            else
                                fPidRace = true;
                        }
                    }
                }

                LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));

                /* Update mcWaitHandles and make sure there is at least one handle to wait on. */
                that->mcWaitHandles = RT_MAX(idxHandle, 1);

                // machines lock unwinds here
            }
            else
                LogFlowFunc(("UPDATE: No update pending.\n"));

            /* reap child processes */
            that->reapProcesses();

        } /* for ever (well, till autoCaller fails). */

    } while (0);

    /* Terminate subworker threads. */
    ASMAtomicWriteBool(&that->mfTerminate, true);
    for (uint32_t iSubworker = 1; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
        if (that->maSubworkers[iSubworker].hThread != NIL_RTTHREAD)
            RTThreadUserSignal(that->maSubworkers[iSubworker].hThread);
    for (uint32_t iSubworker = 1; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
        if (that->maSubworkers[iSubworker].hThread != NIL_RTTHREAD)
        {
            vrc = RTThreadWait(that->maSubworkers[iSubworker].hThread, RT_MS_1MIN, NULL /*prc*/);
            if (RT_SUCCESS(vrc))
                that->maSubworkers[iSubworker].hThread = NIL_RTTHREAD;
            else
                AssertLogRelMsgFailed(("RTThreadWait -> %Rrc\n", vrc));
        }

    /* close old process handles */
    that->winResetHandleArray((uint32_t)cntSpawned);

    /* release sets of machines if any */
    machines.clear();
    spawnedMachines.clear();

    ::CoUninitialize();

#elif defined(RT_OS_OS2)

    /* according to PMREF, 64 is the maximum for the muxwait list */
    SEMRECORD handles[64];

    HMUX muxSem = NULLHANDLE;

    do
    {
        AutoCaller autoCaller(that->mVirtualBox);
        /* VirtualBox has been early uninitialized, terminate */
        if (!autoCaller.isOk())
            break;

        for (;;)
        {
            /* release the caller to let uninit() ever proceed */
            autoCaller.release();

            int vrc = RTSemEventWait(that->mUpdateReq, 500);

            /* Restore the caller before using VirtualBox. If it fails, this
             * means VirtualBox is being uninitialized and we must terminate. */
            autoCaller.add();
            if (!autoCaller.isOk())
                break;

            bool update = false;
            bool updateSpawned = false;

            if (RT_SUCCESS(vrc))
            {
                /* update event is signaled */
                update = true;
                updateSpawned = true;
            }
            else
            {
                AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
                          ("RTSemEventWait returned %Rrc\n", vrc));

                /* are there any mutexes? */
                if (cnt > 0)
                {
                    /* figure out what's going on with machines */

                    unsigned long semId = 0;
                    APIRET arc = ::DosWaitMuxWaitSem(muxSem,
                                                     SEM_IMMEDIATE_RETURN, &semId);

                    if (arc == NO_ERROR)
                    {
                        /* machine mutex is normally released */
                        Assert(semId >= 0 && semId < cnt);
                        if (semId >= 0 && semId < cnt)
                        {
#if 0//def DEBUG
                            {
                                AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
                                LogFlowFunc(("released mutex: machine='%ls'\n",
                                             machines[semId]->name().raw()));
                            }
#endif
                            machines[semId]->i_checkForDeath();
                        }
                        update = true;
                    }
                    else if (arc == ERROR_SEM_OWNER_DIED)
                    {
                        /* machine mutex is abandoned due to client process
                         * termination; find which mutex is in the Owner Died
                         * state */
                        for (size_t i = 0; i < cnt; ++i)
                        {
                            PID pid; TID tid;
                            unsigned long reqCnt;
                            arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
                            if (arc == ERROR_SEM_OWNER_DIED)
                            {
                                /* close the dead mutex as asked by PMREF */
                                ::DosCloseMutexSem((HMTX)handles[i].hsemCur);

                                Assert(i >= 0 && i < cnt);
                                if (i >= 0 && i < cnt)
                                {
#if 0//def DEBUG
                                    {
                                        AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
                                        LogFlowFunc(("mutex owner dead: machine='%ls'\n",
                                                     machines[i]->name().raw()));
                                    }
#endif
                                    machines[i]->i_checkForDeath();
                                }
                            }
                        }
                        update = true;
                    }
                    else
                        AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
                                  ("DosWaitMuxWaitSem returned %d\n", arc));
                }

                /* are there any spawning sessions? */
                if (cntSpawned > 0)
                {
                    for (size_t i = 0; i < cntSpawned; ++i)
                        updateSpawned |= (spawnedMachines[i])->
                            i_checkForSpawnFailure();
                }
            }

            if (update || updateSpawned)
            {
                // get reference to the machines list in VirtualBox
                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();

                // lock the machines list for reading
                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);

                if (update)
                {
                    /* close the old muxsem */
                    if (muxSem != NULLHANDLE)
                        ::DosCloseMuxWaitSem(muxSem);

                    /* obtain a new set of opened machines */
                    cnt = 0;
                    machines.clear();

                    for (MachinesOList::iterator it = allMachines.begin();
                         it != allMachines.end(); ++it)
                    {
                        /// @todo handle situations with more than 64 objects
                        AssertMsg(cnt <= 64 /* according to PMREF */,
                                  ("maximum of 64 mutex semaphores reached (%d)",
                                   cnt));

                        ComObjPtr<SessionMachine> sm;
                        if ((*it)->i_isSessionOpenOrClosing(sm))
                        {
                            AutoCaller smCaller(sm);
                            if (smCaller.isOk())
                            {
                                AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
                                ClientToken *ct = sm->i_getClientToken();
                                if (ct)
                                {
                                    HMTX ipcSem = ct->getToken();
                                    machines.push_back(sm);
                                    handles[cnt].hsemCur = (HSEM)ipcSem;
                                    handles[cnt].ulUser = cnt;
                                    ++cnt;
                                }
                            }
                        }
                    }

                    LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));

                    if (cnt > 0)
                    {
                        /* create a new muxsem */
                        APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
                                                           handles,
                                                           DCMW_WAIT_ANY);
                        AssertMsg(arc == NO_ERROR,
                                  ("DosCreateMuxWaitSem returned %d\n", arc));
                        NOREF(arc);
                    }
                }

                if (updateSpawned)
                {
                    /* obtain a new set of spawned machines */
                    spawnedMachines.clear();

                    for (MachinesOList::iterator it = allMachines.begin();
                         it != allMachines.end(); ++it)
                    {
                        if ((*it)->i_isSessionSpawning())
                            spawnedMachines.push_back(*it);
                    }

                    cntSpawned = spawnedMachines.size();
                    LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
                }
            }

            /* reap child processes */
            that->reapProcesses();

        } /* for ever (well, till autoCaller fails). */

    } while (0);

    /* close the muxsem */
    if (muxSem != NULLHANDLE)
        ::DosCloseMuxWaitSem(muxSem);

    /* release sets of machines if any */
    machines.clear();
    spawnedMachines.clear();

#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)

    bool update = false;
    bool updateSpawned = false;

    do
    {
        AutoCaller autoCaller(that->mVirtualBox);
        if (!autoCaller.isOk())
            break;

        do
        {
            /* release the caller to let uninit() ever proceed */
            autoCaller.release();

            /* determine wait timeout adaptively: after updating information
             * relevant to the client watcher, check a few times more
             * frequently. This ensures good reaction time when the signalling
             * has to be done a bit before the actual change for technical
             * reasons, and saves CPU cycles when no activities are expected. */
            RTMSINTERVAL cMillies;
            {
                uint8_t uOld, uNew;
                do
                {
                    uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
                    uNew = uOld ? uOld - 1 : uOld;
                } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
                Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
                cMillies = s_aUpdateTimeoutSteps[uOld];
            }

            int rc = RTSemEventWait(that->mUpdateReq, cMillies);

            /*
             *  Restore the caller before using VirtualBox. If it fails, this
             *  means VirtualBox is being uninitialized and we must terminate.
             */
            autoCaller.add();
            if (!autoCaller.isOk())
                break;

            if (RT_SUCCESS(rc) || update || updateSpawned)
            {
                /* RT_SUCCESS(rc) means an update event is signaled */

                // get reference to the machines list in VirtualBox
                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();

                // lock the machines list for reading
                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);

                if (RT_SUCCESS(rc) || update)
                {
                    /* obtain a new set of opened machines */
                    machines.clear();

                    for (MachinesOList::iterator it = allMachines.begin();
                         it != allMachines.end();
                         ++it)
                    {
                        ComObjPtr<SessionMachine> sm;
                        if ((*it)->i_isSessionOpenOrClosing(sm))
                            machines.push_back(sm);
                    }

                    cnt = machines.size();
                    LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
                }

                if (RT_SUCCESS(rc) || updateSpawned)
                {
                    /* obtain a new set of spawned machines */
                    spawnedMachines.clear();

                    for (MachinesOList::iterator it = allMachines.begin();
                         it != allMachines.end();
                         ++it)
                    {
                        if ((*it)->i_isSessionSpawning())
                            spawnedMachines.push_back(*it);
                    }

                    cntSpawned = spawnedMachines.size();
                    LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
                }

                // machines lock unwinds here
            }

            update = false;
            for (size_t i = 0; i < cnt; ++i)
                update |= (machines[i])->i_checkForDeath();

            updateSpawned = false;
            for (size_t i = 0; i < cntSpawned; ++i)
                updateSpawned |= (spawnedMachines[i])->i_checkForSpawnFailure();

            /* reap child processes */
            that->reapProcesses();
        }
        while (true);
    }
    while (0);

    /* release sets of machines if any */
    machines.clear();
    spawnedMachines.clear();

#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)

    bool updateSpawned = false;

    do
    {
        AutoCaller autoCaller(that->mVirtualBox);
        if (!autoCaller.isOk())
            break;

        do
        {
            /* release the caller to let uninit() ever proceed */
            autoCaller.release();

            /* determine wait timeout adaptively: after updating information
             * relevant to the client watcher, check a few times more
             * frequently. This ensures good reaction time when the signalling
             * has to be done a bit before the actual change for technical
             * reasons, and saves CPU cycles when no activities are expected. */
            RTMSINTERVAL cMillies;
            {
                uint8_t uOld, uNew;
                do
                {
                    uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
                    uNew = uOld ? (uint8_t)(uOld - 1) : uOld;
                } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
                Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
                cMillies = s_aUpdateTimeoutSteps[uOld];
            }

            int rc = RTSemEventWait(that->mUpdateReq, cMillies);

            /*
             *  Restore the caller before using VirtualBox. If it fails, this
             *  means VirtualBox is being uninitialized and we must terminate.
             */
            autoCaller.add();
            if (!autoCaller.isOk())
                break;

            /** @todo this quite big effort for catching machines in spawning
             * state which can't be caught by the token mechanism (as the token
             * can't be in the other process yet) could be eliminated if the
             * reaping is made smarter, having cross-reference information
             * from the pid to the corresponding machine object. Both cases do
             * more or less the same thing anyway. */
            if (RT_SUCCESS(rc) || updateSpawned)
            {
                /* RT_SUCCESS(rc) means an update event is signaled */

                // get reference to the machines list in VirtualBox
                VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();

                // lock the machines list for reading
                AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);

                if (RT_SUCCESS(rc) || updateSpawned)
                {
                    /* obtain a new set of spawned machines */
                    spawnedMachines.clear();

                    for (MachinesOList::iterator it = allMachines.begin();
                         it != allMachines.end();
                         ++it)
                    {
                        if ((*it)->i_isSessionSpawning())
                            spawnedMachines.push_back(*it);
                    }

                    cntSpawned = spawnedMachines.size();
                    LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
                }

                NOREF(cnt);
                // machines lock unwinds here
            }

            updateSpawned = false;
            for (size_t i = 0; i < cntSpawned; ++i)
                updateSpawned |= (spawnedMachines[i])->i_checkForSpawnFailure();

            /* reap child processes */
            that->reapProcesses();
        }
        while (true);
    }
    while (0);

    /* release sets of machines if any */
    machines.clear();
    spawnedMachines.clear();

#else
# error "Port me!"
#endif

    VirtualBoxBase::uninitializeComForThread();

    LogFlowFuncLeave();
    return 0;
}
/**
 * Construct the DMI table.
 *
 * @returns VBox status code.
 * @param   pDevIns             The device instance.
 * @param   pTable              Where to create the DMI table.
 * @param   cbMax               The maximum size of the DMI table.
 * @param   pUuid               Pointer to the UUID to use if the DmiUuid
 *                              configuration string isn't present.
 * @param   pCfg                The handle to our config node.
 * @param   cCpus               Number of VCPUs.
 * @param   pcbDmiTables        Size of DMI data in bytes.
 * @param   pcNumDmiTables      Number of DMI tables.
 */
int FwCommonPlantDMITable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, PCRTUUID pUuid, PCFGMNODE pCfg, uint16_t cCpus, uint16_t *pcbDmiTables, uint16_t *pcNumDmiTables)
{
    /*
     * CFGM Hint!
     *
     * The macros below makes it a bit hard to figure out the config options
     * available here.  To get a quick hint, take a look a the CFGM
     * validation in the calling code (DevEFI.cpp and DevPcBios.cpp).
     *
     * 32-bit signed integer CFGM options are read by DMI_READ_CFG_S32, the 2nd
     * parameter is the CFGM value name.
     *
     * Strings are read by DMI_READ_CFG_STR and DMI_READ_CFG_STR_DEF, the 2nd parameter is
     * the CFGM value name.
     */
#define DMI_CHECK_SIZE(cbWant) \
    { \
        size_t cbNeed = (size_t)(pszStr + cbWant - (char *)pTable) + 5; /* +1 for strtab terminator +4 for end-of-table entry */ \
        if (cbNeed > cbMax) \
        { \
            if (fHideErrors) \
            { \
                LogRel(("One of the DMI strings is too long -- using default DMI data!\n")); \
                continue; \
            } \
            return PDMDevHlpVMSetError(pDevIns, VERR_TOO_MUCH_DATA, RT_SRC_POS, \
                                       N_("One of the DMI strings is too long. Check all bios/Dmi* configuration entries. At least %zu bytes are needed but there is no space for more than %d bytes"), cbNeed, cbMax); \
        } \
    }

#define DMI_READ_CFG_STR_DEF(variable, name, default_value) \
    { \
        if (fForceDefault) \
            pszTmp = default_value; \
        else \
        { \
            rc = CFGMR3QueryStringDef(pCfg, name, szBuf, sizeof(szBuf), default_value); \
            if (RT_FAILURE(rc)) \
            { \
                if (fHideErrors) \
                { \
                    LogRel(("Configuration error: Querying \"" name "\" as a string failed -- using default DMI data!\n")); \
                    continue; \
                } \
                return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
                                           N_("Configuration error: Querying \"" name "\" as a string failed")); \
            } \
            else if (!strcmp(szBuf, "<EMPTY>")) \
                pszTmp = ""; \
            else \
                pszTmp = szBuf; \
        } \
        if (!pszTmp[0]) \
            variable = 0; /* empty string */ \
        else \
        { \
            variable = iStrNr++; \
            size_t cStr = strlen(pszTmp) + 1; \
            DMI_CHECK_SIZE(cStr); \
            memcpy(pszStr, pszTmp, cStr); \
            pszStr += cStr ; \
        } \
    }

#define DMI_READ_CFG_STR(variable, name) \
    DMI_READ_CFG_STR_DEF(variable, # name, g_pszDef ## name)

#define DMI_READ_CFG_S32(variable, name) \
    { \
        if (fForceDefault) \
            variable = g_iDef ## name; \
        else \
        { \
            rc = CFGMR3QueryS32Def(pCfg, # name, & variable, g_iDef ## name); \
            if (RT_FAILURE(rc)) \
            { \
                if (fHideErrors) \
                { \
                    LogRel(("Configuration error: Querying \"" # name "\" as an int failed -- using default DMI data!\n")); \
                    continue; \
                } \
                return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
                                           N_("Configuration error: Querying \"" # name "\" as an int failed")); \
            } \
        } \
    }

#define DMI_START_STRUCT(tbl) \
        pszStr                       = (char *)(tbl + 1); \
        iStrNr                       = 1;

#define DMI_TERM_STRUCT \
    { \
        *pszStr++                    = '\0'; /* terminate set of text strings */ \
        if (iStrNr == 1) \
            *pszStr++                = '\0'; /* terminate a structure without strings */ \
    }

    bool fForceDefault = false;
#ifdef VBOX_BIOS_DMI_FALLBACK
    /*
     * There will be two passes. If an error occurs during the first pass, a
     * message will be written to the release log and we fall back to default
     * DMI data and start a second pass.
     */
    bool fHideErrors = true;
#else
    /*
     * There will be one pass, every error is fatal and will prevent the VM
     * from starting.
     */
    bool fHideErrors = false;
#endif

    uint8_t fDmiUseHostInfo;
    int rc = CFGMR3QueryU8Def(pCfg, "DmiUseHostInfo", &fDmiUseHostInfo, 0);
    if (RT_FAILURE (rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("Configuration error: Failed to read \"DmiUseHostInfo\""));

    /* Sync up with host default DMI values */
    if (fDmiUseHostInfo)
        fwCommonUseHostDMIStrings();

    uint8_t fDmiExposeMemoryTable;
    rc = CFGMR3QueryU8Def(pCfg, "DmiExposeMemoryTable", &fDmiExposeMemoryTable, 0);
    if (RT_FAILURE (rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("Configuration error: Failed to read \"DmiExposeMemoryTable\""));
    uint8_t fDmiExposeProcessorInf;
    rc = CFGMR3QueryU8Def(pCfg, "DmiExposeProcInf", &fDmiExposeProcessorInf, 0);
    if (RT_FAILURE (rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("Configuration error: Failed to read \"DmiExposeProcInf\""));

    for  (;; fForceDefault = true, fHideErrors = false)
    {
        int  iStrNr;
        char szBuf[256];
        char *pszStr = (char *)pTable;
        char szDmiSystemUuid[64];
        char *pszDmiSystemUuid;
        const char *pszTmp;

        if (fForceDefault)
            pszDmiSystemUuid = NULL;
        else
        {
            rc = CFGMR3QueryString(pCfg, "DmiSystemUuid", szDmiSystemUuid, sizeof(szDmiSystemUuid));
            if (rc == VERR_CFGM_VALUE_NOT_FOUND)
                pszDmiSystemUuid = NULL;
            else if (RT_FAILURE(rc))
            {
                if (fHideErrors)
                {
                    LogRel(("Configuration error: Querying \"DmiSystemUuid\" as a string failed, using default DMI data\n"));
                    continue;
                }
                return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                           N_("Configuration error: Querying \"DmiSystemUuid\" as a string failed"));
            }
            else
                pszDmiSystemUuid = szDmiSystemUuid;
        }

        /*********************************
         * DMI BIOS information (Type 0) *
         *********************************/
        PDMIBIOSINF pBIOSInf         = (PDMIBIOSINF)pszStr;
        DMI_CHECK_SIZE(sizeof(*pBIOSInf));

        pszStr                       = (char *)&pBIOSInf->u8ReleaseMajor;
        pBIOSInf->header.u8Length    = RT_OFFSETOF(DMIBIOSINF, u8ReleaseMajor);

        /* don't set these fields by default for legacy compatibility */
        int iDmiBIOSReleaseMajor, iDmiBIOSReleaseMinor;
        DMI_READ_CFG_S32(iDmiBIOSReleaseMajor, DmiBIOSReleaseMajor);
        DMI_READ_CFG_S32(iDmiBIOSReleaseMinor, DmiBIOSReleaseMinor);
        if (iDmiBIOSReleaseMajor != 0 || iDmiBIOSReleaseMinor != 0)
        {
            pszStr = (char *)&pBIOSInf->u8FirmwareMajor;
            pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8FirmwareMajor);
            pBIOSInf->u8ReleaseMajor  = iDmiBIOSReleaseMajor;
            pBIOSInf->u8ReleaseMinor  = iDmiBIOSReleaseMinor;

            int iDmiBIOSFirmwareMajor, iDmiBIOSFirmwareMinor;
            DMI_READ_CFG_S32(iDmiBIOSFirmwareMajor, DmiBIOSFirmwareMajor);
            DMI_READ_CFG_S32(iDmiBIOSFirmwareMinor, DmiBIOSFirmwareMinor);
            if (iDmiBIOSFirmwareMajor != 0 || iDmiBIOSFirmwareMinor != 0)
            {
                pszStr = (char *)(pBIOSInf + 1);
                pBIOSInf->header.u8Length = sizeof(DMIBIOSINF);
                pBIOSInf->u8FirmwareMajor = iDmiBIOSFirmwareMajor;
                pBIOSInf->u8FirmwareMinor = iDmiBIOSFirmwareMinor;
            }
        }

        iStrNr                       = 1;
        pBIOSInf->header.u8Type      = 0; /* BIOS Information */
        pBIOSInf->header.u16Handle   = 0x0000;
        DMI_READ_CFG_STR(pBIOSInf->u8Vendor,  DmiBIOSVendor);
        DMI_READ_CFG_STR(pBIOSInf->u8Version, DmiBIOSVersion);
        pBIOSInf->u16Start           = 0xE000;
        DMI_READ_CFG_STR(pBIOSInf->u8Release, DmiBIOSReleaseDate);
        pBIOSInf->u8ROMSize          = 1; /* 128K */
        pBIOSInf->u64Characteristics = RT_BIT(4)   /* ISA is supported */
                                     | RT_BIT(7)   /* PCI is supported */
                                     | RT_BIT(15)  /* Boot from CD is supported */
                                     | RT_BIT(16)  /* Selectable Boot is supported */
                                     | RT_BIT(27)  /* Int 9h, 8042 Keyboard services supported */
                                     | RT_BIT(30)  /* Int 10h, CGA/Mono Video Services supported */
                                     /* any more?? */
                                     ;
        pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0)   /* ACPI is supported */
                                         /* any more?? */
                                         ;
        pBIOSInf->u8CharacteristicsByte2 = 0
                                         /* any more?? */
                                         ;
        DMI_TERM_STRUCT;

        /***********************************
         * DMI system information (Type 1) *
         ***********************************/
        PDMISYSTEMINF pSystemInf     = (PDMISYSTEMINF)pszStr;
        DMI_CHECK_SIZE(sizeof(*pSystemInf));
        DMI_START_STRUCT(pSystemInf);
        pSystemInf->header.u8Type    = 1; /* System Information */
        pSystemInf->header.u8Length  = sizeof(*pSystemInf);
        pSystemInf->header.u16Handle = 0x0001;
        DMI_READ_CFG_STR(pSystemInf->u8Manufacturer, DmiSystemVendor);
        DMI_READ_CFG_STR(pSystemInf->u8ProductName,  DmiSystemProduct);
        DMI_READ_CFG_STR(pSystemInf->u8Version,      DmiSystemVersion);
        DMI_READ_CFG_STR(pSystemInf->u8SerialNumber, DmiSystemSerial);

        RTUUID uuid;
        if (pszDmiSystemUuid)
        {
            rc = RTUuidFromStr(&uuid, pszDmiSystemUuid);
            if (RT_FAILURE(rc))
            {
                if (fHideErrors)
                {
                    LogRel(("Configuration error: Invalid UUID for DMI tables specified, using default DMI data\n"));
                    continue;
                }
                return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                           N_("Configuration error: Invalid UUID for DMI tables specified"));
            }
            uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
            uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
            uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
            pUuid = &uuid;
        }
        memcpy(pSystemInf->au8Uuid, pUuid, sizeof(RTUUID));

        pSystemInf->u8WakeupType     = 6; /* Power Switch */
        DMI_READ_CFG_STR(pSystemInf->u8SKUNumber, DmiSystemSKU);
        DMI_READ_CFG_STR(pSystemInf->u8Family, DmiSystemFamily);
        DMI_TERM_STRUCT;

        /**********************************
         * DMI board information (Type 2) *
         **********************************/
        PDMIBOARDINF pBoardInf       = (PDMIBOARDINF)pszStr;
        DMI_CHECK_SIZE(sizeof(*pBoardInf));
        DMI_START_STRUCT(pBoardInf);
        int iDmiBoardBoardType;
        pBoardInf->header.u8Type     = 2; /* Board Information */
        pBoardInf->header.u8Length   = sizeof(*pBoardInf);
        pBoardInf->header.u16Handle  = 0x0008;
        DMI_READ_CFG_STR(pBoardInf->u8Manufacturer, DmiBoardVendor);
        DMI_READ_CFG_STR(pBoardInf->u8Product,      DmiBoardProduct);
        DMI_READ_CFG_STR(pBoardInf->u8Version,      DmiBoardVersion);
        DMI_READ_CFG_STR(pBoardInf->u8SerialNumber, DmiBoardSerial);
        DMI_READ_CFG_STR(pBoardInf->u8AssetTag,     DmiBoardAssetTag);
        pBoardInf->u8FeatureFlags    = RT_BIT(0) /* hosting board, e.g. motherboard */
                                     ;
        DMI_READ_CFG_STR(pBoardInf->u8LocationInChass, DmiBoardLocInChass);
        pBoardInf->u16ChassisHandle  = 0x0003; /* see type 3 */
        DMI_READ_CFG_S32(iDmiBoardBoardType, DmiBoardBoardType);
        pBoardInf->u8BoardType = iDmiBoardBoardType;
        pBoardInf->u8cObjectHandles  = 0;

        DMI_TERM_STRUCT;

        /********************************************
         * DMI System Enclosure or Chassis (Type 3) *
         ********************************************/
        PDMICHASSIS pChassis         = (PDMICHASSIS)pszStr;
        DMI_CHECK_SIZE(sizeof(*pChassis));
        pszStr                       = (char*)&pChassis->u32OEMdefined;
        iStrNr                       = 1;
#ifdef VBOX_WITH_DMI_CHASSIS
        pChassis->header.u8Type      = 3; /* System Enclosure or Chassis */
#else
        pChassis->header.u8Type      = 0x7e; /* inactive */
#endif
        pChassis->header.u8Length    = RT_OFFSETOF(DMICHASSIS, u32OEMdefined);
        pChassis->header.u16Handle   = 0x0003;
        DMI_READ_CFG_STR(pChassis->u8Manufacturer, DmiChassisVendor);
        int iDmiChassisType;
        DMI_READ_CFG_S32(iDmiChassisType, DmiChassisType);
        pChassis->u8Type             = iDmiChassisType;
        DMI_READ_CFG_STR(pChassis->u8Version, DmiChassisVersion);
        DMI_READ_CFG_STR(pChassis->u8SerialNumber, DmiChassisSerial);
        DMI_READ_CFG_STR(pChassis->u8AssetTag, DmiChassisAssetTag);
        pChassis->u8BootupState      = 0x03; /* safe */
        pChassis->u8PowerSupplyState = 0x03; /* safe */
        pChassis->u8ThermalState     = 0x03; /* safe */
        pChassis->u8SecurityStatus   = 0x03; /* none XXX */
# if 0
        /* v2.3+, currently not supported */
        pChassis->u32OEMdefined      = 0;
        pChassis->u8Height           = 0; /* unspecified */
        pChassis->u8NumPowerChords   = 0; /* unspecified */
        pChassis->u8ContElems        = 0; /* no contained elements */
        pChassis->u8ContElemRecLen   = 0; /* no contained elements */
# endif
        DMI_TERM_STRUCT;

        /**************************************
         * DMI Processor Information (Type 4) *
         **************************************/

        /*
         * This is just a dummy processor. Should we expose the real guest CPU features
         * here? Accessing this information at this point is difficult.
         */
        char szSocket[32];
        PDMIPROCESSORINF pProcessorInf = (PDMIPROCESSORINF)pszStr;
        DMI_CHECK_SIZE(sizeof(*pProcessorInf));
        DMI_START_STRUCT(pProcessorInf);
        if (fDmiExposeProcessorInf)
            pProcessorInf->header.u8Type   = 4; /* Processor Information */
        else
            pProcessorInf->header.u8Type   = 126; /* inactive structure */
        pProcessorInf->header.u8Length     = sizeof(*pProcessorInf);
        pProcessorInf->header.u16Handle    = 0x0007;
        RTStrPrintf(szSocket, sizeof(szSocket), "Socket #%u", 0);
        pProcessorInf->u8SocketDesignation = iStrNr++;
        {
            size_t cStr = strlen(szSocket) + 1;
            DMI_CHECK_SIZE(cStr);
            memcpy(pszStr, szSocket, cStr);
            pszStr += cStr;
        }
        pProcessorInf->u8ProcessorType     = 0x03; /* Central Processor */
        pProcessorInf->u8ProcessorFamily   = 0xB1; /* Pentium III with Intel SpeedStep(TM) */
        DMI_READ_CFG_STR(pProcessorInf->u8ProcessorManufacturer, DmiProcManufacturer);

        pProcessorInf->u64ProcessorID      = UINT64_C(0x0FEBFBFF00010676);
                                             /* Ext Family ID  = 0
                                              * Ext Model ID   = 2
                                              * Processor Type = 0
                                              * Family ID      = 6
                                              * Model          = 7
                                              * Stepping       = 6
                                              * Features: FPU, VME, DE, PSE, TSC, MSR, PAE, MCE, CX8,
                                              *           APIC, SEP, MTRR, PGE, MCA, CMOV, PAT, PSE-36,
                                              *           CFLSH, DS, ACPI, MMX, FXSR, SSE, SSE2, SS */
        DMI_READ_CFG_STR(pProcessorInf->u8ProcessorVersion, DmiProcVersion);
        pProcessorInf->u8Voltage           = 0x02;   /* 3.3V */
        pProcessorInf->u16ExternalClock    = 0x00;   /* unknown */
        pProcessorInf->u16MaxSpeed         = 3000;   /* 3GHz */
        pProcessorInf->u16CurrentSpeed     = 3000;   /* 3GHz */
        pProcessorInf->u8Status            = RT_BIT(6)  /* CPU socket populated */
                                           | RT_BIT(0)  /* CPU enabled */
                                           ;
        pProcessorInf->u8ProcessorUpgrade  = 0x04;   /* ZIF Socket */
        pProcessorInf->u16L1CacheHandle    = 0xFFFF; /* not specified */
        pProcessorInf->u16L2CacheHandle    = 0xFFFF; /* not specified */
        pProcessorInf->u16L3CacheHandle    = 0xFFFF; /* not specified */
        pProcessorInf->u8SerialNumber      = 0;      /* not specified */
        pProcessorInf->u8AssetTag          = 0;      /* not specified */
        pProcessorInf->u8PartNumber        = 0;      /* not specified */
        pProcessorInf->u8CoreCount         = cCpus;  /*  */
        pProcessorInf->u8CoreEnabled       = cCpus;
        pProcessorInf->u8ThreadCount       = 1;
        pProcessorInf->u16ProcessorCharacteristics
                                           = RT_BIT(2); /* 64-bit capable */
        pProcessorInf->u16ProcessorFamily2 = 0;
        DMI_TERM_STRUCT;

        /***************************************
         * DMI Physical Memory Array (Type 16) *
         ***************************************/
        uint64_t const  cbRamSize = MMR3PhysGetRamSize(PDMDevHlpGetVM(pDevIns));

        PDMIRAMARRAY pMemArray = (PDMIRAMARRAY)pszStr;
        DMI_CHECK_SIZE(sizeof(*pMemArray));
        DMI_START_STRUCT(pMemArray);
        if (fDmiExposeMemoryTable)
            pMemArray->header.u8Type     = 16;     /* Physical Memory Array */
        else
            pMemArray->header.u8Type     = 126;    /* inactive structure */
        pMemArray->header.u8Length       = sizeof(*pMemArray);
        pMemArray->header.u16Handle      = 0x0005;
        pMemArray->u8Location            = 0x03;   /* Motherboard */
        pMemArray->u8Use                 = 0x03;   /* System memory */
        pMemArray->u8MemErrorCorrection  = 0x01;   /* Other */
        if (cbRamSize / _1K > INT32_MAX)
        {
            /** @todo 2TB-1K limit. In such cases we probably need to provide multiple type-16 descriptors.
             * Or use 0x8000'0000 = 'capacity unknown'? */
            AssertLogRelMsgFailed(("DMI: RAM size %#RX64 does not fit into type-16 descriptor, clipping to %#RX64\n",
                                   cbRamSize, (uint64_t)INT32_MAX * _1K));
            pMemArray->u32MaxCapacity    = INT32_MAX;
        }
        else
            pMemArray->u32MaxCapacity    = (int32_t)(cbRamSize / _1K); /* RAM size in K */
        pMemArray->u16MemErrorHandle     = 0xfffe; /* No error info structure */
        pMemArray->u16NumberOfMemDevices = 1;
        DMI_TERM_STRUCT;

        /***************************************
         * DMI Memory Device (Type 17)         *
         ***************************************/
        PDMIMEMORYDEV pMemDev = (PDMIMEMORYDEV)pszStr;
        DMI_CHECK_SIZE(sizeof(*pMemDev));
        DMI_START_STRUCT(pMemDev);
        if (fDmiExposeMemoryTable)
            pMemDev->header.u8Type       = 17;     /* Memory Device */
        else
            pMemDev->header.u8Type       = 126;    /* inactive structure */
        pMemDev->header.u8Length         = sizeof(*pMemDev);
        pMemDev->header.u16Handle        = 0x0006;
        pMemDev->u16PhysMemArrayHandle   = 0x0005; /* handle of array we belong to */
        pMemDev->u16MemErrHandle         = 0xfffe; /* system doesn't provide this information */
        pMemDev->u16TotalWidth           = 0xffff; /* Unknown */
        pMemDev->u16DataWidth            = 0xffff; /* Unknown */
        int16_t u16RamSizeM;
        if (cbRamSize / _1M > INT16_MAX)
        {
            /** @todo 32G-1M limit. Provide multiple type-17 descriptors.
             * The highest bit of u16Size must be 0 to specify 'GB' units / 1 would be 'KB' */
            AssertLogRelMsgFailed(("DMI: RAM size %#RX64 too big for one type-17 descriptor, clipping to %#RX64\n",
                                   cbRamSize, (uint64_t)INT16_MAX * _1M));
            u16RamSizeM = INT16_MAX;
        }
        else
            u16RamSizeM = (uint16_t)(cbRamSize / _1M);
        if (u16RamSizeM == 0)
            u16RamSizeM = 0x400; /* 1G */
        pMemDev->u16Size                 = u16RamSizeM; /* RAM size */
        pMemDev->u8FormFactor            = 0x09; /* DIMM */
        pMemDev->u8DeviceSet             = 0x00; /* Not part of a device set */
        DMI_READ_CFG_STR_DEF(pMemDev->u8DeviceLocator, " ", "DIMM 0");
        DMI_READ_CFG_STR_DEF(pMemDev->u8BankLocator, " ", "Bank 0");
        pMemDev->u8MemoryType            = 0x03; /* DRAM */
        pMemDev->u16TypeDetail           = 0;    /* Nothing special */
        pMemDev->u16Speed                = 1600; /* Unknown, shall be speed in MHz */
        DMI_READ_CFG_STR(pMemDev->u8Manufacturer, DmiSystemVendor);
        DMI_READ_CFG_STR_DEF(pMemDev->u8SerialNumber, " ", "00000000");
        DMI_READ_CFG_STR_DEF(pMemDev->u8AssetTag, " ", "00000000");
        DMI_READ_CFG_STR_DEF(pMemDev->u8PartNumber, " ", "00000000");
        pMemDev->u8Attributes            = 0; /* Unknown */
        DMI_TERM_STRUCT;

        /*****************************
         * DMI OEM strings (Type 11) *
         *****************************/
        PDMIOEMSTRINGS pOEMStrings    = (PDMIOEMSTRINGS)pszStr;
        DMI_CHECK_SIZE(sizeof(*pOEMStrings));
        DMI_START_STRUCT(pOEMStrings);
#ifdef VBOX_WITH_DMI_OEMSTRINGS
        pOEMStrings->header.u8Type    = 0xb; /* OEM Strings */
#else
        pOEMStrings->header.u8Type    = 126; /* inactive structure */
#endif
        pOEMStrings->header.u8Length  = sizeof(*pOEMStrings);
        pOEMStrings->header.u16Handle = 0x0002;
        pOEMStrings->u8Count          = 2;

        char szTmp[64];
        RTStrPrintf(szTmp, sizeof(szTmp), "vboxVer_%u.%u.%u",
                    RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild());
        DMI_READ_CFG_STR_DEF(pOEMStrings->u8VBoxVersion, "DmiOEMVBoxVer", szTmp);
        RTStrPrintf(szTmp, sizeof(szTmp), "vboxRev_%u", RTBldCfgRevision());
        DMI_READ_CFG_STR_DEF(pOEMStrings->u8VBoxRevision, "DmiOEMVBoxRev", szTmp);
        DMI_TERM_STRUCT;

        /*************************************
         * DMI OEM specific table (Type 128) *
         ************************************/
        PDMIOEMSPECIFIC pOEMSpecific = (PDMIOEMSPECIFIC)pszStr;
        DMI_CHECK_SIZE(sizeof(*pOEMSpecific));
        DMI_START_STRUCT(pOEMSpecific);
        pOEMSpecific->header.u8Type    = 0x80; /* OEM specific */
        pOEMSpecific->header.u8Length  = sizeof(*pOEMSpecific);
        pOEMSpecific->header.u16Handle = 0x0008; /* Just next free handle */
        pOEMSpecific->u32CpuFreqKHz    = RT_H2LE_U32((uint32_t)((uint64_t)TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns)) / 1000));
        DMI_TERM_STRUCT;

        /* End-of-table marker - includes padding to account for fixed table size. */
        PDMIHDR pEndOfTable          = (PDMIHDR)pszStr;
        pszStr                       = (char *)(pEndOfTable + 1);
        pEndOfTable->u8Type          = 0x7f;

        pEndOfTable->u8Length        = sizeof(*pEndOfTable);
        pEndOfTable->u16Handle       = 0xFEFF;
        *pcbDmiTables = ((uintptr_t)pszStr - (uintptr_t)pTable) + 2;

        /* We currently plant 10 DMI tables. Update this if tables number changed. */
        *pcNumDmiTables = 10;

        /* If more fields are added here, fix the size check in DMI_READ_CFG_STR */

        /* Success! */
        break;
    }

#undef DMI_READ_CFG_STR
#undef DMI_READ_CFG_S32
#undef DMI_CHECK_SIZE
    return VINF_SUCCESS;
}