/** * 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; }
/** * 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); }
/** * 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; }
/** * 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; }
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)); }
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); } }
/** * 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; }
/** * 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; }
/*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; }