/** * The GC entry point. * * @returns VBox status code. * @param pVM The VM to operate on. * @param uOperation Which operation to execute (VMMGCOPERATION). * @param uArg Argument to that operation. */ VMMRCDECL(int) VMMGCEntry(PVM pVM, unsigned uOperation, unsigned uArg, ...) { /* todo */ switch (uOperation) { /* * Init RC modules. */ case VMMGC_DO_VMMGC_INIT: { /* * Validate the svn revision (uArg). */ if (uArg != VMMGetSvnRev()) return VERR_VMM_RC_VERSION_MISMATCH; /* * Initialize the runtime. * (The program timestamp is found in the elipsis.) */ va_list va; va_start(va, uArg); uint64_t u64TS = va_arg(va, uint64_t); va_end(va); int rc = RTRCInit(u64TS); Log(("VMMGCEntry: VMMGC_DO_VMMGC_INIT - uArg=%u (svn revision) u64TS=%RX64; rc=%Rrc\n", uArg, u64TS, rc)); AssertRCReturn(rc, rc); rc = PGMRegisterStringFormatTypes(); AssertRCReturn(rc, rc); rc = PGMRCDynMapInit(pVM); AssertRCReturn(rc, rc); return VINF_SUCCESS; } /* * Testcase which is used to test interrupt forwarding. * It spins for a while with interrupts enabled. */ case VMMGC_DO_TESTCASE_HYPER_INTERRUPT: { uint32_t volatile i = 0; ASMIntEnable(); while (i < _2G32) i++; ASMIntDisable(); return 0; } /* * Testcase which simply returns, this is used for * profiling of the switcher. */ case VMMGC_DO_TESTCASE_NOP: return 0; /* * Testcase executes a privileged instruction to force a world switch. (in both SVM & VMX) */ case VMMGC_DO_TESTCASE_HWACCM_NOP: ASMRdMsr_Low(MSR_IA32_SYSENTER_CS); return 0; /* * Delay for ~100us. */ case VMMGC_DO_TESTCASE_INTERRUPT_MASKING: { uint64_t u64MaxTicks = (SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) != ~(uint64_t)0 ? SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage) : _2G) / 10000; uint64_t u64StartTSC = ASMReadTSC(); uint64_t u64TicksNow; uint32_t volatile i = 0; do { /* waste some time and protect against getting stuck. */ for (uint32_t volatile j = 0; j < 1000; j++, i++) if (i > _2G32) return VERR_GENERAL_FAILURE; /* check if we're done.*/ u64TicksNow = ASMReadTSC() - u64StartTSC; } while (u64TicksNow < u64MaxTicks); return VINF_SUCCESS; } /* * Trap testcases and unknown operations. */ default: if ( uOperation >= VMMGC_DO_TESTCASE_TRAP_FIRST && uOperation < VMMGC_DO_TESTCASE_TRAP_LAST) return vmmGCTest(pVM, uOperation, uArg); return VERR_INVALID_PARAMETER; } }
/** * Worker function for dbgfR3CoreWrite() which does the writing. * * @returns VBox status code * @param pVM Pointer to the VM. * @param hFile The file to write to. Caller closes this. */ static int dbgfR3CoreWriteWorker(PVM pVM, RTFILE hFile) { /* * Collect core information. */ uint32_t const cu32MemRanges = dbgfR3GetRamRangeCount(pVM); uint16_t const cMemRanges = cu32MemRanges < UINT16_MAX - 1 ? cu32MemRanges : UINT16_MAX - 1; /* One PT_NOTE Program header */ uint16_t const cProgHdrs = cMemRanges + 1; DBGFCOREDESCRIPTOR CoreDescriptor; RT_ZERO(CoreDescriptor); CoreDescriptor.u32Magic = DBGFCORE_MAGIC; CoreDescriptor.u32FmtVersion = DBGFCORE_FMT_VERSION; CoreDescriptor.cbSelf = sizeof(CoreDescriptor); CoreDescriptor.u32VBoxVersion = VBOX_FULL_VERSION; CoreDescriptor.u32VBoxRevision = VMMGetSvnRev(); CoreDescriptor.cCpus = pVM->cCpus; Log((DBGFLOG_NAME ": CoreDescriptor Version=%u Revision=%u\n", CoreDescriptor.u32VBoxVersion, CoreDescriptor.u32VBoxRevision)); /* * Compute the file layout (see pg_dbgf_vmcore). */ uint64_t const offElfHdr = RTFileTell(hFile); uint64_t const offNoteSection = offElfHdr + sizeof(Elf64_Ehdr); uint64_t const offLoadSections = offNoteSection + sizeof(Elf64_Phdr); uint64_t const cbLoadSections = cMemRanges * sizeof(Elf64_Phdr); uint64_t const offCoreDescriptor = offLoadSections + cbLoadSections; uint64_t const cbCoreDescriptor = Elf64NoteSectionSize(g_pcszCoreVBoxCore, sizeof(CoreDescriptor)); uint64_t const offCpuDumps = offCoreDescriptor + cbCoreDescriptor; uint64_t const cbCpuDumps = pVM->cCpus * Elf64NoteSectionSize(g_pcszCoreVBoxCpu, sizeof(DBGFCORECPU)); uint64_t const offMemory = offCpuDumps + cbCpuDumps; uint64_t const offNoteSectionData = offCoreDescriptor; uint64_t const cbNoteSectionData = cbCoreDescriptor + cbCpuDumps; /* * Write ELF header. */ int rc = Elf64WriteElfHdr(hFile, cProgHdrs, 0 /* cSecHdrs */); if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": Elf64WriteElfHdr failed. rc=%Rrc\n", rc)); return rc; } /* * Write PT_NOTE program header. */ Assert(RTFileTell(hFile) == offNoteSection); rc = Elf64WriteProgHdr(hFile, PT_NOTE, PF_R, offNoteSectionData, /* file offset to contents */ cbNoteSectionData, /* size in core file */ cbNoteSectionData, /* size in memory */ 0); /* physical address */ if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": Elf64WritreProgHdr failed for PT_NOTE. rc=%Rrc\n", rc)); return rc; } /* * Write PT_LOAD program header for each memory range. */ Assert(RTFileTell(hFile) == offLoadSections); uint64_t offMemRange = offMemory; for (uint16_t iRange = 0; iRange < cMemRanges; iRange++) { RTGCPHYS GCPhysStart; RTGCPHYS GCPhysEnd; bool fIsMmio; rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio); if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange failed for iRange(%u) rc=%Rrc\n", iRange, rc)); return rc; } uint64_t cbMemRange = GCPhysEnd - GCPhysStart + 1; uint64_t cbFileRange = fIsMmio ? 0 : cbMemRange; Log((DBGFLOG_NAME ": PGMR3PhysGetRange iRange=%u GCPhysStart=%#x GCPhysEnd=%#x cbMemRange=%u\n", iRange, GCPhysStart, GCPhysEnd, cbMemRange)); rc = Elf64WriteProgHdr(hFile, PT_LOAD, PF_R, offMemRange, /* file offset to contents */ cbFileRange, /* size in core file */ cbMemRange, /* size in memory */ GCPhysStart); /* physical address */ if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": Elf64WriteProgHdr failed for memory range(%u) cbFileRange=%u cbMemRange=%u rc=%Rrc\n", iRange, cbFileRange, cbMemRange, rc)); return rc; } offMemRange += cbFileRange; } /* * Write the Core descriptor note header and data. */ Assert(RTFileTell(hFile) == offCoreDescriptor); rc = Elf64WriteNoteHdr(hFile, NT_VBOXCORE, g_pcszCoreVBoxCore, &CoreDescriptor, sizeof(CoreDescriptor)); if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for Note '%s' rc=%Rrc\n", g_pcszCoreVBoxCore, rc)); return rc; } /* * Write the CPU context note headers and data. */ Assert(RTFileTell(hFile) == offCpuDumps); PDBGFCORECPU pDbgfCoreCpu = (PDBGFCORECPU)RTMemAlloc(sizeof(*pDbgfCoreCpu)); if (RT_UNLIKELY(!pDbgfCoreCpu)) { LogRel((DBGFLOG_NAME ": failed to alloc %u bytes for DBGFCORECPU\n", sizeof(*pDbgfCoreCpu))); return VERR_NO_MEMORY; } for (uint32_t iCpu = 0; iCpu < pVM->cCpus; iCpu++) { PVMCPU pVCpu = &pVM->aCpus[iCpu]; PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); if (RT_UNLIKELY(!pCtx)) { LogRel((DBGFLOG_NAME ": CPUMQueryGuestCtxPtr failed for vCPU[%u]\n", iCpu)); RTMemFree(pDbgfCoreCpu); return VERR_INVALID_POINTER; } RT_BZERO(pDbgfCoreCpu, sizeof(*pDbgfCoreCpu)); dbgfR3GetCoreCpu(pCtx, pDbgfCoreCpu); rc = Elf64WriteNoteHdr(hFile, NT_VBOXCPU, g_pcszCoreVBoxCpu, pDbgfCoreCpu, sizeof(*pDbgfCoreCpu)); if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for vCPU[%u] rc=%Rrc\n", iCpu, rc)); RTMemFree(pDbgfCoreCpu); return rc; } } RTMemFree(pDbgfCoreCpu); pDbgfCoreCpu = NULL; /* * Write memory ranges. */ Assert(RTFileTell(hFile) == offMemory); for (uint16_t iRange = 0; iRange < cMemRanges; iRange++) { RTGCPHYS GCPhysStart; RTGCPHYS GCPhysEnd; bool fIsMmio; rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio); if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange(2) failed for iRange(%u) rc=%Rrc\n", iRange, rc)); return rc; } if (fIsMmio) continue; /* * Write page-by-page of this memory range. * * The read function may fail on MMIO ranges, we write these as zero * pages for now (would be nice to have the VGA bits there though). */ uint64_t cbMemRange = GCPhysEnd - GCPhysStart + 1; uint64_t cPages = cbMemRange >> PAGE_SHIFT; for (uint64_t iPage = 0; iPage < cPages; iPage++) { uint8_t abPage[PAGE_SIZE]; rc = PGMPhysSimpleReadGCPhys(pVM, abPage, GCPhysStart + (iPage << PAGE_SHIFT), sizeof(abPage)); if (RT_FAILURE(rc)) { if (rc != VERR_PGM_PHYS_PAGE_RESERVED) LogRel((DBGFLOG_NAME ": PGMPhysRead failed for iRange=%u iPage=%u. rc=%Rrc. Ignoring...\n", iRange, iPage, rc)); RT_ZERO(abPage); } rc = RTFileWrite(hFile, abPage, sizeof(abPage), NULL /* all */); if (RT_FAILURE(rc)) { LogRel((DBGFLOG_NAME ": RTFileWrite failed. iRange=%u iPage=%u rc=%Rrc\n", iRange, iPage, rc)); return rc; } } } return rc; }