/** * Registers a guest OS digger. * * This will instantiate an instance of the digger and add it * to the list for us in the next call to DBGFR3OSDetect(). * * @returns VBox status code. * @param pVM Pointer to the shared VM structure. * @param pReg The registration structure. * @thread Any. */ VMMR3DECL(int) DBGFR3OSRegister(PVM pVM, PCDBGFOSREG pReg) { /* * Validate intput. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pReg, VERR_INVALID_POINTER); AssertReturn(pReg->u32Magic == DBGFOSREG_MAGIC, VERR_INVALID_MAGIC); AssertReturn(pReg->u32EndMagic == DBGFOSREG_MAGIC, VERR_INVALID_MAGIC); AssertReturn(!pReg->fFlags, VERR_INVALID_PARAMETER); AssertReturn(pReg->cbData < _2G, VERR_INVALID_PARAMETER); AssertReturn(pReg->szName[0], VERR_INVALID_NAME); AssertReturn(RTStrEnd(&pReg->szName[0], sizeof(pReg->szName)), VERR_INVALID_NAME); AssertPtrReturn(pReg->pfnConstruct, VERR_INVALID_POINTER); AssertPtrNullReturn(pReg->pfnDestruct, VERR_INVALID_POINTER); AssertPtrReturn(pReg->pfnProbe, VERR_INVALID_POINTER); AssertPtrReturn(pReg->pfnInit, VERR_INVALID_POINTER); AssertPtrReturn(pReg->pfnRefresh, VERR_INVALID_POINTER); AssertPtrReturn(pReg->pfnTerm, VERR_INVALID_POINTER); AssertPtrReturn(pReg->pfnQueryVersion, VERR_INVALID_POINTER); AssertPtrReturn(pReg->pfnQueryInterface, VERR_INVALID_POINTER); /* * Pass it on to EMT(0). */ return VMR3ReqPriorityCallWait(pVM, 0 /*idDstCpu*/, (PFNRT)dbgfR3OSRegister, 2, pVM, pReg); }
/** * Query an address space by name. * * @returns Retained address space handle if found, NIL_RTDBGAS if not. * * @param pVM Pointer to the VM. * @param pszName The name. */ VMMR3DECL(RTDBGAS) DBGFR3AsQueryByName(PVM pVM, const char *pszName) { /* * Validate the input. */ VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_RTDBGAS); AssertPtrReturn(pszName, NIL_RTDBGAS); AssertReturn(*pszName, NIL_RTDBGAS); /* * Look it up in the string space and retain the result. */ RTDBGAS hDbgAs = NIL_RTDBGAS; DBGF_AS_DB_LOCK_READ(pVM); PRTSTRSPACECORE pNode = RTStrSpaceGet(&pVM->dbgf.s.AsNameSpace, pszName); if (pNode) { PDBGFASDBNODE pDbNode = RT_FROM_MEMBER(pNode, DBGFASDBNODE, NameCore); hDbgAs = (RTDBGAS)pDbNode->HandleCore.Key; uint32_t cRefs = RTDbgAsRetain(hDbgAs); if (RT_UNLIKELY(cRefs == UINT32_MAX)) hDbgAs = NIL_RTDBGAS; } DBGF_AS_DB_UNLOCK_READ(pVM); return hDbgAs; }
/** * Converts an address to a guest physical address. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_PARAMETER if the address is invalid. * @retval VERR_INVALID_STATE if the VM is being terminated or if the virtual * CPU handle is invalid. * @retval VERR_NOT_SUPPORTED is the type of address cannot be converted. * @retval VERR_PAGE_NOT_PRESENT * @retval VERR_PAGE_TABLE_NOT_PRESENT * @retval VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT * @retval VERR_PAGE_MAP_LEVEL4_NOT_PRESENT * * @param pVM The VM handle. * @param idCpu The ID of the CPU context to convert virtual * addresses. * @param pAddress The address. * @param pGCPhys Where to return the physical address. */ VMMR3DECL(int) DBGFR3AddrToPhys(PVM pVM, VMCPUID idCpu, PDBGFADDRESS pAddress, PRTGCPHYS pGCPhys) { /* * Parameter validation. */ AssertPtr(pGCPhys); *pGCPhys = NIL_RTGCPHYS; AssertPtr(pAddress); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), VERR_INVALID_PARAMETER); VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_STATE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_PARAMETER); /* * Convert by address type. */ int rc; if (pAddress->fFlags & DBGFADDRESS_FLAGS_HMA) rc = VERR_NOT_SUPPORTED; else if (pAddress->fFlags & DBGFADDRESS_FLAGS_PHYS) { *pGCPhys = pAddress->FlatPtr; rc = VINF_SUCCESS; } else { PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); if (VMCPU_IS_EMT(pVCpu)) rc = dbgfR3AddrToPhysOnVCpu(pVCpu, pAddress, pGCPhys); else rc = VMR3ReqCallWait(pVCpu->pVMR3, pVCpu->idCpu, (PFNRT)dbgfR3AddrToPhysOnVCpu, 3, pVCpu, pAddress, pGCPhys); } return rc; }
/** * Changes an alias to point to a new address space. * * Not all the aliases can be changed, currently it's only DBGF_AS_GLOBAL * and DBGF_AS_KERNEL. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param hAlias The alias to change. * @param hAliasFor The address space hAlias should be an alias for. * This can be an alias. The caller's reference to * this address space will NOT be consumed. */ VMMR3DECL(int) DBGFR3AsSetAlias(PVM pVM, RTDBGAS hAlias, RTDBGAS hAliasFor) { /* * Input validation. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertMsgReturn(DBGF_AS_IS_ALIAS(hAlias), ("%p\n", hAlias), VERR_INVALID_PARAMETER); AssertMsgReturn(!DBGF_AS_IS_FIXED_ALIAS(hAlias), ("%p\n", hAlias), VERR_INVALID_PARAMETER); RTDBGAS hRealAliasFor = DBGFR3AsResolveAndRetain(pVM, hAliasFor); if (hRealAliasFor == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Make sure the handle has is already in the database. */ int rc = VERR_NOT_FOUND; DBGF_AS_DB_LOCK_WRITE(pVM); if (RTAvlPVGet(&pVM->dbgf.s.AsHandleTree, hRealAliasFor)) { /* * Update the alias table and release the current address space. */ RTDBGAS hAsOld; ASMAtomicXchgHandle(&pVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(hAlias)], hRealAliasFor, &hAsOld); uint32_t cRefs = RTDbgAsRelease(hAsOld); Assert(cRefs > 0); Assert(cRefs != UINT32_MAX); NOREF(cRefs); rc = VINF_SUCCESS; } DBGF_AS_DB_UNLOCK_WRITE(pVM); return rc; }
/** * Resolves the address space handle into a real handle if it's an alias, * and retains whatever it is. * * @returns Real address space handle. NIL_RTDBGAS if invalid handle. * * @param pVM Pointer to the VM. * @param hAlias The possibly address space alias. */ VMMR3DECL(RTDBGAS) DBGFR3AsResolveAndRetain(PVM pVM, RTDBGAS hAlias) { VM_ASSERT_VALID_EXT_RETURN(pVM, NULL); AssertCompileNS(NIL_RTDBGAS == (RTDBGAS)0); uint32_t cRefs; uintptr_t iAlias = DBGF_AS_ALIAS_2_INDEX(hAlias); if (iAlias < DBGF_AS_COUNT) { if (DBGF_AS_IS_FIXED_ALIAS(hAlias)) { /* Perform lazy address space population. */ if (!pVM->dbgf.s.afAsAliasPopuplated[iAlias]) dbgfR3AsLazyPopulate(pVM, hAlias); /* Won't ever change, no need to grab the lock. */ hAlias = pVM->dbgf.s.ahAsAliases[iAlias]; cRefs = RTDbgAsRetain(hAlias); } else { /* May change, grab the lock so we can read it safely. */ DBGF_AS_DB_LOCK_READ(pVM); hAlias = pVM->dbgf.s.ahAsAliases[iAlias]; cRefs = RTDbgAsRetain(hAlias); DBGF_AS_DB_UNLOCK_READ(pVM); } } else /* Not an alias, just retain it. */ cRefs = RTDbgAsRetain(hAlias); return cRefs != UINT32_MAX ? hAlias : NIL_RTDBGAS; }
/** * Deregisters a guest OS digger previously registered by DBGFR3OSRegister. * * @returns VBox status code. * * @param pVM Pointer to the shared VM structure. * @param pReg The registration structure. * @thread Any. */ VMMR3DECL(int) DBGFR3OSDeregister(PVM pVM, PCDBGFOSREG pReg) { /* * Validate input. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pReg, VERR_INVALID_POINTER); AssertReturn(pReg->u32Magic == DBGFOSREG_MAGIC, VERR_INVALID_MAGIC); AssertReturn(pReg->u32EndMagic == DBGFOSREG_MAGIC, VERR_INVALID_MAGIC); AssertReturn(RTStrEnd(&pReg->szName[0], sizeof(pReg->szName)), VERR_INVALID_NAME); DBGF_OS_READ_LOCK(pVM); PDBGFOS pOS; for (pOS = pVM->dbgf.s.pOSHead; pOS; pOS = pOS->pNext) if (pOS->pReg == pReg) break; DBGF_OS_READ_LOCK(pVM); if (!pOS) { Log(("DBGFR3OSDeregister: %s -> VERR_NOT_FOUND\n", pReg->szName)); return VERR_NOT_FOUND; } /* * Pass it on to EMT(0). */ return VMR3ReqPriorityCallWait(pVM, 0 /*idDstCpu*/, (PFNRT)dbgfR3OSDeregister, 2, pVM, pReg); }
/** * Converts an address to a host physical address. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_PARAMETER if the address is invalid. * @retval VERR_INVALID_STATE if the VM is being terminated or if the virtual * CPU handle is invalid. * @retval VERR_NOT_SUPPORTED is the type of address cannot be converted. * @retval VERR_PAGE_NOT_PRESENT * @retval VERR_PAGE_TABLE_NOT_PRESENT * @retval VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT * @retval VERR_PAGE_MAP_LEVEL4_NOT_PRESENT * @retval VERR_PGM_PHYS_PAGE_RESERVED * @retval VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS * * @param pVM The VM handle. * @param idCpu The ID of the CPU context to convert virtual * addresses. * @param pAddress The address. * @param pHCPhys Where to return the physical address. */ VMMR3DECL(int) DBGFR3AddrToHostPhys(PVM pVM, VMCPUID idCpu, PDBGFADDRESS pAddress, PRTHCPHYS pHCPhys) { /* * Parameter validation. */ AssertPtr(pHCPhys); *pHCPhys = NIL_RTHCPHYS; AssertPtr(pAddress); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), VERR_INVALID_PARAMETER); VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_STATE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_PARAMETER); /* * Convert it if we can. */ int rc; if (pAddress->fFlags & DBGFADDRESS_FLAGS_HMA) rc = VERR_NOT_SUPPORTED; /** @todo implement this */ else { RTGCPHYS GCPhys; rc = DBGFR3AddrToPhys(pVM, idCpu, pAddress, &GCPhys); if (RT_SUCCESS(rc)) rc = PGMPhysGCPhys2HCPhys(pVM, pAddress->FlatPtr, pHCPhys); } return rc; }
/** * Query an address space by process ID. * * @returns Retained address space handle if found, NIL_RTDBGAS if not. * * @param pVM Pointer to the VM. * @param ProcId The process ID. */ VMMR3DECL(RTDBGAS) DBGFR3AsQueryByPid(PVM pVM, RTPROCESS ProcId) { /* * Validate the input. */ VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_RTDBGAS); AssertReturn(ProcId != NIL_RTPROCESS, NIL_RTDBGAS); /* * Look it up in the PID tree and retain the result. */ RTDBGAS hDbgAs = NIL_RTDBGAS; DBGF_AS_DB_LOCK_READ(pVM); PAVLU32NODECORE pNode = RTAvlU32Get(&pVM->dbgf.s.AsPidTree, ProcId); if (pNode) { PDBGFASDBNODE pDbNode = RT_FROM_MEMBER(pNode, DBGFASDBNODE, PidCore); hDbgAs = (RTDBGAS)pDbNode->HandleCore.Key; uint32_t cRefs = RTDbgAsRetain(hDbgAs); if (RT_UNLIKELY(cRefs == UINT32_MAX)) hDbgAs = NIL_RTDBGAS; } DBGF_AS_DB_UNLOCK_READ(pVM); return hDbgAs; }
/** * Scan guest memory for an exact byte string. * * @returns VBox status codes: * @retval VINF_SUCCESS and *pGCPtrHit on success. * @retval VERR_DBGF_MEM_NOT_FOUND if not found. * @retval VERR_INVALID_POINTER if any of the pointer arguments are invalid. * @retval VERR_INVALID_ARGUMENT if any other arguments are invalid. * * @param pVM The VM handle. * @param idCpu The ID of the CPU context to search in. * @param pAddress Where to store the mixed address. * @param cbRange The number of bytes to scan. * @param uAlign The alignment restriction imposed on the result. * Usually set to 1. * @param pvNeedle What to search for - exact search. * @param cbNeedle Size of the search byte string. * @param pHitAddress Where to put the address of the first hit. * * @thread Any thread. */ VMMR3DECL(int) DBGFR3MemScan(PVM pVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, RTGCUINTPTR cbRange, RTGCUINTPTR uAlign, const void *pvNeedle, size_t cbNeedle, PDBGFADDRESS pHitAddress) { VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); return VMR3ReqPriorityCallWait(pVM, idCpu, (PFNRT)dbgfR3MemScan, 8, pVM, idCpu, pAddress, &cbRange, &uAlign, pvNeedle, cbNeedle, pHitAddress); }
/** * Worker for DBGFR3SelQueryInfo that calls into SELM. */ static DECLCALLBACK(int) dbgfR3SelQueryInfo(PUVM pUVM, VMCPUID idCpu, RTSEL Sel, uint32_t fFlags, PDBGFSELINFO pSelInfo) { PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); /* * Make the query. */ int rc; if (!(fFlags & DBGFSELQI_FLAGS_DT_SHADOW)) { PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); VMCPU_ASSERT_EMT(pVCpu); rc = SELMR3GetSelectorInfo(pVM, pVCpu, Sel, pSelInfo); /* * 64-bit mode HACKS for making data and stack selectors wide open when * queried. This is voodoo magic. */ if (fFlags & DBGFSELQI_FLAGS_DT_ADJ_64BIT_MODE) { /* Expand 64-bit data and stack selectors. The check is a bit bogus... */ if ( RT_SUCCESS(rc) && (pSelInfo->fFlags & ( DBGFSELINFO_FLAGS_LONG_MODE | DBGFSELINFO_FLAGS_REAL_MODE | DBGFSELINFO_FLAGS_PROT_MODE | DBGFSELINFO_FLAGS_GATE | DBGFSELINFO_FLAGS_HYPER | DBGFSELINFO_FLAGS_INVALID | DBGFSELINFO_FLAGS_NOT_PRESENT)) == DBGFSELINFO_FLAGS_LONG_MODE && pSelInfo->cbLimit != ~(RTGCPTR)0 && CPUMIsGuestIn64BitCode(pVCpu) ) { pSelInfo->GCPtrBase = 0; pSelInfo->cbLimit = ~(RTGCPTR)0; } else if ( Sel == 0 && CPUMIsGuestIn64BitCode(pVCpu)) { pSelInfo->GCPtrBase = 0; pSelInfo->cbLimit = ~(RTGCPTR)0; pSelInfo->Sel = 0; pSelInfo->SelGate = 0; pSelInfo->fFlags = DBGFSELINFO_FLAGS_LONG_MODE; pSelInfo->u.Raw64.Gen.u1Present = 1; pSelInfo->u.Raw64.Gen.u1Long = 1; pSelInfo->u.Raw64.Gen.u1DescType = 1; rc = VINF_SUCCESS; } } } else { if (HMIsEnabled(pVM)) rc = VERR_INVALID_STATE; else rc = SELMR3GetShadowSelectorInfo(pVM, Sel, pSelInfo); } return rc; }
/** * Resolves the address space handle into a real handle if it's an alias. * * @returns Real address space handle. NIL_RTDBGAS if invalid handle. * * @param pVM Pointer to the VM. * @param hAlias The possibly address space alias. * * @remarks Doesn't take any locks. */ VMMR3DECL(RTDBGAS) DBGFR3AsResolve(PVM pVM, RTDBGAS hAlias) { VM_ASSERT_VALID_EXT_RETURN(pVM, NULL); AssertCompileNS(NIL_RTDBGAS == (RTDBGAS)0); uintptr_t iAlias = DBGF_AS_ALIAS_2_INDEX(hAlias); if (iAlias < DBGF_AS_COUNT) ASMAtomicReadHandle(&pVM->dbgf.s.ahAsAliases[iAlias], &hAlias); return hAlias; }
/** * Attaches a debugger to the specified VM. * * Only one debugger at a time. * * @returns VBox status code. * @param pVM Pointer to the VM. */ VMMR3DECL(int) DBGFR3Attach(PVM pVM) { VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); /* * Call the VM, use EMT for serialization. */ /** @todo SMP */ return VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)dbgfR3Attach, 1, pVM); }
/** * Scan guest memory for an exact byte string. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param idCpu The ID of the CPU context to search in. * @param pAddress Where to store the mixed address. * @param puAlign The alignment restriction imposed on the search result. * @param pcbRange The number of bytes to scan. Passed as a pointer because * it may be 64-bit. * @param pabNeedle What to search for - exact search. * @param cbNeedle Size of the search byte string. * @param pHitAddress Where to put the address of the first hit. */ static DECLCALLBACK(int) dbgfR3MemScan(PUVM pUVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, PCRTGCUINTPTR pcbRange, RTGCUINTPTR *puAlign, const uint8_t *pabNeedle, size_t cbNeedle, PDBGFADDRESS pHitAddress) { PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); Assert(idCpu == VMMGetCpuId(pVM)); /* * Validate the input we use, PGM does the rest. */ RTGCUINTPTR cbRange = *pcbRange; if (!DBGFR3AddrIsValid(pUVM, pAddress)) return VERR_INVALID_POINTER; if (!VALID_PTR(pHitAddress)) return VERR_INVALID_POINTER; if (DBGFADDRESS_IS_HMA(pAddress)) return VERR_INVALID_POINTER; /* * Select DBGF worker by addressing mode. */ int rc; PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); PGMMODE enmMode = PGMGetGuestMode(pVCpu); if ( enmMode == PGMMODE_REAL || enmMode == PGMMODE_PROTECTED || DBGFADDRESS_IS_PHYS(pAddress) ) { RTGCPHYS GCPhysAlign = *puAlign; if (GCPhysAlign != *puAlign) return VERR_OUT_OF_RANGE; RTGCPHYS PhysHit; rc = PGMR3DbgScanPhysical(pVM, pAddress->FlatPtr, cbRange, GCPhysAlign, pabNeedle, cbNeedle, &PhysHit); if (RT_SUCCESS(rc)) DBGFR3AddrFromPhys(pUVM, pHitAddress, PhysHit); } else { #if GC_ARCH_BITS > 32 if ( ( pAddress->FlatPtr >= _4G || pAddress->FlatPtr + cbRange > _4G) && enmMode != PGMMODE_AMD64 && enmMode != PGMMODE_AMD64_NX) return VERR_DBGF_MEM_NOT_FOUND; #endif RTGCUINTPTR GCPtrHit; rc = PGMR3DbgScanVirtual(pVM, pVCpu, pAddress->FlatPtr, cbRange, *puAlign, pabNeedle, cbNeedle, &GCPtrHit); if (RT_SUCCESS(rc)) DBGFR3AddrFromFlat(pUVM, pHitAddress, GCPtrHit); } return rc; }
/** * Read guest memory. * * @returns VBox status code. * * @param pVM Pointer to the shared VM structure. * @param idCpu The ID of the source CPU context (for the address). * @param pAddress Where to start reading. * @param pvBuf Where to store the data we've read. * @param cbRead The number of bytes to read. */ VMMR3DECL(int) DBGFR3MemRead(PVM pVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, void *pvBuf, size_t cbRead) { VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); if ((pAddress->fFlags & DBGFADDRESS_FLAGS_TYPE_MASK) == DBGFADDRESS_FLAGS_RING0) { AssertCompile(sizeof(RTHCUINTPTR) <= sizeof(pAddress->FlatPtr)); return VMMR3ReadR0Stack(pVM, idCpu, (RTHCUINTPTR)pAddress->FlatPtr, pvBuf, cbRead); } return VMR3ReqPriorityCallWait(pVM, idCpu, (PFNRT)dbgfR3MemRead, 5, pVM, idCpu, pAddress, pvBuf, cbRead); }
/** * Get the current CPU mode. * * @returns The CPU mode on success, CPUMMODE_INVALID on failure. * @param pUVM The user mode VM handle. * @param idCpu The target CPU ID. */ VMMR3DECL(CPUMMODE) DBGFR3CpuGetMode(PUVM pUVM, VMCPUID idCpu) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, CPUMMODE_INVALID); VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, CPUMMODE_INVALID); AssertReturn(idCpu < pUVM->pVM->cCpus, CPUMMODE_INVALID); CPUMMODE enmMode; int rc = VMR3ReqPriorityCallWaitU(pUVM, idCpu, (PFNRT)dbgfR3CpuGetMode, 3, pUVM->pVM, idCpu, &enmMode); if (RT_FAILURE(rc)) return CPUMMODE_INVALID; return enmMode; }
/** * Checks if the given CPU is executing 64-bit code or not. * * @returns true / false accordingly. * @param pUVM The user mode VM handle. * @param idCpu The target CPU ID. */ VMMR3DECL(bool) DBGFR3CpuIsIn64BitCode(PUVM pUVM, VMCPUID idCpu) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, false); VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, false); AssertReturn(idCpu < pUVM->pVM->cCpus, false); bool fIn64BitCode; int rc = VMR3ReqPriorityCallWaitU(pUVM, idCpu, (PFNRT)dbgfR3CpuIn64BitCode, 3, pUVM->pVM, idCpu, &fIn64BitCode); if (RT_FAILURE(rc)) return false; return fIn64BitCode; }
/** * Query the trace configuration specification string. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_VM_HANDLE * @retval VERR_INVALID_POINTER * @retval VERR_BUFFER_OVERFLOW if the buffer is too small. Buffer will be * empty. * @param pVM The cross context VM structure. * @param pszConfig Pointer to the output buffer. * @param cbConfig The size of the output buffer. */ VMMDECL(int) DBGFR3TraceQueryConfig(PVM pVM, char *pszConfig, size_t cbConfig) { VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pszConfig, VERR_INVALID_POINTER); if (cbConfig < 1) return VERR_BUFFER_OVERFLOW; *pszConfig = '\0'; if (pVM->hTraceBufR3 == NIL_RTTRACEBUF) return VERR_DBGF_NO_TRACE_BUFFER; int rc = VINF_SUCCESS; uint32_t const fTraceGroups = pVM->aCpus[0].fTraceGroups; if ( fTraceGroups == UINT32_MAX && PDMR3TracingAreAll(pVM, true /*fEnabled*/)) rc = RTStrCopy(pszConfig, cbConfig, "all"); else if ( fTraceGroups == 0 && PDMR3TracingAreAll(pVM, false /*fEnabled*/)) rc = RTStrCopy(pszConfig, cbConfig, "-all"); else { char *pszDst = pszConfig; size_t cbDst = cbConfig; uint32_t i = RT_ELEMENTS(g_aVmmTpGroups); while (i-- > 0) if (g_aVmmTpGroups[i].fMask & fTraceGroups) { size_t cchThis = g_aVmmTpGroups[i].cchName + (pszDst != pszConfig); if (cchThis >= cbDst) { rc = VERR_BUFFER_OVERFLOW; break; } if (pszDst != pszConfig) { *pszDst = ' '; memcpy(pszDst + 1, g_aVmmTpGroups[i].pszName, g_aVmmTpGroups[i].cchName + 1); } else memcpy(pszDst, g_aVmmTpGroups[i].pszName, g_aVmmTpGroups[i].cchName + 1); pszDst += cchThis; cbDst -= cchThis; } if (RT_SUCCESS(rc)) rc = PDMR3TracingQueryConfig(pVM, pszDst, cbDst); } if (RT_FAILURE(rc)) *pszConfig = '\0'; return rc; }
/** * Common worker for DBGFR3StackWalkBeginGuestEx, DBGFR3StackWalkBeginHyperEx, * DBGFR3StackWalkBeginGuest and DBGFR3StackWalkBeginHyper. */ static int dbgfR3StackWalkBeginCommon(PUVM pUVM, VMCPUID idCpu, DBGFCODETYPE enmCodeType, PCDBGFADDRESS pAddrFrame, PCDBGFADDRESS pAddrStack, PCDBGFADDRESS pAddrPC, DBGFRETURNTYPE enmReturnType, PCDBGFSTACKFRAME *ppFirstFrame) { /* * Validate parameters. */ *ppFirstFrame = NULL; UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); if (pAddrFrame) AssertReturn(DBGFR3AddrIsValid(pUVM, pAddrFrame), VERR_INVALID_PARAMETER); if (pAddrStack) AssertReturn(DBGFR3AddrIsValid(pUVM, pAddrStack), VERR_INVALID_PARAMETER); if (pAddrPC) AssertReturn(DBGFR3AddrIsValid(pUVM, pAddrPC), VERR_INVALID_PARAMETER); AssertReturn(enmReturnType >= DBGFRETURNTYPE_INVALID && enmReturnType < DBGFRETURNTYPE_END, VERR_INVALID_PARAMETER); /* * Get the CPUM context pointer and pass it on the specified EMT. */ RTDBGAS hAs; PCCPUMCTXCORE pCtxCore; switch (enmCodeType) { case DBGFCODETYPE_GUEST: pCtxCore = CPUMGetGuestCtxCore(VMMGetCpuById(pVM, idCpu)); hAs = DBGF_AS_GLOBAL; break; case DBGFCODETYPE_HYPER: pCtxCore = CPUMGetHyperCtxCore(VMMGetCpuById(pVM, idCpu)); hAs = DBGF_AS_RC_AND_GC_GLOBAL; break; case DBGFCODETYPE_RING0: pCtxCore = NULL; /* No valid context present. */ hAs = DBGF_AS_R0; break; default: AssertFailedReturn(VERR_INVALID_PARAMETER); } return VMR3ReqPriorityCallWaitU(pUVM, idCpu, (PFNRT)dbgfR3StackWalkCtxFull, 10, pUVM, idCpu, pCtxCore, hAs, enmCodeType, pAddrFrame, pAddrStack, pAddrPC, enmReturnType, ppFirstFrame); }
/** * Read guest memory. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param idCpu The ID of the CPU context to read memory from. * @param pAddress Where to start reading. * @param pvBuf Where to store the data we've read. * @param cbRead The number of bytes to read. */ static DECLCALLBACK(int) dbgfR3MemRead(PUVM pUVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, void *pvBuf, size_t cbRead) { PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); Assert(idCpu == VMMGetCpuId(pVM)); /* * Validate the input we use, PGM does the rest. */ if (!DBGFR3AddrIsValid(pUVM, pAddress)) return VERR_INVALID_POINTER; if (!VALID_PTR(pvBuf)) return VERR_INVALID_POINTER; /* * HMA is special */ int rc; if (DBGFADDRESS_IS_HMA(pAddress)) { if (DBGFADDRESS_IS_PHYS(pAddress)) rc = VERR_INVALID_POINTER; else rc = MMR3HyperReadGCVirt(pVM, pvBuf, pAddress->FlatPtr, cbRead); } else { /* * Select DBGF worker by addressing mode. */ PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); PGMMODE enmMode = PGMGetGuestMode(pVCpu); if ( enmMode == PGMMODE_REAL || enmMode == PGMMODE_PROTECTED || DBGFADDRESS_IS_PHYS(pAddress) ) rc = PGMPhysSimpleReadGCPhys(pVM, pvBuf, pAddress->FlatPtr, cbRead); else { #if GC_ARCH_BITS > 32 if ( ( pAddress->FlatPtr >= _4G || pAddress->FlatPtr + cbRead > _4G) && enmMode != PGMMODE_AMD64 && enmMode != PGMMODE_AMD64_NX) return VERR_PAGE_TABLE_NOT_PRESENT; #endif rc = PGMPhysSimpleReadGCPtr(pVCpu, pvBuf, pAddress->FlatPtr, cbRead); } } return rc; }
/** * EMT worker for DBGFR3LogModifyGroups. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param pszGroupSettings The group settings string. (VBOX_LOG) */ static DECLCALLBACK(int) dbgfR3LogModifyGroups(PUVM pUVM, const char *pszGroupSettings) { PRTLOGGER pLogger = dbgfR3LogResolvedLogger(&pszGroupSettings); if (!pLogger) return VINF_SUCCESS; int rc = RTLogGroupSettings(pLogger, pszGroupSettings); if (RT_SUCCESS(rc) && pUVM->pVM) { VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE); rc = VMMR3UpdateLoggers(pUVM->pVM); } return rc; }
/** * Delete an address space from the database. * * The address space must not be engaged as any of the standard aliases. * * @returns VBox status code. * @retval VERR_SHARING_VIOLATION if in use as an alias. * @retval VERR_NOT_FOUND if not found in the address space database. * * @param pVM Pointer to the VM. * @param hDbgAs The address space handle. Aliases are not allowed. */ VMMR3DECL(int) DBGFR3AsDelete(PVM pVM, RTDBGAS hDbgAs) { /* * Input validation. Retain the address space so it can be released outside * the lock as well as validated. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); if (hDbgAs == NIL_RTDBGAS) return VINF_SUCCESS; uint32_t cRefs = RTDbgAsRetain(hDbgAs); if (cRefs == UINT32_MAX) return VERR_INVALID_HANDLE; RTDbgAsRelease(hDbgAs); DBGF_AS_DB_LOCK_WRITE(pVM); /* * You cannot delete any of the aliases. */ for (size_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.ahAsAliases); i++) if (pVM->dbgf.s.ahAsAliases[i] == hDbgAs) { DBGF_AS_DB_UNLOCK_WRITE(pVM); return VERR_SHARING_VIOLATION; } /* * Ok, try remove it from the database. */ PDBGFASDBNODE pDbNode = (PDBGFASDBNODE)RTAvlPVRemove(&pVM->dbgf.s.AsHandleTree, hDbgAs); if (!pDbNode) { DBGF_AS_DB_UNLOCK_WRITE(pVM); return VERR_NOT_FOUND; } RTStrSpaceRemove(&pVM->dbgf.s.AsNameSpace, pDbNode->NameCore.pszString); if (pDbNode->PidCore.Key != NIL_RTPROCESS) RTAvlU32Remove(&pVM->dbgf.s.AsPidTree, pDbNode->PidCore.Key); DBGF_AS_DB_UNLOCK_WRITE(pVM); /* * Free the resources. */ RTDbgAsRelease(hDbgAs); MMR3HeapFree(pDbNode); return VINF_SUCCESS; }
/** * Query a symbol by name. * * The symbol can be prefixed by a module name pattern to scope the search. The * pattern is a simple string pattern with '*' and '?' as wild chars. See * RTStrSimplePatternMatch(). * * @returns VBox status code. See RTDbgAsSymbolByAddr. * * @param pVM Pointer to the VM. * @param hDbgAs The address space handle. * @param pszSymbol The symbol to search for, maybe prefixed by a * module pattern. * @param pSymbol Where to return the symbol information. * The returned symbol name will be prefixed by * the module name as far as space allows. * @param phMod Where to return the module handle. Optional. */ VMMR3DECL(int) DBGFR3AsSymbolByName(PVM pVM, RTDBGAS hDbgAs, const char *pszSymbol, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod) { /* * Implement the special address space aliases the lazy way. */ if (hDbgAs == DBGF_AS_RC_AND_GC_GLOBAL) { int rc = DBGFR3AsSymbolByName(pVM, DBGF_AS_RC, pszSymbol, pSymbol, phMod); if (RT_FAILURE(rc)) rc = DBGFR3AsSymbolByName(pVM, DBGF_AS_GLOBAL, pszSymbol, pSymbol, phMod); return rc; } /* * Input validation. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pSymbol, VERR_INVALID_POINTER); AssertPtrNullReturn(phMod, VERR_INVALID_POINTER); if (phMod) *phMod = NIL_RTDBGMOD; RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the lookup. */ RTDBGMOD hMod; int rc = RTDbgAsSymbolByName(hRealAS, pszSymbol, pSymbol, &hMod); if (RT_SUCCESS(rc)) { dbgfR3AsSymbolJoinNames(pSymbol, hMod); if (!phMod) RTDbgModRelease(hMod); } /* Temporary conversion. */ else if (hDbgAs == DBGF_AS_GLOBAL) { DBGFSYMBOL DbgfSym; rc = DBGFR3SymbolByName(pVM, pszSymbol, &DbgfSym); if (RT_SUCCESS(rc)) dbgfR3AsSymbolConvert(pSymbol, &DbgfSym); } return rc; }
/** * Wakes up a CPU that has called VMR3WaitForDeviceReady. * * @returns VBox error status (never informational statuses). * @param pVM The VM handle. * @param idCpu The id of the calling EMT. */ VMMR3DECL(int) VMR3NotifyCpuDeviceReady(PVM pVM, VMCPUID idCpu) { /* * Validate caller and resolve the CPU ID. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); PVMCPU pVCpu = &pVM->aCpus[idCpu]; /* * Pretend it was an FF that got set since we've got logic for that already. */ VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM); return VINF_SUCCESS; }
/** * Gets information about a selector. * * Intended for the debugger mostly and will prefer the guest * descriptor tables over the shadow ones. * * @returns VBox status code, the following are the common ones. * @retval VINF_SUCCESS on success. * @retval VERR_INVALID_SELECTOR if the selector isn't fully inside the * descriptor table. * @retval VERR_SELECTOR_NOT_PRESENT if the LDT is invalid or not present. This * is not returned if the selector itself isn't present, you have to * check that for yourself (see DBGFSELINFO::fFlags). * @retval VERR_PAGE_TABLE_NOT_PRESENT or VERR_PAGE_NOT_PRESENT if the * pagetable or page backing the selector table wasn't present. * * @param pVM VM handle. * @param idCpu The ID of the virtual CPU context. * @param Sel The selector to get info about. * @param fFlags Flags, see DBGFQSEL_FLAGS_*. * @param pSelInfo Where to store the information. This will always be * updated. * * @remarks This is a wrapper around SELMR3GetSelectorInfo and * SELMR3GetShadowSelectorInfo. */ VMMR3DECL(int) DBGFR3SelQueryInfo(PVM pVM, VMCPUID idCpu, RTSEL Sel, uint32_t fFlags, PDBGFSELINFO pSelInfo) { VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); AssertReturn(!(fFlags & ~(DBGFSELQI_FLAGS_DT_GUEST | DBGFSELQI_FLAGS_DT_SHADOW | DBGFSELQI_FLAGS_DT_ADJ_64BIT_MODE)), VERR_INVALID_PARAMETER); AssertReturn( (fFlags & (DBGFSELQI_FLAGS_DT_SHADOW | DBGFSELQI_FLAGS_DT_ADJ_64BIT_MODE)) != (DBGFSELQI_FLAGS_DT_SHADOW | DBGFSELQI_FLAGS_DT_ADJ_64BIT_MODE), VERR_INVALID_PARAMETER); /* Clear the return data here on this thread. */ memset(pSelInfo, 0, sizeof(*pSelInfo)); /* * Dispatch the request to a worker running on the target CPU. */ return VMR3ReqPriorityCallWait(pVM, idCpu, (PFNRT)dbgfR3SelQueryInfo, 5, pVM, idCpu, Sel, fFlags, pSelInfo); }
/** * Writes guest memory. * * @returns VBox status code. * * @param pUVM The user mode VM handle. * @param idCpu The ID of the target CPU context (for the address). * @param pAddress Where to start writing. * @param pvBuf The data to write. * @param cbWrite The number of bytes to write. */ static DECLCALLBACK(int) dbgfR3MemWrite(PUVM pUVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, void const *pvBuf, size_t cbWrite) { /* * Validate the input we use, PGM does the rest. */ if (!DBGFR3AddrIsValid(pUVM, pAddress)) return VERR_INVALID_POINTER; if (!VALID_PTR(pvBuf)) return VERR_INVALID_POINTER; PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); /* * HMA is always special. */ int rc; if (DBGFADDRESS_IS_HMA(pAddress)) { /** @todo write to HMA. */ rc = VERR_ACCESS_DENIED; } else { /* * Select PGM function by addressing mode. */ PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); PGMMODE enmMode = PGMGetGuestMode(pVCpu); if ( enmMode == PGMMODE_REAL || enmMode == PGMMODE_PROTECTED || DBGFADDRESS_IS_PHYS(pAddress) ) rc = PGMPhysSimpleWriteGCPhys(pVM, pAddress->FlatPtr, pvBuf, cbWrite); else { #if GC_ARCH_BITS > 32 if ( ( pAddress->FlatPtr >= _4G || pAddress->FlatPtr + cbWrite > _4G) && enmMode != PGMMODE_AMD64 && enmMode != PGMMODE_AMD64_NX) return VERR_PAGE_TABLE_NOT_PRESENT; #endif rc = PGMPhysSimpleWriteGCPtr(pVCpu, pAddress->FlatPtr, pvBuf, cbWrite); } } return rc; }
/** * Converts an address to a volatile host virtual address. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_PARAMETER if the address is invalid. * @retval VERR_INVALID_STATE if the VM is being terminated or if the virtual * CPU handle is invalid. * @retval VERR_NOT_SUPPORTED is the type of address cannot be converted. * @retval VERR_PAGE_NOT_PRESENT * @retval VERR_PAGE_TABLE_NOT_PRESENT * @retval VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT * @retval VERR_PAGE_MAP_LEVEL4_NOT_PRESENT * @retval VERR_PGM_PHYS_PAGE_RESERVED * @retval VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS * * @param pVM The VM handle. * @param idCpu The ID of the CPU context to convert virtual * addresses. * @param pAddress The address. * @param fReadOnly Whether returning a read-only page is fine or not. * If set to thru the page may have to be made writable * before we return. * @param ppvR3Ptr Where to return the address. */ VMMR3DECL(int) DBGFR3AddrToVolatileR3Ptr(PVM pVM, VMCPUID idCpu, PDBGFADDRESS pAddress, bool fReadOnly, void **ppvR3Ptr) { /* * Parameter validation. */ AssertPtr(ppvR3Ptr); *ppvR3Ptr = NULL; AssertPtr(pAddress); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), VERR_INVALID_PARAMETER); VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_STATE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_PARAMETER); /* * Convert it. */ return VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3AddrToVolatileR3PtrOnVCpu, 5, pVM, idCpu, pAddress, fReadOnly, ppvR3Ptr); }
/** * Read a zero terminated string from guest memory. * * @returns VBox status code. * * @param pVM Pointer to the shared VM structure. * @param idCpu The ID of the source CPU context (for the address). * @param pAddress Where to start reading. * @param pszBuf Where to store the string. * @param cchBuf The size of the buffer. */ VMMR3DECL(int) DBGFR3MemReadString(PVM pVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, char *pszBuf, size_t cchBuf) { /* * Validate and zero output. */ if (!VALID_PTR(pszBuf)) return VERR_INVALID_POINTER; if (cchBuf <= 0) return VERR_INVALID_PARAMETER; memset(pszBuf, 0, cchBuf); VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); /* * Pass it on to the EMT. */ return VMR3ReqPriorityCallWait(pVM, idCpu, (PFNRT)dbgfR3MemReadString, 5, pVM, idCpu, pAddress, pszBuf, cchBuf); }
/** * Special interface for implementing a HLT-like port on a device. * * This can be called directly from device code, provide the device is trusted * to access the VMM directly. Since we may not have an accurate register set * and the caller certainly shouldn't (device code does not access CPU * registers), this function will return when interrupts are pending regardless * of the actual EFLAGS.IF state. * * @returns VBox error status (never informational statuses). * @param pVM The VM handle. * @param idCpu The id of the calling EMT. */ VMMR3DECL(int) VMR3WaitForDeviceReady(PVM pVM, VMCPUID idCpu) { /* * Validate caller and resolve the CPU ID. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID); PVMCPU pVCpu = &pVM->aCpus[idCpu]; VMCPU_ASSERT_EMT_RETURN(pVCpu, VERR_VM_THREAD_NOT_EMT); /* * Tag along with the HLT mechanics for now. */ int rc = VMR3WaitHalted(pVM, pVCpu, false /*fIgnoreInterrupts*/); if (RT_SUCCESS(rc)) return VINF_SUCCESS; return rc; }
/** * Adds the address space to the database. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param hDbgAs The address space handle. The reference of the * caller will NOT be consumed. * @param ProcId The process id or NIL_RTPROCESS. */ VMMR3DECL(int) DBGFR3AsAdd(PVM pVM, RTDBGAS hDbgAs, RTPROCESS ProcId) { /* * Input validation. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); const char *pszName = RTDbgAsName(hDbgAs); if (!pszName) return VERR_INVALID_HANDLE; uint32_t cRefs = RTDbgAsRetain(hDbgAs); if (cRefs == UINT32_MAX) return VERR_INVALID_HANDLE; /* * Allocate a tracking node. */ int rc = VERR_NO_MEMORY; PDBGFASDBNODE pDbNode = (PDBGFASDBNODE)MMR3HeapAlloc(pVM, MM_TAG_DBGF_AS, sizeof(*pDbNode)); if (pDbNode) { pDbNode->HandleCore.Key = hDbgAs; pDbNode->PidCore.Key = ProcId; pDbNode->NameCore.pszString = pszName; pDbNode->NameCore.cchString = strlen(pszName); DBGF_AS_DB_LOCK_WRITE(pVM); if (RTStrSpaceInsert(&pVM->dbgf.s.AsNameSpace, &pDbNode->NameCore)) { if (RTAvlPVInsert(&pVM->dbgf.s.AsHandleTree, &pDbNode->HandleCore)) { DBGF_AS_DB_UNLOCK_WRITE(pVM); return VINF_SUCCESS; } /* bail out */ RTStrSpaceRemove(&pVM->dbgf.s.AsNameSpace, pszName); } DBGF_AS_DB_UNLOCK_WRITE(pVM); MMR3HeapFree(pDbNode); } RTDbgAsRelease(hDbgAs); return rc; }
/** * @callback_method_impl{FNDBGCCMD, The '.pgmsharedmodules' command.} */ DECLCALLBACK(int) pgmR3CmdShowSharedModules(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs) { NOREF(pCmd); NOREF(paArgs); NOREF(cArgs); PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); pgmLock(pVM); for (unsigned i = 0; i < RT_ELEMENTS(g_apSharedModules); i++) { if (g_apSharedModules[i]) { pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Shared module %s (%s):\n", g_apSharedModules[i]->szName, g_apSharedModules[i]->szVersion); for (unsigned j = 0; j < g_apSharedModules[i]->cRegions; j++) pCmdHlp->pfnPrintf(pCmdHlp, NULL, "--- Region %d: base %RGv size %x\n", j, g_apSharedModules[i]->aRegions[j].GCRegionAddr, g_apSharedModules[i]->aRegions[j].cbRegion); } } pgmUnlock(pVM); return VINF_SUCCESS; }