PGMDbg.cpp revision 9f9a20823b87e89c1b5cb45eb9b5699b29bfefeb
/* $Id$ */
/** @file
* PGM - Page Manager and Monitor - Debugger & Debugging APIs.
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PGM
#include "PGMInternal.h"
#include "PGMInline.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The max needle size that we will bother searching for
* This must not be more than half a page! */
#define MAX_NEEDLE_SIZE 256
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* State structure for the paging hierarchy dumpers.
*/
typedef struct PGMR3DUMPHIERARCHYSTATE
{
/** The VM handle. */
/** Output helpers. */
/** Set if PSE, PAE or long mode is enabled. */
bool fPse;
/** Set if PAE or long mode is enabled. */
bool fPae;
/** Set if long mode is enabled. */
bool fLme;
/** Set if nested paging. */
bool fNp;
/** Set if EPT. */
bool fEpt;
/** Set if NXE is enabled. */
bool fNxe;
/** The number or chars the address needs. */
/** The last reserved bit. */
/** Dump the page info as well (shadow page summary / guest physical
* page summary). */
bool fDumpPageInfo;
/** Whether or not to print the header. */
bool fPrintHeader;
/** Whether to print the CR3 value */
bool fPrintCr3;
/** Padding*/
bool afReserved[5];
/** The current address. */
/** The last address to dump structures for. */
/** The last address to dump structures for. */
/** Mask with the high reserved bits set. */
/** The number of leaf entries that we've printed. */
/** Pointer to the paging hierarchy dumper state. */
/**
* Converts a R3 pointer to a GC physical address.
*
* Only for the debugger.
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success, *pGCPhys is set.
* @retval VERR_INVALID_POINTER if the pointer is not within the GC physical memory.
*
* @param pVM The VM handle.
* @param R3Ptr The R3 pointer to convert.
* @param pGCPhys Where to store the GC physical address on success.
*/
{
*pGCPhys = NIL_RTGCPHYS;
return VERR_NOT_IMPLEMENTED;
}
/**
* Converts a R3 pointer to a HC physical address.
*
* Only for the debugger.
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success, *pHCPhys is set.
* @retval VERR_PGM_PHYS_PAGE_RESERVED it it's a valid GC physical page but has no physical backing.
* @retval VERR_INVALID_POINTER if the pointer is not within the GC physical memory.
*
* @param pVM The VM handle.
* @param R3Ptr The R3 pointer to convert.
* @param pHCPhys Where to store the HC physical address on success.
*/
{
*pHCPhys = NIL_RTHCPHYS;
return VERR_NOT_IMPLEMENTED;
}
/**
* Converts a HC physical address to a GC physical address.
*
* Only for the debugger.
*
* @returns VBox status code
* @retval VINF_SUCCESS on success, *pGCPhys is set.
* @retval VERR_INVALID_POINTER if the HC physical address is not within the GC physical memory.
*
* @param pVM The VM handle.
* @param HCPhys The HC physical address to convert.
* @param pGCPhys Where to store the GC physical address on success.
*/
{
/*
* Validate and adjust the input a bit.
*/
if (HCPhys == NIL_RTHCPHYS)
return VERR_INVALID_POINTER;
if (HCPhys == 0)
return VERR_INVALID_POINTER;
pRam;
{
while (iPage-- > 0)
{
return VINF_SUCCESS;
}
}
return VERR_INVALID_POINTER;
}
/**
* Read physical memory API for the debugger, similar to
* PGMPhysSimpleReadGCPhys.
*
* @returns VBox status code.
*
* @param pVM The VM handle.
* @param pvDst Where to store what's read.
* @param GCPhysDst Where to start reading from.
* @param cb The number of bytes to attempt reading.
* @param fFlags Flags, MBZ.
* @param pcbRead For store the actual number of bytes read, pass NULL if
* partial reads are unwanted.
* @todo Unused?
*/
VMMR3DECL(int) PGMR3DbgReadGCPhys(PVM pVM, void *pvDst, RTGCPHYS GCPhysSrc, size_t cb, uint32_t fFlags, size_t *pcbRead)
{
/* validate */
/* try simple first. */
return rc;
/* partial read that failed, chop it up in pages. */
*pcbRead = 0;
rc = VINF_SUCCESS;
while (cb > 0)
{
/* advance */
if (RT_FAILURE(rc))
break;
}
}
/**
* Write physical memory API for the debugger, similar to
* PGMPhysSimpleWriteGCPhys.
*
* @returns VBox status code.
*
* @param pVM The VM handle.
* @param GCPhysDst Where to start writing.
* @param pvSrc What to write.
* @param cb The number of bytes to attempt writing.
* @param fFlags Flags, MBZ.
* @param pcbWritten For store the actual number of bytes written, pass NULL
* if partial writes are unwanted.
* @todo Unused?
*/
VMMR3DECL(int) PGMR3DbgWriteGCPhys(PVM pVM, RTGCPHYS GCPhysDst, const void *pvSrc, size_t cb, uint32_t fFlags, size_t *pcbWritten)
{
/* validate */
/* try simple first. */
return rc;
/* partial write that failed, chop it up in pages. */
*pcbWritten = 0;
rc = VINF_SUCCESS;
while (cb > 0)
{
/* advance */
if (RT_FAILURE(rc))
break;
*pcbWritten += cbChunk;
}
}
/**
* Read virtual memory API for the debugger, similar to PGMPhysSimpleReadGCPtr.
*
* @returns VBox status code.
*
* @param pVM The VM handle.
* @param pvDst Where to store what's read.
* @param GCPtrDst Where to start reading from.
* @param cb The number of bytes to attempt reading.
* @param fFlags Flags, MBZ.
* @param pcbRead For store the actual number of bytes read, pass NULL if
* partial reads are unwanted.
* @todo Unused?
*/
VMMR3DECL(int) PGMR3DbgReadGCPtr(PVM pVM, void *pvDst, RTGCPTR GCPtrSrc, size_t cb, uint32_t fFlags, size_t *pcbRead)
{
/* validate */
/* @todo SMP support! */
/** @todo deal with HMA */
/* try simple first. */
return rc;
/* partial read that failed, chop it up in pages. */
*pcbRead = 0;
rc = VINF_SUCCESS;
while (cb > 0)
{
/* advance */
if (RT_FAILURE(rc))
break;
}
}
/**
* Write virtual memory API for the debugger, similar to
* PGMPhysSimpleWriteGCPtr.
*
* @returns VBox status code.
*
* @param pVM The VM handle.
* @param GCPtrDst Where to start writing.
* @param pvSrc What to write.
* @param cb The number of bytes to attempt writing.
* @param fFlags Flags, MBZ.
* @param pcbWritten For store the actual number of bytes written, pass NULL
* if partial writes are unwanted.
* @todo Unused?
*/
VMMR3DECL(int) PGMR3DbgWriteGCPtr(PVM pVM, RTGCPTR GCPtrDst, void const *pvSrc, size_t cb, uint32_t fFlags, size_t *pcbWritten)
{
/* validate */
/* @todo SMP support! */
/** @todo deal with HMA */
/* try simple first. */
return rc;
/* partial write that failed, chop it up in pages. */
*pcbWritten = 0;
rc = VINF_SUCCESS;
while (cb > 0)
{
/* advance */
if (RT_FAILURE(rc))
break;
*pcbWritten += cbChunk;
}
}
/**
* memchr() with alignment considerations.
*
* @returns Pointer to matching byte, NULL if none found.
* @param pb Where to search. Aligned.
* @param b What to search for.
* @param cb How much to search .
* @param uAlign The alignment restriction of the result.
*/
static const uint8_t *pgmR3DbgAlignedMemChr(const uint8_t *pb, uint8_t b, size_t cb, uint32_t uAlign)
{
if (uAlign <= 32)
{
{
do
{
pbRet++;
if (!cbLeft)
{
break;
}
}
}
else
{
if (cb)
{
for (;;)
{
if (*pb == b)
{
break;
}
break;
}
}
}
return pbRet;
}
/**
* Scans a page for a byte string, keeping track of potential
* cross page matches.
*
* @returns true and *poff on match.
* false on mismatch.
* @param pbPage Pointer to the current page.
* @param poff Input: The offset into the page (aligned).
* Output: The page offset of the match on success.
* @param cb The number of bytes to search, starting of *poff.
* @param uAlign The needle alignment. This is of course less than a page.
* @param pabNeedle The byte string to search for.
* @param cbNeedle The length of the byte string.
* @param pabPrev The buffer that keeps track of a partial match that we
* bring over from the previous page. This buffer must be
* at least cbNeedle - 1 big.
* @param pcbPrev Input: The number of partial matching bytes from the previous page.
* Output: The number of partial matching bytes from this page.
* Initialize to 0 before the first call to this function.
*/
{
/*
* Try complete any partial match from the previous page.
*/
if (*pcbPrev > 0)
{
{
return false;
return true;
}
/* check out the remainder of the previous page. */
for (;;)
{
break;
if (!pb)
break;
{
return false;
return true;
}
}
*pcbPrev = 0;
}
/*
* Match the body of the page.
*/
for (;;)
{
if (!pb)
break;
{
/* match? */
{
return true;
}
}
else
{
/* paritial match at the end of the page? */
{
/* We're copying one byte more that we really need here, but wtf. */
return false;
}
}
/* no match, skip ahead. */
break;
}
return false;
}
/**
* Scans guest physical memory for a 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 Pointer to the shared VM structure.
* @param GCPhys Where to start searching.
* @param cbRange The number of bytes to search.
* @param GCPhysAlign The alignment of the needle. Must be a power of two
* and less or equal to 4GB.
* @param pabNeedle The byte string to search for.
* @param cbNeedle The length of the byte string. Max 256 bytes.
* @param pGCPhysHit Where to store the address of the first occurence on success.
*/
VMMR3DECL(int) PGMR3DbgScanPhysical(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cbRange, RTGCPHYS GCPhysAlign,
{
/*
* Validate and adjust the input a bit.
*/
if (!VALID_PTR(pGCPhysHit))
return VERR_INVALID_POINTER;
|| GCPhys == NIL_RTGCPHYS)
return VERR_INVALID_POINTER;
if (!cbNeedle)
return VERR_INVALID_PARAMETER;
if (cbNeedle > MAX_NEEDLE_SIZE)
return VERR_INVALID_PARAMETER;
if (!cbRange)
return VERR_DBGF_MEM_NOT_FOUND;
return VERR_DBGF_MEM_NOT_FOUND;
if (!GCPhysAlign)
return VERR_INVALID_PARAMETER;
if (GCPhysAlign > UINT32_MAX)
return VERR_NOT_POWER_OF_TWO;
return VERR_INVALID_PARAMETER;
{
return VERR_DBGF_MEM_NOT_FOUND;
}
? 1
: GCPhysAlign >> PAGE_SHIFT;
: ~(RTGCPHYS)0;
/*
* Search the memory - ignore MMIO and zero pages, also don't
* bother to match across ranges.
*/
pRam;
{
/*
* If the search range starts prior to the current ram range record,
* adjust the search range and possibly conclude the search.
*/
{
break;
off = 0;
}
else
{
/*
* Iterate the relevant pages.
*/
for (;; offPage = 0)
{
if ( ( !PGM_PAGE_IS_ZERO(pPage)
|| fAllZero)
&& !PGM_PAGE_IS_MMIO(pPage))
{
void const *pvPage;
if (RT_SUCCESS(rc))
{
bool fRc;
if (GCPhysAlign < PAGE_SIZE)
{
}
else
if (fRc)
{
return VINF_SUCCESS;
}
}
else
cbPrev = 0; /* ignore error. */
}
else
cbPrev = 0;
/* advance to the next page. */
{
return VERR_DBGF_MEM_NOT_FOUND;
}
break;
}
}
}
return VERR_DBGF_MEM_NOT_FOUND;
}
/**
* Scans (guest) virtual memory for a 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 Pointer to the shared VM structure.
* @param pVCpu The CPU context to search in.
* @param GCPtr Where to start searching.
* @param GCPtrAlign The alignment of the needle. Must be a power of two
* and less or equal to 4GB.
* @param cbRange The number of bytes to search. Max 256 bytes.
* @param pabNeedle The byte string to search for.
* @param cbNeedle The length of the byte string.
* @param pGCPtrHit Where to store the address of the first occurence on success.
*/
VMMR3DECL(int) PGMR3DbgScanVirtual(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, RTGCPTR cbRange, RTGCPTR GCPtrAlign,
{
/*
* Validate and adjust the input a bit.
*/
return VERR_INVALID_POINTER;
*pGCPtrHit = 0;
return VERR_INVALID_POINTER;
if (!cbNeedle)
return VERR_INVALID_PARAMETER;
if (cbNeedle > MAX_NEEDLE_SIZE)
return VERR_INVALID_PARAMETER;
if (!cbRange)
return VERR_DBGF_MEM_NOT_FOUND;
return VERR_DBGF_MEM_NOT_FOUND;
if (!GCPtrAlign)
return VERR_INVALID_PARAMETER;
if (GCPtrAlign > UINT32_MAX)
return VERR_NOT_POWER_OF_TWO;
return VERR_INVALID_PARAMETER;
{
return VERR_DBGF_MEM_NOT_FOUND;
}
/*
* Search the memory - ignore MMIO, zero and not-present pages.
*/
? 1
: GCPtrAlign >> PAGE_SHIFT;
: GCPtrMask;
for (;; offPage = 0)
{
if (RT_SUCCESS(rc))
{
if ( pPage
&& ( !PGM_PAGE_IS_ZERO(pPage)
|| fAllZero)
&& !PGM_PAGE_IS_MMIO(pPage))
{
void const *pvPage;
if (RT_SUCCESS(rc))
{
bool fRc;
if (GCPtrAlign < PAGE_SIZE)
{
}
else
if (fRc)
{
return VINF_SUCCESS;
}
}
else
cbPrev = 0; /* ignore error. */
}
else
cbPrev = 0;
}
else
cbPrev = 0; /* ignore error. */
/* advance to the next page. */
break;
}
return VERR_DBGF_MEM_NOT_FOUND;
}
/**
* Initializes the dumper state.
*
* @param pState The state to initialize.
* @param pVM The VM handle.
* @param fFlags The flags.
* @param u64FirstAddr The first address.
* @param u64LastAddr The last address.
* @param pHlp The output helpers.
*/
{
pState->afReserved[0] = false;
pState->u64HighReservedBits = pState->uLastRsvdBit == 62 ? UINT64_C(0x7ff) << 52 : UINT64_C(0xfff) << 52;
}
/**
* The simple way out, too tired to think of a more elegant solution.
*
* @param pState The state where we get the current address.
* @param cShift The shift count for the table entries.
* @param cEntries The number of table entries.
* @param piFirst Where to return the table index of the first
* entry to dump.
* @param piLast Where to return the table index of the last
* entry.
*/
static uint64_t pgmR3DumpHierarchyCalcRange(PPGMR3DUMPHIERARCHYSTATE pState, uint32_t cShift, uint32_t cEntries,
{
{
/* full range. */
*piFirst = 0;
}
{
/* no match */
*piLast = 0;
}
else
{
/* partial overlap */
: 0;
? cEntries - 1
}
}
/**
*
* @returns VBox status code.
* @param pState The dumper state.
* @param HCPhys The physical address of the shadow page.
* @param pszDesc The description.
* @param fIsMapping Set if it's a mapping.
* @param ppv Where to return the pointer.
*/
static int pgmR3DumpHierarchyShwMapPage(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, const char *pszDesc,
bool fIsMapping, void const **ppv)
{
void *pvPage;
if (!fIsMapping)
{
if (RT_FAILURE(rc))
{
pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! %s at HCPhys=%RHp was not found in the page pool!\n",
return rc;
}
}
else
{
{
{
"%0*llx error! Mapping error! PT %d has HCPhysPT=%RHp not %RHp is in the PD.\n",
break;
}
}
if (!pvPage)
{
pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! PT mapping %s at HCPhys=%RHp was not found in the page pool!\n",
return VERR_INVALID_PARAMETER;
}
}
return VINF_SUCCESS;
}
/**
* Dumps the a shadow page summary or smth.
*
* @param pState The dumper state.
* @param HCPhys The page address.
*/
{
char szPage[80];
if (pPage)
else
{
/* probably a mapping */
{
{
else
continue;
break;
}
}
}
}
/**
* Figures out which guest page this is and dumps a summary.
*
* @param pState The dumper state.
* @param HCPhys The page address.
* @param cbPage The page size.
*/
static void pgmR3DumpHierarchyShwGuestPageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, uint32_t cbPage)
{
char szPage[80];
if (RT_SUCCESS(rc))
{
if (pPage)
else
}
else
{
/* check the heap */
if (RT_SUCCESS(rc))
else
}
}
/**
* Dumps a PAE shadow page table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param HCPhys The page table address.
* @param fIsMapping Whether it is a mapping.
*/
static int pgmR3DumpHierarchyShwPaePT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, bool fIsMapping)
{
int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page table", fIsMapping, (void const **)&pPT);
if (RT_FAILURE(rc))
return rc;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
{
if (PGMSHWPTEPAE_IS_P(pPT->a[i]))
{
? "%016llx 3 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx"
: "%08llx 2 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx",
Pte.u & X86_PTE_PAE_PG_MASK_FULL);
if (pState->fDumpPageInfo)
pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx%s", (Pte.u >> 52) & 0x7ff, pState->fLme ? "" : "!");
}
else if ( (PGMSHWPTEPAE_GET_U(pPT->a[i]) & (pState->pVM->pgm.s.HCPhysInvMmioPg | X86_PTE_PAE_MBZ_MASK_NO_NX))
? "%016llx 3 | invalid / MMIO optimization\n"
: "%08llx 2 | invalid / MMIO optimization\n",
pState->u64Address);
else
? "%016llx 3 | invalid: %RX64\n"
: "%08llx 2 | invalid: %RX64\n",
}
return VINF_SUCCESS;
}
/**
* Dumps a PAE shadow page directory table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param HCPhys The physical address of the page directory table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyShwPaePD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
{
int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page directory", false, (void const **)&pPD);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
{
{
{
? "%016llx 2 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx"
: "%08llx 1 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx",
Pde.u & X86_PDE2M_PAE_PG_MASK);
if (pState->fDumpPageInfo)
pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx%s", (Pde.u >> 52) & 0x7ff, pState->fLme ? "" : "!");
pState->pHlp->pfnPrintf(pState->pHlp, " 20:13=%02llx%s", (Pde.u >> 13) & 0x0ff, pState->fLme ? "" : "!");
}
else
{
? "%016llx 2 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx"
: "%08llx 1 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx",
Pde.u & X86_PDE_PAE_PG_MASK_FULL);
if (pState->fDumpPageInfo)
if (cMaxDepth)
{
int rc2 = pgmR3DumpHierarchyShwPaePT(pState, Pde.u & X86_PDE_PAE_PG_MASK_FULL, !!(Pde.u & PGM_PDFLAGS_MAPPING));
}
else
}
}
}
return rc;
}
/**
* Dumps a PAE shadow page directory pointer table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param HCPhys The physical address of the page directory pointer table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyShwPaePDPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
{
/* Fend of addresses that are out of range in PAE mode - simplifies the code below. */
return VINF_SUCCESS;
int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page directory pointer table", false, (void const **)&pPDPT);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
{
{
{
"%016llx 1 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
Pdpe.u & X86_PDPE_PG_MASK);
if (pState->fDumpPageInfo)
}
else
{
"%08llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
Pdpe.u & X86_PDPE_PG_MASK);
if (pState->fDumpPageInfo)
}
if (cMaxDepth)
{
}
else
}
}
return rc;
}
/**
* Dumps a 32-bit shadow page table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pVM The VM handle.
* @param HCPhys The physical address of the table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyShwPaePML4(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
{
int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page map level 4", false, (void const **)&pPML4);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
/*
* This is a bit tricky as we're working on unsigned addresses while the
* AMD64 spec uses signed tricks.
*/
{ /* Simple, nothing to adjust */ }
else
{
{
"%016llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
Pml4e.u & X86_PML4E_PG_MASK);
if (pState->fDumpPageInfo)
if (cMaxDepth)
{
}
else
}
}
return rc;
}
/**
* Dumps a 32-bit shadow page table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pVM The VM handle.
* @param pPT Pointer to the page table.
* @param fMapping Set if it's a guest mapping.
*/
static int pgmR3DumpHierarchyShw32BitPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, bool fMapping)
{
if (RT_FAILURE(rc))
return rc;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
{
{
"%08llx 1 | P %c %c %c %c %c %s %s %s .. 4K %c%c%c %08x",
Pte.u & X86_PDE_PG_MASK);
if (pState->fDumpPageInfo)
}
}
return VINF_SUCCESS;
}
/**
* Dumps a 32-bit shadow page directory and page tables.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param HCPhys The physical address of the table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyShw32BitPD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
{
return VINF_SUCCESS;
int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page directory", false, (void const **)&pPD);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
{
{
{
| (Pde.u & X86_PDE4M_PG_MASK);
"%08llx 0 | P %c %c %c %c %c %s %s %s .. 4M %c%c%c %08llx",
u64Phys);
if (pState->fDumpPageInfo)
}
else
{
"%08llx 0 | P %c %c %c %c %c %s %s .. .. 4K %c%c%c %08x",
Pde.u & X86_PDE_PG_MASK);
if (pState->fDumpPageInfo)
if (cMaxDepth)
{
int rc2 = pgmR3DumpHierarchyShw32BitPT(pState, Pde.u & X86_PDE_PG_MASK, !!(Pde.u & PGM_PDFLAGS_MAPPING));
}
else
}
}
}
return rc;
}
/**
* Internal worker that initiates the actual dump.
*
* @returns VBox status code.
* @param pState The dumper state.
* @param cr3 The CR3 value.
* @param cMaxDepth The max depth.
*/
static int pgmR3DumpHierarchyShwDoIt(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t cr3, unsigned cMaxDepth)
{
int rc;
{
: "32-bit";
if (pState->fDumpPageInfo)
}
{
if (pState->fPrintHeader)
"%-*s R - Readable\n"
"%-*s | W - Writeable\n"
"%-*s | | X - Executable\n"
"%-*s | | | EMT - EPT memory type\n"
"%-*s | | | | PAT - Ignored PAT?\n"
"%-*s | | | | | AVL1 - 4 available bits\n"
"%-*s | | | | | | AVL2 - 12 available bits\n"
"%-*s Level | | | | | | | page \n"
/* xxxx n **** R W X EMT PAT AVL1 AVL2 xxxxxxxxxxxxx
R W X 7 0 f fff 0123456701234567 */
,
/** @todo implemented EPT dumping. */
}
else
{
if (pState->fPrintHeader)
"%-*s P - Present\n"
"%-*s | R/W - Read (0) / Write (1)\n"
"%-*s | | U/S - User (1) / Supervisor (0)\n"
"%-*s | | | A - Accessed\n"
"%-*s | | | | D - Dirty\n"
"%-*s | | | | | G - Global\n"
"%-*s | | | | | | WT - Write thru\n"
"%-*s | | | | | | | CD - Cache disable\n"
"%-*s | | | | | | | | AT - Attribute table (PAT)\n"
"%-*s | | | | | | | | | NX - No execute (K8)\n"
"%-*s | | | | | | | | | | 4K/4M/2M - Page size.\n"
"%-*s | | | | | | | | | | | AVL - a=allocated; m=mapping; d=track dirty;\n"
"%-*s | | | | | | | | | | | | p=permanent; v=validated;\n"
"%-*s Level | | | | | | | | | | | | Page\n"
/* xxxx n **** P R S A D G WT CD AT NX 4M AVL xxxxxxxxxxxxx
- W U - - - -- -- -- -- -- 010 */
,
else
}
return rc;
}
/**
* dbgfR3PagingDumpEx worker.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param cr3 The CR3 register value.
* @param fFlags The flags, DBGFPGDMP_FLAGS_XXX.
* @param u64FirstAddr The start address.
* @param u64LastAddr The address to stop after.
* @param cMaxDepth The max depth.
* @param pHlp The output callbacks. Defaults to log if NULL.
*
* @internal
*/
VMMR3_INT_DECL(int) PGMR3DumpHierarchyShw(PVM pVM, uint64_t cr3, uint32_t fFlags, uint64_t u64FirstAddr, uint64_t u64LastAddr,
{
/* Minimal validation as we're only supposed to service DBGF. */
AssertReturn(!(fFlags & (DBGFPGDMP_FLAGS_CURRENT_MODE | DBGFPGDMP_FLAGS_CURRENT_CR3)), VERR_INVALID_PARAMETER);
}
/**
*
* @returns VBox status code (VINF_SUCCESS).
* @param pVM The VM handle.
* @param cr3 The root of the hierarchy.
* @param cr4 The cr4, only PAE and PSE is currently used.
* @param fLongMode Set if long mode, false if not long mode.
* @param cMaxDepth Number of levels to dump.
* @param pHlp Pointer to the output functions.
*
* @deprecated Use DBGFR3PagingDumpEx.
*/
VMMR3DECL(int) PGMR3DumpHierarchyHC(PVM pVM, uint64_t cr3, uint64_t cr4, bool fLongMode, unsigned cMaxDepth, PCDBGFINFOHLP pHlp)
{
if (!cMaxDepth)
return VINF_SUCCESS;
if (!pVCpu)
uint32_t fFlags = DBGFPGDMP_FLAGS_HEADER | DBGFPGDMP_FLAGS_PRINT_CR3 | DBGFPGDMP_FLAGS_PAGE_INFO | DBGFPGDMP_FLAGS_SHADOW;
if (fLongMode)
return DBGFR3PagingDumpEx(pVM, pVCpu->idCpu, fFlags, cr3, 0, fLongMode ? UINT64_MAX : UINT32_MAX, cMaxDepth, pHlp);
}
/**
* Maps the guest page.
*
* @returns VBox status code.
* @param pState The dumper state.
* @param GCPhys The physical address of the guest page.
* @param pszDesc The description.
* @param ppv Where to return the pointer.
* @param pLock Where to return the mapping lock. Hand this to
* PGMPhysReleasePageMappingLock when done.
*/
static int pgmR3DumpHierarchyGstMapPage(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, const char *pszDesc,
{
if (RT_FAILURE(rc))
{
return rc;
}
return VINF_SUCCESS;
}
/**
* Figures out which guest page this is and dumps a summary.
*
* @param pState The dumper state.
* @param GCPhys The page address.
* @param cbPage The page size.
*/
static void pgmR3DumpHierarchyGstPageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, uint32_t cbPage)
{
char szPage[80];
if (pPage)
else
}
/**
* Checks the entry for reserved bits.
*
* @param pState The dumper state.
* @param u64Entry The entry to check.
*/
static void pgmR3DumpHierarchyGstCheckReservedHighBits(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t u64Entry)
{
if (uRsvd)
/** @todo check the valid physical bits as well. */
}
/**
* Dumps a PAE shadow page table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param GCPhys The page table address.
*/
{
if (RT_FAILURE(rc))
return rc;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
{
{
? "%016llx 3 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx"
: "%08llx 2 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx",
Pte.u & X86_PTE_PAE_PG_MASK_FULL);
if (pState->fDumpPageInfo)
}
}
return VINF_SUCCESS;
}
/**
* Dumps a PAE shadow page directory table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param GCPhys The physical address of the table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyGstPaePD(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, unsigned cMaxDepth)
{
int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page directory", (void const **)&pPD, &Lock);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
{
{
{
? "%016llx 2 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx"
: "%08llx 1 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx",
Pde.u & X86_PDE2M_PAE_PG_MASK);
if (pState->fDumpPageInfo)
pState->pHlp->pfnPrintf(pState->pHlp, " 20:13=%02llx%s", (Pde.u >> 13) & 0x0ff, pState->fLme ? "" : "!");
}
else
{
? "%016llx 2 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx"
: "%08llx 1 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx",
Pde.u & X86_PDE_PAE_PG_MASK_FULL);
if (pState->fDumpPageInfo)
if (cMaxDepth)
{
}
else
}
}
}
return rc;
}
/**
* Dumps a PAE shadow page directory pointer table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param GCPhys The physical address of the table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyGstPaePDPT(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, unsigned cMaxDepth)
{
/* Fend of addresses that are out of range in PAE mode - simplifies the code below. */
return VINF_SUCCESS;
int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page directory pointer table", (void const **)&pPDPT, &Lock);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
{
{
{
/** @todo Do 1G pages. */
"%016llx 1 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
Pdpe.u & X86_PDPE_PG_MASK);
if (pState->fDumpPageInfo)
}
else
{
"%08llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
Pdpe.u & X86_PDPE_PG_MASK);
if (pState->fDumpPageInfo)
}
if (cMaxDepth)
{
}
else
}
}
return rc;
}
/**
* Dumps a 32-bit shadow page table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pVM The VM handle.
* @param GCPhys The physical address of the table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyGstPaePML4(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS GCPhys, unsigned cMaxDepth)
{
int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page map level 4", (void const **)&pPML4, &Lock);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
/*
* This is a bit tricky as we're working on unsigned addresses while the
* AMD64 spec uses signed tricks.
*/
{ /* Simple, nothing to adjust */ }
else
{
{
"%016llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
Pml4e.u & X86_PML4E_PG_MASK);
if (pState->fDumpPageInfo)
if (cMaxDepth)
{
}
else
}
}
return rc;
}
/**
* Dumps a 32-bit shadow page table.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param GCPhys The physical address of the table.
*/
{
if (RT_FAILURE(rc))
return rc;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
{
{
"%08llx 1 | P %c %c %c %c %c %s %s %s .. 4K %c%c%c %08x",
Pte.u & X86_PDE_PG_MASK);
if (pState->fDumpPageInfo)
}
}
return VINF_SUCCESS;
}
/**
* Dumps a 32-bit shadow page directory and page tables.
*
* @returns VBox status code (VINF_SUCCESS).
* @param pState The dumper state.
* @param GCPhys The physical address of the table.
* @param cMaxDepth The maxium depth.
*/
static int pgmR3DumpHierarchyGst32BitPD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS GCPhys, unsigned cMaxDepth)
{
return VINF_SUCCESS;
int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page directory", (void const **)&pPD, &Lock);
if (RT_FAILURE(rc))
return rc;
cMaxDepth--;
uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
{
{
{
| (Pde.u & X86_PDE4M_PG_MASK);
"%08llx 0 | P %c %c %c %c %c %s %s %s .. 4M %c%c%c %08llx",
u64Phys);
if (pState->fDumpPageInfo)
}
else
{
"%08llx 0 | P %c %c %c %c %c %s %s .. .. .. %c%c%c %08x",
Pde.u & X86_PDE_PG_MASK);
if (pState->fDumpPageInfo)
if (cMaxDepth)
{
}
else
}
}
}
return rc;
}
/**
* Internal worker that initiates the actual dump.
*
* @returns VBox status code.
* @param pState The dumper state.
* @param cr3 The CR3 value.
* @param cMaxDepth The max depth.
*/
static int pgmR3DumpHierarchyGstDoIt(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t cr3, unsigned cMaxDepth)
{
int rc;
{
: "32-bit";
if (pState->fDumpPageInfo)
}
{
if (pState->fPrintHeader)
"%-*s R - Readable\n"
"%-*s | W - Writeable\n"
"%-*s | | X - Executable\n"
"%-*s | | | EMT - EPT memory type\n"
"%-*s | | | | PAT - Ignored PAT?\n"
"%-*s | | | | | AVL1 - 4 available bits\n"
"%-*s | | | | | | AVL2 - 12 available bits\n"
"%-*s Level | | | | | | | page \n"
/* xxxx n **** R W X EMT PAT AVL1 AVL2 xxxxxxxxxxxxx
R W X 7 0 f fff 0123456701234567 */
,
/** @todo implemented EPT dumping. */
}
else
{
if (pState->fPrintHeader)
"%-*s P - Present\n"
"%-*s | R/W - Read (0) / Write (1)\n"
"%-*s | | U/S - User (1) / Supervisor (0)\n"
"%-*s | | | A - Accessed\n"
"%-*s | | | | D - Dirty\n"
"%-*s | | | | | G - Global\n"
"%-*s | | | | | | WT - Write thru\n"
"%-*s | | | | | | | CD - Cache disable\n"
"%-*s | | | | | | | | AT - Attribute table (PAT)\n"
"%-*s | | | | | | | | | NX - No execute (K8)\n"
"%-*s | | | | | | | | | | 4K/4M/2M - Page size.\n"
"%-*s | | | | | | | | | | | AVL - 3 available bits.\n"
"%-*s Level | | | | | | | | | | | | Page\n"
/* xxxx n **** P R S A D G WT CD AT NX 4M AVL xxxxxxxxxxxxx
- W U - - - -- -- -- -- -- 010 */
,
else
}
return rc;
}
/**
* dbgfR3PagingDumpEx worker.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param cr3 The CR3 register value.
* @param fFlags The flags, DBGFPGDMP_FLAGS_XXX.
* @param FirstAddr The start address.
* @param LastAddr The address to stop after.
* @param cMaxDepth The max depth.
* @param pHlp The output callbacks. Defaults to log if NULL.
*
* @internal
*/
VMMR3_INT_DECL(int) PGMR3DumpHierarchyGst(PVM pVM, uint64_t cr3, uint32_t fFlags, RTGCPTR FirstAddr, RTGCPTR LastAddr,
{
/* Minimal validation as we're only supposed to service DBGF. */
AssertReturn(!(fFlags & (DBGFPGDMP_FLAGS_CURRENT_MODE | DBGFPGDMP_FLAGS_CURRENT_CR3)), VERR_INVALID_PARAMETER);
}