VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PGMDbg.cpp@ 57334

Last change on this file since 57334 was 57334, checked in by vboxsync, 9 years ago

next try

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 103.7 KB
Line 
1/* $Id: PGMDbg.cpp 57334 2015-08-13 16:21:10Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor - Debugger & Debugging APIs.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_PGM
22#include <VBox/vmm/pgm.h>
23#include <VBox/vmm/stam.h>
24#include "PGMInternal.h"
25#include <VBox/vmm/vm.h>
26#include <VBox/vmm/uvm.h>
27#include "PGMInline.h"
28#include <iprt/assert.h>
29#include <iprt/asm.h>
30#include <iprt/string.h>
31#include <VBox/log.h>
32#include <VBox/param.h>
33#include <VBox/err.h>
34
35
36/*******************************************************************************
37* Defined Constants And Macros *
38*******************************************************************************/
39/** The max needle size that we will bother searching for
40 * This must not be more than half a page! */
41#define MAX_NEEDLE_SIZE 256
42
43
44/*******************************************************************************
45* Structures and Typedefs *
46*******************************************************************************/
47/**
48 * State structure for the paging hierarchy dumpers.
49 */
50typedef struct PGMR3DUMPHIERARCHYSTATE
51{
52 /** Pointer to the VM. */
53 PVM pVM;
54 /** Output helpers. */
55 PCDBGFINFOHLP pHlp;
56 /** Set if PSE, PAE or long mode is enabled. */
57 bool fPse;
58 /** Set if PAE or long mode is enabled. */
59 bool fPae;
60 /** Set if long mode is enabled. */
61 bool fLme;
62 /** Set if nested paging. */
63 bool fNp;
64 /** Set if EPT. */
65 bool fEpt;
66 /** Set if NXE is enabled. */
67 bool fNxe;
68 /** The number or chars the address needs. */
69 uint8_t cchAddress;
70 /** The last reserved bit. */
71 uint8_t uLastRsvdBit;
72 /** Dump the page info as well (shadow page summary / guest physical
73 * page summary). */
74 bool fDumpPageInfo;
75 /** Whether or not to print the header. */
76 bool fPrintHeader;
77 /** Whether to print the CR3 value */
78 bool fPrintCr3;
79 /** Padding*/
80 bool afReserved[5];
81 /** The current address. */
82 uint64_t u64Address;
83 /** The last address to dump structures for. */
84 uint64_t u64FirstAddress;
85 /** The last address to dump structures for. */
86 uint64_t u64LastAddress;
87 /** Mask with the high reserved bits set. */
88 uint64_t u64HighReservedBits;
89 /** The number of leaf entries that we've printed. */
90 uint64_t cLeaves;
91} PGMR3DUMPHIERARCHYSTATE;
92/** Pointer to the paging hierarchy dumper state. */
93typedef PGMR3DUMPHIERARCHYSTATE *PPGMR3DUMPHIERARCHYSTATE;
94
95
96/**
97 * Assembly scanning function.
98 *
99 * @returns Pointer to possible match or NULL.
100 * @param pvHaystack Pointer to what we search in.
101 * @param cbHaystack Number of bytes to search.
102 * @param pvNeedle Pointer to what we search for.
103 * @param cbNeedle Size of what we're searching for.
104 */
105
106#if defined(_MSC_VER) || defined(RT_OS_OS2)
107# define DECLASMCALLBACK(type) type __cdecl
108#elif defined(__GNUC__) && defined(RT_ARCH_X86)
109# define DECLASMCALLBACK(type) type __attribute__((cdecl,regparm(0)))
110#else
111# define DECLASMCALLBACK(type) type
112#endif
113typedef DECLASMCALLBACK(uint8_t const *) FNPGMR3DBGFIXEDMEMSCAN(void const *pvHaystack, uint32_t cbHaystack,
114 void const *pvNeedle, size_t cbNeedle);
115/** Pointer to an fixed size and step assembly scanner function. */
116typedef FNPGMR3DBGFIXEDMEMSCAN *PFNPGMR3DBGFIXEDMEMSCAN;
117
118
119/*******************************************************************************
120* Internal Functions *
121*******************************************************************************/
122DECLASM(uint8_t const *) pgmR3DbgFixedMemScan8Wide8Step(void const *, uint32_t, void const *, size_t cbNeedle);
123DECLASM(uint8_t const *) pgmR3DbgFixedMemScan4Wide4Step(void const *, uint32_t, void const *, size_t cbNeedle);
124DECLASM(uint8_t const *) pgmR3DbgFixedMemScan2Wide2Step(void const *, uint32_t, void const *, size_t cbNeedle);
125DECLASM(uint8_t const *) pgmR3DbgFixedMemScan1Wide1Step(void const *, uint32_t, void const *, size_t cbNeedle);
126DECLASM(uint8_t const *) pgmR3DbgFixedMemScan4Wide1Step(void const *, uint32_t, void const *, size_t cbNeedle);
127DECLASM(uint8_t const *) pgmR3DbgFixedMemScan8Wide1Step(void const *, uint32_t, void const *, size_t cbNeedle);
128
129
130/**
131 * Converts a R3 pointer to a GC physical address.
132 *
133 * Only for the debugger.
134 *
135 * @returns VBox status code.
136 * @retval VINF_SUCCESS on success, *pGCPhys is set.
137 * @retval VERR_INVALID_POINTER if the pointer is not within the GC physical memory.
138 *
139 * @param pUVM The user mode VM handle.
140 * @param R3Ptr The R3 pointer to convert.
141 * @param pGCPhys Where to store the GC physical address on success.
142 */
143VMMR3DECL(int) PGMR3DbgR3Ptr2GCPhys(PUVM pUVM, RTR3PTR R3Ptr, PRTGCPHYS pGCPhys)
144{
145 NOREF(pUVM); NOREF(R3Ptr);
146 *pGCPhys = NIL_RTGCPHYS;
147 return VERR_NOT_IMPLEMENTED;
148}
149
150
151/**
152 * Converts a R3 pointer to a HC physical address.
153 *
154 * Only for the debugger.
155 *
156 * @returns VBox status code.
157 * @retval VINF_SUCCESS on success, *pHCPhys is set.
158 * @retval VERR_PGM_PHYS_PAGE_RESERVED it it's a valid GC physical page but has no physical backing.
159 * @retval VERR_INVALID_POINTER if the pointer is not within the GC physical memory.
160 *
161 * @param pUVM The user mode VM handle.
162 * @param R3Ptr The R3 pointer to convert.
163 * @param pHCPhys Where to store the HC physical address on success.
164 */
165VMMR3DECL(int) PGMR3DbgR3Ptr2HCPhys(PUVM pUVM, RTR3PTR R3Ptr, PRTHCPHYS pHCPhys)
166{
167 NOREF(pUVM); NOREF(R3Ptr);
168 *pHCPhys = NIL_RTHCPHYS;
169 return VERR_NOT_IMPLEMENTED;
170}
171
172
173/**
174 * Converts a HC physical address to a GC physical address.
175 *
176 * Only for the debugger.
177 *
178 * @returns VBox status code
179 * @retval VINF_SUCCESS on success, *pGCPhys is set.
180 * @retval VERR_INVALID_POINTER if the HC physical address is not within the GC physical memory.
181 *
182 * @param pUVM The user mode VM handle.
183 * @param HCPhys The HC physical address to convert.
184 * @param pGCPhys Where to store the GC physical address on success.
185 */
186VMMR3DECL(int) PGMR3DbgHCPhys2GCPhys(PUVM pUVM, RTHCPHYS HCPhys, PRTGCPHYS pGCPhys)
187{
188 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
189 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
190
191 /*
192 * Validate and adjust the input a bit.
193 */
194 if (HCPhys == NIL_RTHCPHYS)
195 return VERR_INVALID_POINTER;
196 unsigned off = HCPhys & PAGE_OFFSET_MASK;
197 HCPhys &= X86_PTE_PAE_PG_MASK;
198 if (HCPhys == 0)
199 return VERR_INVALID_POINTER;
200
201 for (PPGMRAMRANGE pRam = pUVM->pVM->pgm.s.CTX_SUFF(pRamRangesX);
202 pRam;
203 pRam = pRam->CTX_SUFF(pNext))
204 {
205 uint32_t iPage = pRam->cb >> PAGE_SHIFT;
206 while (iPage-- > 0)
207 if (PGM_PAGE_GET_HCPHYS(&pRam->aPages[iPage]) == HCPhys)
208 {
209 *pGCPhys = pRam->GCPhys + (iPage << PAGE_SHIFT) + off;
210 return VINF_SUCCESS;
211 }
212 }
213 return VERR_INVALID_POINTER;
214}
215
216
217/**
218 * Read physical memory API for the debugger, similar to
219 * PGMPhysSimpleReadGCPhys.
220 *
221 * @returns VBox status code.
222 *
223 * @param pVM Pointer to the VM.
224 * @param pvDst Where to store what's read.
225 * @param GCPhysDst Where to start reading from.
226 * @param cb The number of bytes to attempt reading.
227 * @param fFlags Flags, MBZ.
228 * @param pcbRead For store the actual number of bytes read, pass NULL if
229 * partial reads are unwanted.
230 * @todo Unused?
231 */
232VMMR3_INT_DECL(int) PGMR3DbgReadGCPhys(PVM pVM, void *pvDst, RTGCPHYS GCPhysSrc, size_t cb, uint32_t fFlags, size_t *pcbRead)
233{
234 /* validate */
235 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
236 AssertReturn(pVM, VERR_INVALID_PARAMETER);
237
238 /* try simple first. */
239 int rc = PGMPhysSimpleReadGCPhys(pVM, pvDst, GCPhysSrc, cb);
240 if (RT_SUCCESS(rc) || !pcbRead)
241 return rc;
242
243 /* partial read that failed, chop it up in pages. */
244 *pcbRead = 0;
245 rc = VINF_SUCCESS;
246 while (cb > 0)
247 {
248 size_t cbChunk = PAGE_SIZE;
249 cbChunk -= GCPhysSrc & PAGE_OFFSET_MASK;
250 if (cbChunk > cb)
251 cbChunk = cb;
252
253 rc = PGMPhysSimpleReadGCPhys(pVM, pvDst, GCPhysSrc, cbChunk);
254
255 /* advance */
256 if (RT_FAILURE(rc))
257 break;
258 *pcbRead += cbChunk;
259 cb -= cbChunk;
260 GCPhysSrc += cbChunk;
261 pvDst = (uint8_t *)pvDst + cbChunk;
262 }
263
264 return *pcbRead && RT_FAILURE(rc) ? -rc : rc;
265}
266
267
268/**
269 * Write physical memory API for the debugger, similar to
270 * PGMPhysSimpleWriteGCPhys.
271 *
272 * @returns VBox status code.
273 *
274 * @param pVM Pointer to the VM.
275 * @param GCPhysDst Where to start writing.
276 * @param pvSrc What to write.
277 * @param cb The number of bytes to attempt writing.
278 * @param fFlags Flags, MBZ.
279 * @param pcbWritten For store the actual number of bytes written, pass NULL
280 * if partial writes are unwanted.
281 * @todo Unused?
282 */
283VMMR3_INT_DECL(int) PGMR3DbgWriteGCPhys(PVM pVM, RTGCPHYS GCPhysDst, const void *pvSrc, size_t cb, uint32_t fFlags, size_t *pcbWritten)
284{
285 /* validate */
286 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
287 AssertReturn(pVM, VERR_INVALID_PARAMETER);
288
289 /* try simple first. */
290 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysDst, pvSrc, cb);
291 if (RT_SUCCESS(rc) || !pcbWritten)
292 return rc;
293
294 /* partial write that failed, chop it up in pages. */
295 *pcbWritten = 0;
296 rc = VINF_SUCCESS;
297 while (cb > 0)
298 {
299 size_t cbChunk = PAGE_SIZE;
300 cbChunk -= GCPhysDst & PAGE_OFFSET_MASK;
301 if (cbChunk > cb)
302 cbChunk = cb;
303
304 rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysDst, pvSrc, cbChunk);
305
306 /* advance */
307 if (RT_FAILURE(rc))
308 break;
309 *pcbWritten += cbChunk;
310 cb -= cbChunk;
311 GCPhysDst += cbChunk;
312 pvSrc = (uint8_t const *)pvSrc + cbChunk;
313 }
314
315 return *pcbWritten && RT_FAILURE(rc) ? -rc : rc;
316
317}
318
319
320/**
321 * Read virtual memory API for the debugger, similar to PGMPhysSimpleReadGCPtr.
322 *
323 * @returns VBox status code.
324 *
325 * @param pVM Pointer to the VM.
326 * @param pvDst Where to store what's read.
327 * @param GCPtrDst Where to start reading from.
328 * @param cb The number of bytes to attempt reading.
329 * @param fFlags Flags, MBZ.
330 * @param pcbRead For store the actual number of bytes read, pass NULL if
331 * partial reads are unwanted.
332 * @todo Unused?
333 */
334VMMR3_INT_DECL(int) PGMR3DbgReadGCPtr(PVM pVM, void *pvDst, RTGCPTR GCPtrSrc, size_t cb, uint32_t fFlags, size_t *pcbRead)
335{
336 /* validate */
337 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
338 AssertReturn(pVM, VERR_INVALID_PARAMETER);
339
340 /* @todo SMP support! */
341 PVMCPU pVCpu = &pVM->aCpus[0];
342
343/** @todo deal with HMA */
344 /* try simple first. */
345 int rc = PGMPhysSimpleReadGCPtr(pVCpu, pvDst, GCPtrSrc, cb);
346 if (RT_SUCCESS(rc) || !pcbRead)
347 return rc;
348
349 /* partial read that failed, chop it up in pages. */
350 *pcbRead = 0;
351 rc = VINF_SUCCESS;
352 while (cb > 0)
353 {
354 size_t cbChunk = PAGE_SIZE;
355 cbChunk -= GCPtrSrc & PAGE_OFFSET_MASK;
356 if (cbChunk > cb)
357 cbChunk = cb;
358
359 rc = PGMPhysSimpleReadGCPtr(pVCpu, pvDst, GCPtrSrc, cbChunk);
360
361 /* advance */
362 if (RT_FAILURE(rc))
363 break;
364 *pcbRead += cbChunk;
365 cb -= cbChunk;
366 GCPtrSrc += cbChunk;
367 pvDst = (uint8_t *)pvDst + cbChunk;
368 }
369
370 return *pcbRead && RT_FAILURE(rc) ? -rc : rc;
371
372}
373
374
375/**
376 * Write virtual memory API for the debugger, similar to
377 * PGMPhysSimpleWriteGCPtr.
378 *
379 * @returns VBox status code.
380 *
381 * @param pVM Pointer to the VM.
382 * @param GCPtrDst Where to start writing.
383 * @param pvSrc What to write.
384 * @param cb The number of bytes to attempt writing.
385 * @param fFlags Flags, MBZ.
386 * @param pcbWritten For store the actual number of bytes written, pass NULL
387 * if partial writes are unwanted.
388 * @todo Unused?
389 */
390VMMR3_INT_DECL(int) PGMR3DbgWriteGCPtr(PVM pVM, RTGCPTR GCPtrDst, void const *pvSrc, size_t cb, uint32_t fFlags, size_t *pcbWritten)
391{
392 /* validate */
393 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
394 AssertReturn(pVM, VERR_INVALID_PARAMETER);
395
396 /* @todo SMP support! */
397 PVMCPU pVCpu = &pVM->aCpus[0];
398
399/** @todo deal with HMA */
400 /* try simple first. */
401 int rc = PGMPhysSimpleWriteGCPtr(pVCpu, GCPtrDst, pvSrc, cb);
402 if (RT_SUCCESS(rc) || !pcbWritten)
403 return rc;
404
405 /* partial write that failed, chop it up in pages. */
406 *pcbWritten = 0;
407 rc = VINF_SUCCESS;
408 while (cb > 0)
409 {
410 size_t cbChunk = PAGE_SIZE;
411 cbChunk -= GCPtrDst & PAGE_OFFSET_MASK;
412 if (cbChunk > cb)
413 cbChunk = cb;
414
415 rc = PGMPhysSimpleWriteGCPtr(pVCpu, GCPtrDst, pvSrc, cbChunk);
416
417 /* advance */
418 if (RT_FAILURE(rc))
419 break;
420 *pcbWritten += cbChunk;
421 cb -= cbChunk;
422 GCPtrDst += cbChunk;
423 pvSrc = (uint8_t const *)pvSrc + cbChunk;
424 }
425
426 return *pcbWritten && RT_FAILURE(rc) ? -rc : rc;
427
428}
429
430
431/**
432 * memchr() with alignment considerations.
433 *
434 * @returns Pointer to matching byte, NULL if none found.
435 * @param pb Where to search. Aligned.
436 * @param b What to search for.
437 * @param cb How much to search .
438 * @param uAlign The alignment restriction of the result.
439 */
440static const uint8_t *pgmR3DbgAlignedMemChr(const uint8_t *pb, uint8_t b, size_t cb, uint32_t uAlign)
441{
442 const uint8_t *pbRet;
443 if (uAlign <= 32)
444 {
445 pbRet = (const uint8_t *)memchr(pb, b, cb);
446 if ((uintptr_t)pbRet & (uAlign - 1))
447 {
448 do
449 {
450 pbRet++;
451 size_t cbLeft = cb - (pbRet - pb);
452 if (!cbLeft)
453 {
454 pbRet = NULL;
455 break;
456 }
457 pbRet = (const uint8_t *)memchr(pbRet, b, cbLeft);
458 } while ((uintptr_t)pbRet & (uAlign - 1));
459 }
460 }
461 else
462 {
463 pbRet = NULL;
464 if (cb)
465 {
466 for (;;)
467 {
468 if (*pb == b)
469 {
470 pbRet = pb;
471 break;
472 }
473 if (cb <= uAlign)
474 break;
475 cb -= uAlign;
476 pb += uAlign;
477 }
478 }
479 }
480 return pbRet;
481}
482
483
484/**
485 * Scans a page for a byte string, keeping track of potential
486 * cross page matches.
487 *
488 * @returns true and *poff on match.
489 * false on mismatch.
490 * @param pbPage Pointer to the current page.
491 * @param poff Input: The offset into the page (aligned).
492 * Output: The page offset of the match on success.
493 * @param cb The number of bytes to search, starting of *poff.
494 * @param uAlign The needle alignment. This is of course less than a page.
495 * @param pabNeedle The byte string to search for.
496 * @param cbNeedle The length of the byte string.
497 * @param pabPrev The buffer that keeps track of a partial match that we
498 * bring over from the previous page. This buffer must be
499 * at least cbNeedle - 1 big.
500 * @param pcbPrev Input: The number of partial matching bytes from the previous page.
501 * Output: The number of partial matching bytes from this page.
502 * Initialize to 0 before the first call to this function.
503 */
504static bool pgmR3DbgScanPage(const uint8_t *pbPage, int32_t *poff, uint32_t cb, uint32_t uAlign,
505 const uint8_t *pabNeedle, size_t cbNeedle, PFNPGMR3DBGFIXEDMEMSCAN pfnFixedMemScan,
506 uint8_t *pabPrev, size_t *pcbPrev)
507{
508 /*
509 * Try complete any partial match from the previous page.
510 */
511 if (*pcbPrev > 0)
512 {
513 size_t cbPrev = *pcbPrev;
514 Assert(!*poff);
515 Assert(cbPrev < cbNeedle);
516 if (!memcmp(pbPage, pabNeedle + cbPrev, cbNeedle - cbPrev))
517 {
518 if (cbNeedle - cbPrev > cb)
519 return false;
520 *poff = -(int32_t)cbPrev;
521 return true;
522 }
523
524 /* check out the remainder of the previous page. */
525 const uint8_t *pb = pabPrev;
526 for (;;)
527 {
528 if (cbPrev <= uAlign)
529 break;
530 cbPrev -= uAlign;
531 pb = pgmR3DbgAlignedMemChr(pb + uAlign, *pabNeedle, cbPrev, uAlign);
532 if (!pb)
533 break;
534 cbPrev = *pcbPrev - (pb - pabPrev);
535 if ( !memcmp(pb + 1, &pabNeedle[1], cbPrev - 1)
536 && !memcmp(pbPage, pabNeedle + cbPrev, cbNeedle - cbPrev))
537 {
538 if (cbNeedle - cbPrev > cb)
539 return false;
540 *poff = -(int32_t)cbPrev;
541 return true;
542 }
543 }
544
545 *pcbPrev = 0;
546 }
547
548 /*
549 * Match the body of the page.
550 */
551 const uint8_t *pb = pbPage + *poff;
552 const uint8_t * const pbEnd = pb + cb;
553 for (;;)
554 {
555 AssertMsg(((uintptr_t)pb & (uAlign - 1)) == 0, ("%#p %#x\n", pb, uAlign));
556 if (pfnFixedMemScan)
557 pb = pfnFixedMemScan(pb, cb, pabNeedle, cbNeedle);
558 else
559 pb = pgmR3DbgAlignedMemChr(pb, *pabNeedle, cb, uAlign);
560 if (!pb)
561 break;
562 cb = pbEnd - pb;
563 if (cb >= cbNeedle)
564 {
565 /* match? */
566 if (!memcmp(pb + 1, &pabNeedle[1], cbNeedle - 1))
567 {
568 *poff = pb - pbPage;
569 return true;
570 }
571 }
572 else
573 {
574 /* partial match at the end of the page? */
575 if (!memcmp(pb + 1, &pabNeedle[1], cb - 1))
576 {
577 /* We're copying one byte more that we really need here, but wtf. */
578 memcpy(pabPrev, pb, cb);
579 *pcbPrev = cb;
580 return false;
581 }
582 }
583
584 /* no match, skip ahead. */
585 if (cb <= uAlign)
586 break;
587 pb += uAlign;
588 cb -= uAlign;
589 }
590
591 return false;
592}
593
594
595static void pgmR3DbgSelectMemScanFunction(PFNPGMR3DBGFIXEDMEMSCAN *ppfnMemScan, uint32_t GCPhysAlign, size_t cbNeedle)
596{
597 *ppfnMemScan = NULL;
598 switch (GCPhysAlign)
599 {
600 case 1:
601 if (cbNeedle >= 8)
602 *ppfnMemScan = pgmR3DbgFixedMemScan8Wide1Step;
603 else if (cbNeedle >= 4)
604 *ppfnMemScan = pgmR3DbgFixedMemScan8Wide1Step;
605 else
606 *ppfnMemScan = pgmR3DbgFixedMemScan1Wide1Step;
607 break;
608 case 2:
609 if (cbNeedle >= 2)
610 *ppfnMemScan = pgmR3DbgFixedMemScan2Wide2Step;
611 break;
612 case 4:
613 if (cbNeedle >= 4)
614 *ppfnMemScan = pgmR3DbgFixedMemScan4Wide4Step;
615 break;
616 case 8:
617 if (cbNeedle >= 8)
618 *ppfnMemScan = pgmR3DbgFixedMemScan8Wide8Step;
619 break;
620 }
621}
622
623
624
625/**
626 * Scans guest physical memory for a byte string.
627 *
628 * @returns VBox status codes:
629 * @retval VINF_SUCCESS and *pGCPtrHit on success.
630 * @retval VERR_DBGF_MEM_NOT_FOUND if not found.
631 * @retval VERR_INVALID_POINTER if any of the pointer arguments are invalid.
632 * @retval VERR_INVALID_ARGUMENT if any other arguments are invalid.
633 *
634 * @param pVM Pointer to the VM.
635 * @param GCPhys Where to start searching.
636 * @param cbRange The number of bytes to search.
637 * @param GCPhysAlign The alignment of the needle. Must be a power of two
638 * and less or equal to 4GB.
639 * @param pabNeedle The byte string to search for.
640 * @param cbNeedle The length of the byte string. Max 256 bytes.
641 * @param pGCPhysHit Where to store the address of the first occurrence on success.
642 */
643VMMR3_INT_DECL(int) PGMR3DbgScanPhysical(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cbRange, RTGCPHYS GCPhysAlign,
644 const uint8_t *pabNeedle, size_t cbNeedle, PRTGCPHYS pGCPhysHit)
645{
646 /*
647 * Validate and adjust the input a bit.
648 */
649 if (!VALID_PTR(pGCPhysHit))
650 return VERR_INVALID_POINTER;
651 *pGCPhysHit = NIL_RTGCPHYS;
652
653 if ( !VALID_PTR(pabNeedle)
654 || GCPhys == NIL_RTGCPHYS)
655 return VERR_INVALID_POINTER;
656 if (!cbNeedle)
657 return VERR_INVALID_PARAMETER;
658 if (cbNeedle > MAX_NEEDLE_SIZE)
659 return VERR_INVALID_PARAMETER;
660
661 if (!cbRange)
662 return VERR_DBGF_MEM_NOT_FOUND;
663 if (GCPhys + cbNeedle - 1 < GCPhys)
664 return VERR_DBGF_MEM_NOT_FOUND;
665
666 if (!GCPhysAlign)
667 return VERR_INVALID_PARAMETER;
668 if (GCPhysAlign > UINT32_MAX)
669 return VERR_NOT_POWER_OF_TWO;
670 if (GCPhysAlign & (GCPhysAlign - 1))
671 return VERR_INVALID_PARAMETER;
672
673 if (GCPhys & (GCPhysAlign - 1))
674 {
675 RTGCPHYS Adj = GCPhysAlign - (GCPhys & (GCPhysAlign - 1));
676 if ( cbRange <= Adj
677 || GCPhys + Adj < GCPhys)
678 return VERR_DBGF_MEM_NOT_FOUND;
679 GCPhys += Adj;
680 cbRange -= Adj;
681 }
682
683 const bool fAllZero = ASMMemIsAll8(pabNeedle, cbNeedle, 0) == NULL;
684 const uint32_t cIncPages = GCPhysAlign <= PAGE_SIZE
685 ? 1
686 : GCPhysAlign >> PAGE_SHIFT;
687 const RTGCPHYS GCPhysLast = GCPhys + cbRange - 1 >= GCPhys
688 ? GCPhys + cbRange - 1
689 : ~(RTGCPHYS)0;
690
691 PFNPGMR3DBGFIXEDMEMSCAN pfnMemScan;
692 pgmR3DbgSelectMemScanFunction(&pfnMemScan, (uint32_t)GCPhysAlign, cbNeedle);
693
694 /*
695 * Search the memory - ignore MMIO and zero pages, also don't
696 * bother to match across ranges.
697 */
698 pgmLock(pVM);
699 for (PPGMRAMRANGE pRam = pVM->pgm.s.CTX_SUFF(pRamRangesX);
700 pRam;
701 pRam = pRam->CTX_SUFF(pNext))
702 {
703 /*
704 * If the search range starts prior to the current ram range record,
705 * adjust the search range and possibly conclude the search.
706 */
707 RTGCPHYS off;
708 if (GCPhys < pRam->GCPhys)
709 {
710 if (GCPhysLast < pRam->GCPhys)
711 break;
712 GCPhys = pRam->GCPhys;
713 off = 0;
714 }
715 else
716 off = GCPhys - pRam->GCPhys;
717 if (off < pRam->cb)
718 {
719 /*
720 * Iterate the relevant pages.
721 */
722 uint8_t abPrev[MAX_NEEDLE_SIZE];
723 size_t cbPrev = 0;
724 const uint32_t cPages = pRam->cb >> PAGE_SHIFT;
725 uint32_t iPage = off >> PAGE_SHIFT;
726 uint32_t offPage = GCPhys & PAGE_OFFSET_MASK;
727 GCPhys &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
728 for (;; offPage = 0)
729 {
730 PPGMPAGE pPage = &pRam->aPages[iPage];
731 if ( ( !PGM_PAGE_IS_ZERO(pPage)
732 || fAllZero)
733 && !PGM_PAGE_IS_MMIO_OR_ALIAS(pPage)
734 && !PGM_PAGE_IS_BALLOONED(pPage))
735 {
736 void const *pvPage;
737 PGMPAGEMAPLOCK Lock;
738 int rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, GCPhys, &pvPage, &Lock);
739 if (RT_SUCCESS(rc))
740 {
741 int32_t offHit = offPage;
742 bool fRc;
743 if (GCPhysAlign < PAGE_SIZE)
744 {
745 uint32_t cbSearch = (GCPhys ^ GCPhysLast) & ~(RTGCPHYS)PAGE_OFFSET_MASK
746 ? PAGE_SIZE - (uint32_t)offPage
747 : (GCPhysLast & PAGE_OFFSET_MASK) + 1 - (uint32_t)offPage;
748 fRc = pgmR3DbgScanPage((uint8_t const *)pvPage, &offHit, cbSearch, (uint32_t)GCPhysAlign,
749 pabNeedle, cbNeedle, pfnMemScan, &abPrev[0], &cbPrev);
750 }
751 else
752 fRc = memcmp(pvPage, pabNeedle, cbNeedle) == 0
753 && (GCPhysLast - GCPhys) >= cbNeedle;
754 PGMPhysReleasePageMappingLock(pVM, &Lock);
755 if (fRc)
756 {
757 *pGCPhysHit = GCPhys + offHit;
758 pgmUnlock(pVM);
759 return VINF_SUCCESS;
760 }
761 }
762 else
763 cbPrev = 0; /* ignore error. */
764 }
765 else
766 cbPrev = 0;
767
768 /* advance to the next page. */
769 GCPhys += (RTGCPHYS)cIncPages << PAGE_SHIFT;
770 if (GCPhys >= GCPhysLast) /* (may not always hit, but we're run out of ranges.) */
771 {
772 pgmUnlock(pVM);
773 return VERR_DBGF_MEM_NOT_FOUND;
774 }
775 iPage += cIncPages;
776 if ( iPage < cIncPages
777 || iPage >= cPages)
778 break;
779 }
780 }
781 }
782 pgmUnlock(pVM);
783 return VERR_DBGF_MEM_NOT_FOUND;
784}
785
786
787/**
788 * Scans (guest) virtual memory for a byte string.
789 *
790 * @returns VBox status codes:
791 * @retval VINF_SUCCESS and *pGCPtrHit on success.
792 * @retval VERR_DBGF_MEM_NOT_FOUND if not found.
793 * @retval VERR_INVALID_POINTER if any of the pointer arguments are invalid.
794 * @retval VERR_INVALID_ARGUMENT if any other arguments are invalid.
795 *
796 * @param pVM Pointer to the VM.
797 * @param pVCpu The CPU context to search in.
798 * @param GCPtr Where to start searching.
799 * @param GCPtrAlign The alignment of the needle. Must be a power of two
800 * and less or equal to 4GB.
801 * @param cbRange The number of bytes to search. Max 256 bytes.
802 * @param pabNeedle The byte string to search for.
803 * @param cbNeedle The length of the byte string.
804 * @param pGCPtrHit Where to store the address of the first occurrence on success.
805 */
806VMMR3_INT_DECL(int) PGMR3DbgScanVirtual(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, RTGCPTR cbRange, RTGCPTR GCPtrAlign,
807 const uint8_t *pabNeedle, size_t cbNeedle, PRTGCUINTPTR pGCPtrHit)
808{
809 VMCPU_ASSERT_EMT(pVCpu);
810
811 /*
812 * Validate and adjust the input a bit.
813 */
814 if (!VALID_PTR(pGCPtrHit))
815 return VERR_INVALID_POINTER;
816 *pGCPtrHit = 0;
817
818 if (!VALID_PTR(pabNeedle))
819 return VERR_INVALID_POINTER;
820 if (!cbNeedle)
821 return VERR_INVALID_PARAMETER;
822 if (cbNeedle > MAX_NEEDLE_SIZE)
823 return VERR_INVALID_PARAMETER;
824
825 if (!cbRange)
826 return VERR_DBGF_MEM_NOT_FOUND;
827 if (GCPtr + cbNeedle - 1 < GCPtr)
828 return VERR_DBGF_MEM_NOT_FOUND;
829
830 if (!GCPtrAlign)
831 return VERR_INVALID_PARAMETER;
832 if (GCPtrAlign > UINT32_MAX)
833 return VERR_NOT_POWER_OF_TWO;
834 if (GCPtrAlign & (GCPtrAlign - 1))
835 return VERR_INVALID_PARAMETER;
836
837 if (GCPtr & (GCPtrAlign - 1))
838 {
839 RTGCPTR Adj = GCPtrAlign - (GCPtr & (GCPtrAlign - 1));
840 if ( cbRange <= Adj
841 || GCPtr + Adj < GCPtr)
842 return VERR_DBGF_MEM_NOT_FOUND;
843 GCPtr += Adj;
844 cbRange -= Adj;
845 }
846
847 /* Only paged protected mode or long mode here, use the physical scan for
848 the other modes. */
849 PGMMODE enmMode = PGMGetGuestMode(pVCpu);
850 AssertReturn(PGMMODE_WITH_PAGING(enmMode), VERR_PGM_NOT_USED_IN_MODE);
851
852 /*
853 * Search the memory - ignore MMIO, zero and not-present pages.
854 */
855 const bool fAllZero = ASMMemIsAll8(pabNeedle, cbNeedle, 0) == NULL;
856 RTGCPTR GCPtrMask = PGMMODE_IS_LONG_MODE(enmMode) ? UINT64_MAX : UINT32_MAX;
857 uint8_t abPrev[MAX_NEEDLE_SIZE];
858 size_t cbPrev = 0;
859 const uint32_t cIncPages = GCPtrAlign <= PAGE_SIZE
860 ? 1
861 : GCPtrAlign >> PAGE_SHIFT;
862 const RTGCPTR GCPtrLast = GCPtr + cbRange - 1 >= GCPtr
863 ? (GCPtr + cbRange - 1) & GCPtrMask
864 : GCPtrMask;
865 RTGCPTR cPages = (((GCPtrLast - GCPtr) + (GCPtr & PAGE_OFFSET_MASK)) >> PAGE_SHIFT) + 1;
866 uint32_t offPage = GCPtr & PAGE_OFFSET_MASK;
867 GCPtr &= ~(RTGCPTR)PAGE_OFFSET_MASK;
868
869 PFNPGMR3DBGFIXEDMEMSCAN pfnMemScan;
870 pgmR3DbgSelectMemScanFunction(&pfnMemScan, (uint32_t)GCPtrAlign, cbNeedle);
871
872 uint32_t cYieldCountDown = 4096;
873 pgmLock(pVM);
874 for (;; offPage = 0)
875 {
876 PGMPTWALKGST Walk;
877 int rc = pgmGstPtWalk(pVCpu, GCPtr, &Walk);
878 if (RT_SUCCESS(rc) && Walk.u.Core.fSucceeded)
879 {
880 PPGMPAGE pPage = pgmPhysGetPage(pVM, Walk.u.Core.GCPhys);
881 if ( pPage
882 && ( !PGM_PAGE_IS_ZERO(pPage)
883 || fAllZero)
884 && !PGM_PAGE_IS_MMIO_OR_ALIAS(pPage)
885 && !PGM_PAGE_IS_BALLOONED(pPage))
886 {
887 void const *pvPage;
888 PGMPAGEMAPLOCK Lock;
889 rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, Walk.u.Core.GCPhys, &pvPage, &Lock);
890 if (RT_SUCCESS(rc))
891 {
892 int32_t offHit = offPage;
893 bool fRc;
894 if (GCPtrAlign < PAGE_SIZE)
895 {
896 uint32_t cbSearch = cPages > 0
897 ? PAGE_SIZE - (uint32_t)offPage
898 : (GCPtrLast & PAGE_OFFSET_MASK) + 1 - (uint32_t)offPage;
899 fRc = pgmR3DbgScanPage((uint8_t const *)pvPage, &offHit, cbSearch, (uint32_t)GCPtrAlign,
900 pabNeedle, cbNeedle, pfnMemScan, &abPrev[0], &cbPrev);
901 }
902 else
903 fRc = memcmp(pvPage, pabNeedle, cbNeedle) == 0
904 && (GCPtrLast - GCPtr) >= cbNeedle;
905 PGMPhysReleasePageMappingLock(pVM, &Lock);
906 if (fRc)
907 {
908 *pGCPtrHit = GCPtr + offHit;
909 pgmUnlock(pVM);
910 return VINF_SUCCESS;
911 }
912 }
913 else
914 cbPrev = 0; /* ignore error. */
915 }
916 else
917 cbPrev = 0;
918 }
919 else
920 {
921 Assert(Walk.enmType != PGMPTWALKGSTTYPE_INVALID);
922 Assert(!Walk.u.Core.fSucceeded);
923 cbPrev = 0; /* ignore error. */
924
925 /*
926 * Try skip as much as possible. No need to figure out that a PDE
927 * is not present 512 times!
928 */
929 uint64_t cPagesCanSkip;
930 switch (Walk.u.Core.uLevel)
931 {
932 case 1:
933 /* page level, use cIncPages */
934 cPagesCanSkip = 1;
935 break;
936 case 2:
937 if (Walk.enmType == PGMPTWALKGSTTYPE_32BIT)
938 {
939 cPagesCanSkip = X86_PG_ENTRIES - ((GCPtr >> X86_PT_SHIFT) & X86_PT_MASK);
940 Assert(!((GCPtr + ((RTGCPTR)cPagesCanSkip << X86_PT_PAE_SHIFT)) & (RT_BIT_64(X86_PD_SHIFT) - 1)));
941 }
942 else
943 {
944 cPagesCanSkip = X86_PG_PAE_ENTRIES - ((GCPtr >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK);
945 Assert(!((GCPtr + ((RTGCPTR)cPagesCanSkip << X86_PT_PAE_SHIFT)) & (RT_BIT_64(X86_PD_PAE_SHIFT) - 1)));
946 }
947 break;
948 case 3:
949 cPagesCanSkip = (X86_PG_PAE_ENTRIES - ((GCPtr >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK)) * X86_PG_PAE_ENTRIES
950 - ((GCPtr >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK);
951 Assert(!((GCPtr + ((RTGCPTR)cPagesCanSkip << X86_PT_PAE_SHIFT)) & (RT_BIT_64(X86_PDPT_SHIFT) - 1)));
952 break;
953 case 4:
954 cPagesCanSkip = (X86_PG_PAE_ENTRIES - ((GCPtr >> X86_PDPT_SHIFT) & X86_PDPT_MASK_AMD64))
955 * X86_PG_PAE_ENTRIES * X86_PG_PAE_ENTRIES
956 - ((((GCPtr >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK)) * X86_PG_PAE_ENTRIES)
957 - (( GCPtr >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK);
958 Assert(!((GCPtr + ((RTGCPTR)cPagesCanSkip << X86_PT_PAE_SHIFT)) & (RT_BIT_64(X86_PML4_SHIFT) - 1)));
959 break;
960 case 8:
961 /* The CR3 value is bad, forget the whole search. */
962 cPagesCanSkip = cPages;
963 break;
964 default:
965 AssertMsgFailed(("%d\n", Walk.u.Core.uLevel));
966 cPagesCanSkip = 0;
967 break;
968 }
969 if (cPages <= cPagesCanSkip)
970 break;
971 if (cPagesCanSkip >= cIncPages)
972 {
973 cPages -= cPagesCanSkip;
974 GCPtr += (RTGCPTR)cPagesCanSkip << X86_PT_PAE_SHIFT;
975 continue;
976 }
977 }
978
979 /* advance to the next page. */
980 if (cPages <= cIncPages)
981 break;
982 cPages -= cIncPages;
983 GCPtr += (RTGCPTR)cIncPages << X86_PT_PAE_SHIFT;
984
985 /* Yield the PGM lock every now and then. */
986 if (!--cYieldCountDown)
987 {
988 PDMR3CritSectYield(&pVM->pgm.s.CritSectX);
989 cYieldCountDown = 4096;
990 }
991 }
992 pgmUnlock(pVM);
993 return VERR_DBGF_MEM_NOT_FOUND;
994}
995
996
997/**
998 * Initializes the dumper state.
999 *
1000 * @param pState The state to initialize.
1001 * @param pVM Pointer to the VM.
1002 * @param fFlags The flags.
1003 * @param u64FirstAddr The first address.
1004 * @param u64LastAddr The last address.
1005 * @param pHlp The output helpers.
1006 */
1007static void pgmR3DumpHierarchyInitState(PPGMR3DUMPHIERARCHYSTATE pState, PVM pVM, uint32_t fFlags,
1008 uint64_t u64FirstAddr, uint64_t u64LastAddr, PCDBGFINFOHLP pHlp)
1009{
1010 pState->pVM = pVM;
1011 pState->pHlp = pHlp ? pHlp : DBGFR3InfoLogHlp();
1012 pState->fPse = !!(fFlags & (DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME));
1013 pState->fPae = !!(fFlags & (DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME));
1014 pState->fLme = !!(fFlags & DBGFPGDMP_FLAGS_LME);
1015 pState->fNp = !!(fFlags & DBGFPGDMP_FLAGS_NP);
1016 pState->fEpt = !!(fFlags & DBGFPGDMP_FLAGS_EPT);
1017 pState->fNxe = !!(fFlags & DBGFPGDMP_FLAGS_NXE);
1018 pState->cchAddress = pState->fLme ? 16 : 8;
1019 pState->uLastRsvdBit = pState->fNxe ? 62 : 63;
1020 pState->fDumpPageInfo = !!(fFlags & DBGFPGDMP_FLAGS_PAGE_INFO);
1021 pState->fPrintHeader = !!(fFlags & DBGFPGDMP_FLAGS_HEADER);
1022 pState->fPrintCr3 = !!(fFlags & DBGFPGDMP_FLAGS_PRINT_CR3);
1023 pState->afReserved[0] = false;
1024 pState->afReserved[1] = false;
1025 pState->afReserved[2] = false;
1026 pState->afReserved[3] = false;
1027 pState->afReserved[4] = false;
1028 pState->u64Address = u64FirstAddr;
1029 pState->u64FirstAddress = u64FirstAddr;
1030 pState->u64LastAddress = u64LastAddr;
1031 pState->u64HighReservedBits = pState->uLastRsvdBit == 62 ? UINT64_C(0x7ff) << 52 : UINT64_C(0xfff) << 52;
1032 pState->cLeaves = 0;
1033}
1034
1035
1036/**
1037 * The simple way out, too tired to think of a more elegant solution.
1038 *
1039 * @returns The base address of this page table/directory/whatever.
1040 * @param pState The state where we get the current address.
1041 * @param cShift The shift count for the table entries.
1042 * @param cEntries The number of table entries.
1043 * @param piFirst Where to return the table index of the first
1044 * entry to dump.
1045 * @param piLast Where to return the table index of the last
1046 * entry.
1047 */
1048static uint64_t pgmR3DumpHierarchyCalcRange(PPGMR3DUMPHIERARCHYSTATE pState, uint32_t cShift, uint32_t cEntries,
1049 uint32_t *piFirst, uint32_t *piLast)
1050{
1051 const uint64_t iBase = (pState->u64Address >> cShift) & ~(uint64_t)(cEntries - 1);
1052 const uint64_t iFirst = pState->u64FirstAddress >> cShift;
1053 const uint64_t iLast = pState->u64LastAddress >> cShift;
1054
1055 if ( iBase >= iFirst
1056 && iBase + cEntries - 1 <= iLast)
1057 {
1058 /* full range. */
1059 *piFirst = 0;
1060 *piLast = cEntries - 1;
1061 }
1062 else if ( iBase + cEntries - 1 < iFirst
1063 || iBase > iLast)
1064 {
1065 /* no match */
1066 *piFirst = cEntries;
1067 *piLast = 0;
1068 }
1069 else
1070 {
1071 /* partial overlap */
1072 *piFirst = iBase <= iFirst
1073 ? iFirst - iBase
1074 : 0;
1075 *piLast = iBase + cEntries - 1 <= iLast
1076 ? cEntries - 1
1077 : iLast - iBase;
1078 }
1079
1080 return iBase << cShift;
1081}
1082
1083
1084/**
1085 * Maps/finds the shadow page.
1086 *
1087 * @returns VBox status code.
1088 * @param pState The dumper state.
1089 * @param HCPhys The physical address of the shadow page.
1090 * @param pszDesc The description.
1091 * @param fIsMapping Set if it's a mapping.
1092 * @param ppv Where to return the pointer.
1093 */
1094static int pgmR3DumpHierarchyShwMapPage(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, const char *pszDesc,
1095 bool fIsMapping, void const **ppv)
1096{
1097 void *pvPage;
1098 if (!fIsMapping)
1099 {
1100 int rc = MMPagePhys2PageTry(pState->pVM, HCPhys, &pvPage);
1101 if (RT_FAILURE(rc))
1102 {
1103 pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! %s at HCPhys=%RHp was not found in the page pool!\n",
1104 pState->cchAddress, pState->u64Address, pszDesc, HCPhys);
1105 return rc;
1106 }
1107 }
1108 else
1109 {
1110 pvPage = NULL;
1111 for (PPGMMAPPING pMap = pState->pVM->pgm.s.pMappingsR3; pMap; pMap = pMap->pNextR3)
1112 {
1113 uint64_t off = pState->u64Address - pMap->GCPtr;
1114 if (off < pMap->cb)
1115 {
1116 const int iPDE = (uint32_t)(off >> X86_PD_SHIFT);
1117 const int iSub = (int)((off >> X86_PD_PAE_SHIFT) & 1); /* MSC is a pain sometimes */
1118 if ((iSub ? pMap->aPTs[iPDE].HCPhysPaePT1 : pMap->aPTs[iPDE].HCPhysPaePT0) != HCPhys)
1119 pState->pHlp->pfnPrintf(pState->pHlp,
1120 "%0*llx error! Mapping error! PT %d has HCPhysPT=%RHp not %RHp is in the PD.\n",
1121 pState->cchAddress, pState->u64Address, iPDE,
1122 iSub ? pMap->aPTs[iPDE].HCPhysPaePT1 : pMap->aPTs[iPDE].HCPhysPaePT0, HCPhys);
1123 pvPage = &pMap->aPTs[iPDE].paPaePTsR3[iSub];
1124 break;
1125 }
1126 }
1127 if (!pvPage)
1128 {
1129 pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! PT mapping %s at HCPhys=%RHp was not found in the page pool!\n",
1130 pState->cchAddress, pState->u64Address, pszDesc, HCPhys);
1131 return VERR_INVALID_PARAMETER;
1132 }
1133 }
1134 *ppv = pvPage;
1135 return VINF_SUCCESS;
1136}
1137
1138
1139/**
1140 * Dumps the a shadow page summary or smth.
1141 *
1142 * @param pState The dumper state.
1143 * @param HCPhys The page address.
1144 */
1145static void pgmR3DumpHierarchyShwTablePageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys)
1146{
1147 pgmLock(pState->pVM);
1148 char szPage[80];
1149 PPGMPOOLPAGE pPage = pgmPoolQueryPageForDbg(pState->pVM->pgm.s.CTX_SUFF(pPool), HCPhys);
1150 if (pPage)
1151 RTStrPrintf(szPage, sizeof(szPage), " idx=0i%u", pPage->idx);
1152 else
1153 {
1154 /* probably a mapping */
1155 strcpy(szPage, " not found");
1156 for (PPGMMAPPING pMap = pState->pVM->pgm.s.pMappingsR3; pMap; pMap = pMap->pNextR3)
1157 {
1158 uint64_t off = pState->u64Address - pMap->GCPtr;
1159 if (off < pMap->cb)
1160 {
1161 const int iPDE = (uint32_t)(off >> X86_PD_SHIFT);
1162 if (pMap->aPTs[iPDE].HCPhysPT == HCPhys)
1163 RTStrPrintf(szPage, sizeof(szPage), " #%u: %s", iPDE, pMap->pszDesc);
1164 else if (pMap->aPTs[iPDE].HCPhysPaePT0 == HCPhys)
1165 RTStrPrintf(szPage, sizeof(szPage), " #%u/0: %s", iPDE, pMap->pszDesc);
1166 else if (pMap->aPTs[iPDE].HCPhysPaePT1 == HCPhys)
1167 RTStrPrintf(szPage, sizeof(szPage), " #%u/1: %s", iPDE, pMap->pszDesc);
1168 else
1169 continue;
1170 break;
1171 }
1172 }
1173 }
1174 pgmUnlock(pState->pVM);
1175 pState->pHlp->pfnPrintf(pState->pHlp, "%s", szPage);
1176}
1177
1178
1179/**
1180 * Figures out which guest page this is and dumps a summary.
1181 *
1182 * @param pState The dumper state.
1183 * @param HCPhys The page address.
1184 * @param cbPage The page size.
1185 */
1186static void pgmR3DumpHierarchyShwGuestPageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, uint32_t cbPage)
1187{
1188 char szPage[80];
1189 RTGCPHYS GCPhys;
1190 int rc = PGMR3DbgHCPhys2GCPhys(pState->pVM->pUVM, HCPhys, &GCPhys);
1191 if (RT_SUCCESS(rc))
1192 {
1193 pgmLock(pState->pVM);
1194 PCPGMPAGE pPage = pgmPhysGetPage(pState->pVM, GCPhys);
1195 if (pPage)
1196 RTStrPrintf(szPage, sizeof(szPage), "%R[pgmpage]", pPage);
1197 else
1198 strcpy(szPage, "not found");
1199 pgmUnlock(pState->pVM);
1200 pState->pHlp->pfnPrintf(pState->pHlp, " -> %RGp %s", GCPhys, szPage);
1201 }
1202 else
1203 {
1204 /* check the heap */
1205 uint32_t cbAlloc;
1206 rc = MMR3HyperQueryInfoFromHCPhys(pState->pVM, HCPhys, szPage, sizeof(szPage), &cbAlloc);
1207 if (RT_SUCCESS(rc))
1208 pState->pHlp->pfnPrintf(pState->pHlp, " %s %#x bytes", szPage, cbAlloc);
1209 else
1210 pState->pHlp->pfnPrintf(pState->pHlp, " not found");
1211 }
1212 NOREF(cbPage);
1213}
1214
1215
1216/**
1217 * Dumps a PAE shadow page table.
1218 *
1219 * @returns VBox status code (VINF_SUCCESS).
1220 * @param pState The dumper state.
1221 * @param HCPhys The page table address.
1222 * @param fIsMapping Whether it is a mapping.
1223 */
1224static int pgmR3DumpHierarchyShwPaePT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, bool fIsMapping)
1225{
1226 PCPGMSHWPTPAE pPT;
1227 int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page table", fIsMapping, (void const **)&pPT);
1228 if (RT_FAILURE(rc))
1229 return rc;
1230
1231 uint32_t iFirst, iLast;
1232 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
1233 for (uint32_t i = iFirst; i <= iLast; i++)
1234 if (PGMSHWPTEPAE_GET_U(pPT->a[i]) & X86_PTE_P)
1235 {
1236 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PT_PAE_SHIFT);
1237 if (PGMSHWPTEPAE_IS_P(pPT->a[i]))
1238 {
1239 X86PTEPAE Pte;
1240 Pte.u = PGMSHWPTEPAE_GET_U(pPT->a[i]);
1241 pState->pHlp->pfnPrintf(pState->pHlp,
1242 pState->fLme /*P R S A D G WT CD AT NX 4M a p ? */
1243 ? "%016llx 3 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx"
1244 : "%08llx 2 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx",
1245 pState->u64Address,
1246 Pte.n.u1Write ? 'W' : 'R',
1247 Pte.n.u1User ? 'U' : 'S',
1248 Pte.n.u1Accessed ? 'A' : '-',
1249 Pte.n.u1Dirty ? 'D' : '-',
1250 Pte.n.u1Global ? 'G' : '-',
1251 Pte.n.u1WriteThru ? "WT" : "--",
1252 Pte.n.u1CacheDisable? "CD" : "--",
1253 Pte.n.u1PAT ? "AT" : "--",
1254 Pte.n.u1NoExecute ? "NX" : "--",
1255 Pte.u & PGM_PTFLAGS_TRACK_DIRTY ? 'd' : '-',
1256 Pte.u & RT_BIT(10) ? '1' : '0',
1257 Pte.u & PGM_PTFLAGS_CSAM_VALIDATED? 'v' : '-',
1258 Pte.u & X86_PTE_PAE_PG_MASK);
1259 if (pState->fDumpPageInfo)
1260 pgmR3DumpHierarchyShwGuestPageInfo(pState, Pte.u & X86_PTE_PAE_PG_MASK, _4K);
1261 if ((Pte.u >> 52) & 0x7ff)
1262 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx%s", (Pte.u >> 52) & 0x7ff, pState->fLme ? "" : "!");
1263 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1264 }
1265 else if ( (PGMSHWPTEPAE_GET_U(pPT->a[i]) & (pState->pVM->pgm.s.HCPhysInvMmioPg | X86_PTE_PAE_MBZ_MASK_NO_NX))
1266 == (pState->pVM->pgm.s.HCPhysInvMmioPg | X86_PTE_PAE_MBZ_MASK_NO_NX))
1267 pState->pHlp->pfnPrintf(pState->pHlp,
1268 pState->fLme
1269 ? "%016llx 3 | invalid / MMIO optimization\n"
1270 : "%08llx 2 | invalid / MMIO optimization\n",
1271 pState->u64Address);
1272 else
1273 pState->pHlp->pfnPrintf(pState->pHlp,
1274 pState->fLme
1275 ? "%016llx 3 | invalid: %RX64\n"
1276 : "%08llx 2 | invalid: %RX64\n",
1277 pState->u64Address, PGMSHWPTEPAE_GET_U(pPT->a[i]));
1278 pState->cLeaves++;
1279 }
1280 return VINF_SUCCESS;
1281}
1282
1283
1284/**
1285 * Dumps a PAE shadow page directory table.
1286 *
1287 * @returns VBox status code (VINF_SUCCESS).
1288 * @param pState The dumper state.
1289 * @param HCPhys The physical address of the page directory table.
1290 * @param cMaxDepth The maximum depth.
1291 */
1292static int pgmR3DumpHierarchyShwPaePD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1293{
1294 PCX86PDPAE pPD;
1295 int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page directory", false, (void const **)&pPD);
1296 if (RT_FAILURE(rc))
1297 return rc;
1298
1299 Assert(cMaxDepth > 0);
1300 cMaxDepth--;
1301
1302 uint32_t iFirst, iLast;
1303 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
1304 for (uint32_t i = iFirst; i <= iLast; i++)
1305 {
1306 X86PDEPAE Pde = pPD->a[i];
1307 if (Pde.n.u1Present)
1308 {
1309 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PD_PAE_SHIFT);
1310 if (Pde.b.u1Size)
1311 {
1312 pState->pHlp->pfnPrintf(pState->pHlp,
1313 pState->fLme /*P R S A D G WT CD AT NX 2M a p ? phys*/
1314 ? "%016llx 2 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx"
1315 : "%08llx 1 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx",
1316 pState->u64Address,
1317 Pde.b.u1Write ? 'W' : 'R',
1318 Pde.b.u1User ? 'U' : 'S',
1319 Pde.b.u1Accessed ? 'A' : '-',
1320 Pde.b.u1Dirty ? 'D' : '-',
1321 Pde.b.u1Global ? 'G' : '-',
1322 Pde.b.u1WriteThru ? "WT" : "--",
1323 Pde.b.u1CacheDisable? "CD" : "--",
1324 Pde.b.u1PAT ? "AT" : "--",
1325 Pde.b.u1NoExecute ? "NX" : "--",
1326 Pde.u & RT_BIT_64(9) ? '1' : '0',
1327 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1328 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1329 Pde.u & X86_PDE2M_PAE_PG_MASK);
1330 if (pState->fDumpPageInfo)
1331 pgmR3DumpHierarchyShwGuestPageInfo(pState, Pde.u & X86_PDE2M_PAE_PG_MASK, _2M);
1332 if ((Pde.u >> 52) & 0x7ff)
1333 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx%s", (Pde.u >> 52) & 0x7ff, pState->fLme ? "" : "!");
1334 if ((Pde.u >> 13) & 0xff)
1335 pState->pHlp->pfnPrintf(pState->pHlp, " 20:13=%02llx%s", (Pde.u >> 13) & 0x0ff, pState->fLme ? "" : "!");
1336 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1337
1338 pState->cLeaves++;
1339 }
1340 else
1341 {
1342 pState->pHlp->pfnPrintf(pState->pHlp,
1343 pState->fLme /*P R S A D G WT CD AT NX 4M a p ? phys */
1344 ? "%016llx 2 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx"
1345 : "%08llx 1 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx",
1346 pState->u64Address,
1347 Pde.n.u1Write ? 'W' : 'R',
1348 Pde.n.u1User ? 'U' : 'S',
1349 Pde.n.u1Accessed ? 'A' : '-',
1350 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
1351 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
1352 Pde.n.u1WriteThru ? "WT" : "--",
1353 Pde.n.u1CacheDisable? "CD" : "--",
1354 Pde.n.u1NoExecute ? "NX" : "--",
1355 Pde.u & RT_BIT_64(9) ? '1' : '0',
1356 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1357 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1358 Pde.u & X86_PDE_PAE_PG_MASK);
1359 if (pState->fDumpPageInfo)
1360 pgmR3DumpHierarchyShwTablePageInfo(pState, Pde.u & X86_PDE_PAE_PG_MASK);
1361 if ((Pde.u >> 52) & 0x7ff)
1362 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx!", (Pde.u >> 52) & 0x7ff);
1363 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1364
1365 if (cMaxDepth)
1366 {
1367 int rc2 = pgmR3DumpHierarchyShwPaePT(pState, Pde.u & X86_PDE_PAE_PG_MASK, !!(Pde.u & PGM_PDFLAGS_MAPPING));
1368 if (rc2 < rc && RT_SUCCESS(rc))
1369 rc = rc2;
1370 }
1371 else
1372 pState->cLeaves++;
1373 }
1374 }
1375 }
1376 return rc;
1377}
1378
1379
1380/**
1381 * Dumps a PAE shadow page directory pointer table.
1382 *
1383 * @returns VBox status code (VINF_SUCCESS).
1384 * @param pState The dumper state.
1385 * @param HCPhys The physical address of the page directory pointer table.
1386 * @param cMaxDepth The maximum depth.
1387 */
1388static int pgmR3DumpHierarchyShwPaePDPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1389{
1390 /* Fend of addresses that are out of range in PAE mode - simplifies the code below. */
1391 if (!pState->fLme && pState->u64Address >= _4G)
1392 return VINF_SUCCESS;
1393
1394 PCX86PDPT pPDPT;
1395 int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page directory pointer table", false, (void const **)&pPDPT);
1396 if (RT_FAILURE(rc))
1397 return rc;
1398
1399 Assert(cMaxDepth > 0);
1400 cMaxDepth--;
1401
1402 uint32_t iFirst, iLast;
1403 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PDPT_SHIFT,
1404 pState->fLme ? X86_PG_AMD64_PDPE_ENTRIES : X86_PG_PAE_PDPE_ENTRIES,
1405 &iFirst, &iLast);
1406 for (uint32_t i = iFirst; i <= iLast; i++)
1407 {
1408 X86PDPE Pdpe = pPDPT->a[i];
1409 if (Pdpe.n.u1Present)
1410 {
1411 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PDPT_SHIFT);
1412 if (pState->fLme)
1413 {
1414 pState->pHlp->pfnPrintf(pState->pHlp, /*P R S A D G WT CD AT NX .. a p ? */
1415 "%016llx 1 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
1416 pState->u64Address,
1417 Pdpe.lm.u1Write ? 'W' : 'R',
1418 Pdpe.lm.u1User ? 'U' : 'S',
1419 Pdpe.lm.u1Accessed ? 'A' : '-',
1420 Pdpe.lm.u3Reserved & 1? '?' : '.', /* ignored */
1421 Pdpe.lm.u3Reserved & 4? '!' : '.', /* mbz */
1422 Pdpe.lm.u1WriteThru ? "WT" : "--",
1423 Pdpe.lm.u1CacheDisable? "CD" : "--",
1424 Pdpe.lm.u3Reserved & 2? "!" : "..",/* mbz */
1425 Pdpe.lm.u1NoExecute ? "NX" : "--",
1426 Pdpe.u & RT_BIT(9) ? '1' : '0',
1427 Pdpe.u & PGM_PLXFLAGS_PERMANENT ? 'p' : '-',
1428 Pdpe.u & RT_BIT(11) ? '1' : '0',
1429 Pdpe.u & X86_PDPE_PG_MASK);
1430 if (pState->fDumpPageInfo)
1431 pgmR3DumpHierarchyShwTablePageInfo(pState, Pdpe.u & X86_PDPE_PG_MASK);
1432 if ((Pdpe.u >> 52) & 0x7ff)
1433 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx", (Pdpe.u >> 52) & 0x7ff);
1434 }
1435 else
1436 {
1437 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX .. a p ? */
1438 "%08llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
1439 pState->u64Address,
1440 Pdpe.n.u2Reserved & 1? '!' : '.', /* mbz */
1441 Pdpe.n.u2Reserved & 2? '!' : '.', /* mbz */
1442 Pdpe.n.u4Reserved & 1? '!' : '.', /* mbz */
1443 Pdpe.n.u4Reserved & 2? '!' : '.', /* mbz */
1444 Pdpe.n.u4Reserved & 8? '!' : '.', /* mbz */
1445 Pdpe.n.u1WriteThru ? "WT" : "--",
1446 Pdpe.n.u1CacheDisable? "CD" : "--",
1447 Pdpe.n.u4Reserved & 2? "!" : "..",/* mbz */
1448 Pdpe.lm.u1NoExecute ? "!!" : "..",/* mbz */
1449 Pdpe.u & RT_BIT(9) ? '1' : '0',
1450 Pdpe.u & PGM_PLXFLAGS_PERMANENT ? 'p' : '-',
1451 Pdpe.u & RT_BIT(11) ? '1' : '0',
1452 Pdpe.u & X86_PDPE_PG_MASK);
1453 if (pState->fDumpPageInfo)
1454 pgmR3DumpHierarchyShwTablePageInfo(pState, Pdpe.u & X86_PDPE_PG_MASK);
1455 if ((Pdpe.u >> 52) & 0xfff)
1456 pState->pHlp->pfnPrintf(pState->pHlp, " 63:52=%03llx!", (Pdpe.u >> 52) & 0xfff);
1457 }
1458 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1459
1460 if (cMaxDepth)
1461 {
1462 int rc2 = pgmR3DumpHierarchyShwPaePD(pState, Pdpe.u & X86_PDPE_PG_MASK, cMaxDepth);
1463 if (rc2 < rc && RT_SUCCESS(rc))
1464 rc = rc2;
1465 }
1466 else
1467 pState->cLeaves++;
1468 }
1469 }
1470 return rc;
1471}
1472
1473
1474/**
1475 * Dumps a 32-bit shadow page table.
1476 *
1477 * @returns VBox status code (VINF_SUCCESS).
1478 * @param pVM Pointer to the VM.
1479 * @param HCPhys The physical address of the table.
1480 * @param cMaxDepth The maximum depth.
1481 */
1482static int pgmR3DumpHierarchyShwPaePML4(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1483{
1484 PCX86PML4 pPML4;
1485 int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page map level 4", false, (void const **)&pPML4);
1486 if (RT_FAILURE(rc))
1487 return rc;
1488
1489 Assert(cMaxDepth);
1490 cMaxDepth--;
1491
1492 /*
1493 * This is a bit tricky as we're working on unsigned addresses while the
1494 * AMD64 spec uses signed tricks.
1495 */
1496 uint32_t iFirst = (pState->u64FirstAddress >> X86_PML4_SHIFT) & X86_PML4_MASK;
1497 uint32_t iLast = (pState->u64LastAddress >> X86_PML4_SHIFT) & X86_PML4_MASK;
1498 if ( pState->u64LastAddress <= UINT64_C(0x00007fffffffffff)
1499 || pState->u64FirstAddress >= UINT64_C(0xffff800000000000))
1500 { /* Simple, nothing to adjust */ }
1501 else if (pState->u64FirstAddress <= UINT64_C(0x00007fffffffffff))
1502 iLast = X86_PG_AMD64_ENTRIES / 2 - 1;
1503 else if (pState->u64LastAddress >= UINT64_C(0xffff800000000000))
1504 iFirst = X86_PG_AMD64_ENTRIES / 2;
1505 else
1506 iFirst = X86_PG_AMD64_ENTRIES; /* neither address is canonical */
1507
1508 for (uint32_t i = iFirst; i <= iLast; i++)
1509 {
1510 X86PML4E Pml4e = pPML4->a[i];
1511 if (Pml4e.n.u1Present)
1512 {
1513 pState->u64Address = ((uint64_t)i << X86_PML4_SHIFT)
1514 | (i >= RT_ELEMENTS(pPML4->a) / 2 ? UINT64_C(0xffff000000000000) : 0);
1515 pState->pHlp->pfnPrintf(pState->pHlp, /*P R S A D G WT CD AT NX 4M a p ? */
1516 "%016llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
1517 pState->u64Address,
1518 Pml4e.n.u1Write ? 'W' : 'R',
1519 Pml4e.n.u1User ? 'U' : 'S',
1520 Pml4e.n.u1Accessed ? 'A' : '-',
1521 Pml4e.n.u3Reserved & 1? '?' : '.', /* ignored */
1522 Pml4e.n.u3Reserved & 4? '!' : '.', /* mbz */
1523 Pml4e.n.u1WriteThru ? "WT" : "--",
1524 Pml4e.n.u1CacheDisable? "CD" : "--",
1525 Pml4e.n.u3Reserved & 2? "!" : "..",/* mbz */
1526 Pml4e.n.u1NoExecute ? "NX" : "--",
1527 Pml4e.u & RT_BIT(9) ? '1' : '0',
1528 Pml4e.u & PGM_PLXFLAGS_PERMANENT ? 'p' : '-',
1529 Pml4e.u & RT_BIT(11) ? '1' : '0',
1530 Pml4e.u & X86_PML4E_PG_MASK);
1531 if (pState->fDumpPageInfo)
1532 pgmR3DumpHierarchyShwTablePageInfo(pState, Pml4e.u & X86_PML4E_PG_MASK);
1533 if ((Pml4e.u >> 52) & 0x7ff)
1534 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx!", (Pml4e.u >> 52) & 0x7ff);
1535 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1536
1537 if (cMaxDepth)
1538 {
1539 int rc2 = pgmR3DumpHierarchyShwPaePDPT(pState, Pml4e.u & X86_PML4E_PG_MASK, cMaxDepth);
1540 if (rc2 < rc && RT_SUCCESS(rc))
1541 rc = rc2;
1542 }
1543 else
1544 pState->cLeaves++;
1545 }
1546 }
1547 return rc;
1548}
1549
1550
1551/**
1552 * Dumps a 32-bit shadow page table.
1553 *
1554 * @returns VBox status code (VINF_SUCCESS).
1555 * @param pVM Pointer to the VM.
1556 * @param pPT Pointer to the page table.
1557 * @param fMapping Set if it's a guest mapping.
1558 */
1559static int pgmR3DumpHierarchyShw32BitPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, bool fMapping)
1560{
1561 PCX86PT pPT;
1562 int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page table", fMapping, (void const **)&pPT);
1563 if (RT_FAILURE(rc))
1564 return rc;
1565
1566 uint32_t iFirst, iLast;
1567 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
1568 for (uint32_t i = iFirst; i <= iLast; i++)
1569 {
1570 X86PTE Pte = pPT->a[i];
1571 if (Pte.n.u1Present)
1572 {
1573 pState->u64Address = u64BaseAddress + (i << X86_PT_SHIFT);
1574 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d */
1575 "%08llx 1 | P %c %c %c %c %c %s %s %s .. 4K %c%c%c %08x",
1576 pState->u64Address,
1577 Pte.n.u1Write ? 'W' : 'R',
1578 Pte.n.u1User ? 'U' : 'S',
1579 Pte.n.u1Accessed ? 'A' : '-',
1580 Pte.n.u1Dirty ? 'D' : '-',
1581 Pte.n.u1Global ? 'G' : '-',
1582 Pte.n.u1WriteThru ? "WT" : "--",
1583 Pte.n.u1CacheDisable? "CD" : "--",
1584 Pte.n.u1PAT ? "AT" : "--",
1585 Pte.u & PGM_PTFLAGS_TRACK_DIRTY ? 'd' : '-',
1586 Pte.u & RT_BIT(10) ? '1' : '0',
1587 Pte.u & PGM_PTFLAGS_CSAM_VALIDATED ? 'v' : '-',
1588 Pte.u & X86_PDE_PG_MASK);
1589 if (pState->fDumpPageInfo)
1590 pgmR3DumpHierarchyShwGuestPageInfo(pState, Pte.u & X86_PDE_PG_MASK, _4K);
1591 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1592 }
1593 }
1594 return VINF_SUCCESS;
1595}
1596
1597
1598/**
1599 * Dumps a 32-bit shadow page directory and page tables.
1600 *
1601 * @returns VBox status code (VINF_SUCCESS).
1602 * @param pState The dumper state.
1603 * @param HCPhys The physical address of the table.
1604 * @param cMaxDepth The maximum depth.
1605 */
1606static int pgmR3DumpHierarchyShw32BitPD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1607{
1608 if (pState->u64Address >= _4G)
1609 return VINF_SUCCESS;
1610
1611 PCX86PD pPD;
1612 int rc = pgmR3DumpHierarchyShwMapPage(pState, HCPhys, "Page directory", false, (void const **)&pPD);
1613 if (RT_FAILURE(rc))
1614 return rc;
1615
1616 Assert(cMaxDepth > 0);
1617 cMaxDepth--;
1618
1619 uint32_t iFirst, iLast;
1620 pgmR3DumpHierarchyCalcRange(pState, X86_PD_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
1621 for (uint32_t i = iFirst; i <= iLast; i++)
1622 {
1623 X86PDE Pde = pPD->a[i];
1624 if (Pde.n.u1Present)
1625 {
1626 pState->u64Address = (uint32_t)i << X86_PD_SHIFT;
1627 if (Pde.b.u1Size && pState->fPse)
1628 {
1629 uint64_t u64Phys = ((uint64_t)(Pde.u & X86_PDE4M_PG_HIGH_MASK) << X86_PDE4M_PG_HIGH_SHIFT)
1630 | (Pde.u & X86_PDE4M_PG_MASK);
1631 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d phys */
1632 "%08llx 0 | P %c %c %c %c %c %s %s %s .. 4M %c%c%c %08llx",
1633 pState->u64Address,
1634 Pde.b.u1Write ? 'W' : 'R',
1635 Pde.b.u1User ? 'U' : 'S',
1636 Pde.b.u1Accessed ? 'A' : '-',
1637 Pde.b.u1Dirty ? 'D' : '-',
1638 Pde.b.u1Global ? 'G' : '-',
1639 Pde.b.u1WriteThru ? "WT" : "--",
1640 Pde.b.u1CacheDisable? "CD" : "--",
1641 Pde.b.u1PAT ? "AT" : "--",
1642 Pde.u & RT_BIT_32(9) ? '1' : '0',
1643 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1644 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1645 u64Phys);
1646 if (pState->fDumpPageInfo)
1647 pgmR3DumpHierarchyShwGuestPageInfo(pState, u64Phys, _4M);
1648 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1649 pState->cLeaves++;
1650 }
1651 else
1652 {
1653 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d phys */
1654 "%08llx 0 | P %c %c %c %c %c %s %s .. .. 4K %c%c%c %08x",
1655 pState->u64Address,
1656 Pde.n.u1Write ? 'W' : 'R',
1657 Pde.n.u1User ? 'U' : 'S',
1658 Pde.n.u1Accessed ? 'A' : '-',
1659 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
1660 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
1661 Pde.n.u1WriteThru ? "WT" : "--",
1662 Pde.n.u1CacheDisable? "CD" : "--",
1663 Pde.u & RT_BIT_32(9) ? '1' : '0',
1664 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1665 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1666 Pde.u & X86_PDE_PG_MASK);
1667 if (pState->fDumpPageInfo)
1668 pgmR3DumpHierarchyShwTablePageInfo(pState, Pde.u & X86_PDE_PG_MASK);
1669 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1670
1671 if (cMaxDepth)
1672 {
1673 int rc2 = pgmR3DumpHierarchyShw32BitPT(pState, Pde.u & X86_PDE_PG_MASK, !!(Pde.u & PGM_PDFLAGS_MAPPING));
1674 if (rc2 < rc && RT_SUCCESS(rc))
1675 rc = rc2;
1676 }
1677 else
1678 pState->cLeaves++;
1679 }
1680 }
1681 }
1682
1683 return rc;
1684}
1685
1686
1687/**
1688 * Internal worker that initiates the actual dump.
1689 *
1690 * @returns VBox status code.
1691 * @param pState The dumper state.
1692 * @param cr3 The CR3 value.
1693 * @param cMaxDepth The max depth.
1694 */
1695static int pgmR3DumpHierarchyShwDoIt(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t cr3, unsigned cMaxDepth)
1696{
1697 int rc;
1698 unsigned const cch = pState->cchAddress;
1699 uint64_t const cr3Mask = pState->fEpt ? X86_CR3_AMD64_PAGE_MASK
1700 : pState->fLme ? X86_CR3_AMD64_PAGE_MASK
1701 : pState->fPae ? X86_CR3_PAE_PAGE_MASK
1702 : X86_CR3_PAGE_MASK;
1703 if (pState->fPrintCr3)
1704 {
1705 const char * const pszMode = pState->fEpt ? "Extended Page Tables"
1706 : pState->fLme ? "Long Mode"
1707 : pState->fPae ? "PAE Mode"
1708 : pState->fPse ? "32-bit w/ PSE"
1709 : "32-bit";
1710 pState->pHlp->pfnPrintf(pState->pHlp, "cr3=%0*llx", cch, cr3);
1711 if (pState->fDumpPageInfo)
1712 pgmR3DumpHierarchyShwTablePageInfo(pState, cr3 & X86_CR3_AMD64_PAGE_MASK);
1713 pState->pHlp->pfnPrintf(pState->pHlp, " %s%s%s\n",
1714 pszMode,
1715 pState->fNp ? " + Nested Paging" : "",
1716 pState->fNxe ? " + NX" : "");
1717 }
1718
1719
1720 if (pState->fEpt)
1721 {
1722 if (pState->fPrintHeader)
1723 pState->pHlp->pfnPrintf(pState->pHlp,
1724 "%-*s R - Readable\n"
1725 "%-*s | W - Writeable\n"
1726 "%-*s | | X - Executable\n"
1727 "%-*s | | | EMT - EPT memory type\n"
1728 "%-*s | | | | PAT - Ignored PAT?\n"
1729 "%-*s | | | | | AVL1 - 4 available bits\n"
1730 "%-*s | | | | | | AVL2 - 12 available bits\n"
1731 "%-*s Level | | | | | | | page \n"
1732 /* xxxx n **** R W X EMT PAT AVL1 AVL2 xxxxxxxxxxxxx
1733 R W X 7 0 f fff 0123456701234567 */
1734 ,
1735 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address");
1736
1737 pState->pHlp->pfnPrintf(pState->pHlp, "EPT dumping is not yet implemented, sorry.\n");
1738 /** @todo implemented EPT dumping. */
1739 rc = VERR_NOT_IMPLEMENTED;
1740 }
1741 else
1742 {
1743 if (pState->fPrintHeader)
1744 pState->pHlp->pfnPrintf(pState->pHlp,
1745 "%-*s P - Present\n"
1746 "%-*s | R/W - Read (0) / Write (1)\n"
1747 "%-*s | | U/S - User (1) / Supervisor (0)\n"
1748 "%-*s | | | A - Accessed\n"
1749 "%-*s | | | | D - Dirty\n"
1750 "%-*s | | | | | G - Global\n"
1751 "%-*s | | | | | | WT - Write thru\n"
1752 "%-*s | | | | | | | CD - Cache disable\n"
1753 "%-*s | | | | | | | | AT - Attribute table (PAT)\n"
1754 "%-*s | | | | | | | | | NX - No execute (K8)\n"
1755 "%-*s | | | | | | | | | | 4K/4M/2M - Page size.\n"
1756 "%-*s | | | | | | | | | | | AVL - a=allocated; m=mapping; d=track dirty;\n"
1757 "%-*s | | | | | | | | | | | | p=permanent; v=validated;\n"
1758 "%-*s Level | | | | | | | | | | | | Page\n"
1759 /* xxxx n **** P R S A D G WT CD AT NX 4M AVL xxxxxxxxxxxxx
1760 - W U - - - -- -- -- -- -- 010 */
1761 ,
1762 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "",
1763 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address");
1764 if (pState->fLme)
1765 rc = pgmR3DumpHierarchyShwPaePML4(pState, cr3 & cr3Mask, cMaxDepth);
1766 else if (pState->fPae)
1767 rc = pgmR3DumpHierarchyShwPaePDPT(pState, cr3 & cr3Mask, cMaxDepth);
1768 else
1769 rc = pgmR3DumpHierarchyShw32BitPD(pState, cr3 & cr3Mask, cMaxDepth);
1770 }
1771
1772 if (!pState->cLeaves)
1773 pState->pHlp->pfnPrintf(pState->pHlp, "not present\n");
1774 return rc;
1775}
1776
1777
1778/**
1779 * dbgfR3PagingDumpEx worker.
1780 *
1781 * @returns VBox status code.
1782 * @param pVM Pointer to the VM.
1783 * @param cr3 The CR3 register value.
1784 * @param fFlags The flags, DBGFPGDMP_FLAGS_XXX.
1785 * @param u64FirstAddr The start address.
1786 * @param u64LastAddr The address to stop after.
1787 * @param cMaxDepth The max depth.
1788 * @param pHlp The output callbacks. Defaults to log if NULL.
1789 *
1790 * @internal
1791 */
1792VMMR3_INT_DECL(int) PGMR3DumpHierarchyShw(PVM pVM, uint64_t cr3, uint32_t fFlags, uint64_t u64FirstAddr, uint64_t u64LastAddr,
1793 uint32_t cMaxDepth, PCDBGFINFOHLP pHlp)
1794{
1795 /* Minimal validation as we're only supposed to service DBGF. */
1796 AssertReturn(~(fFlags & ~DBGFPGDMP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
1797 AssertReturn(!(fFlags & (DBGFPGDMP_FLAGS_CURRENT_MODE | DBGFPGDMP_FLAGS_CURRENT_CR3)), VERR_INVALID_PARAMETER);
1798 AssertReturn(fFlags & DBGFPGDMP_FLAGS_SHADOW, VERR_INVALID_PARAMETER);
1799
1800 PGMR3DUMPHIERARCHYSTATE State;
1801 pgmR3DumpHierarchyInitState(&State, pVM, fFlags, u64FirstAddr, u64LastAddr, pHlp);
1802 return pgmR3DumpHierarchyShwDoIt(&State, cr3, cMaxDepth);
1803}
1804
1805
1806/**
1807 * Dumps a page table hierarchy use only physical addresses and cr4/lm flags.
1808 *
1809 * @returns VBox status code (VINF_SUCCESS).
1810 * @param pVM Pointer to the VM.
1811 * @param cr3 The root of the hierarchy.
1812 * @param cr4 The cr4, only PAE and PSE is currently used.
1813 * @param fLongMode Set if long mode, false if not long mode.
1814 * @param cMaxDepth Number of levels to dump.
1815 * @param pHlp Pointer to the output functions.
1816 *
1817 * @deprecated Use DBGFR3PagingDumpEx.
1818 */
1819VMMR3DECL(int) PGMR3DumpHierarchyHC(PVM pVM, uint64_t cr3, uint64_t cr4, bool fLongMode, unsigned cMaxDepth, PCDBGFINFOHLP pHlp)
1820{
1821 if (!cMaxDepth)
1822 return VINF_SUCCESS;
1823
1824 PVMCPU pVCpu = VMMGetCpu(pVM);
1825 if (!pVCpu)
1826 pVCpu = &pVM->aCpus[0];
1827
1828 uint32_t fFlags = DBGFPGDMP_FLAGS_HEADER | DBGFPGDMP_FLAGS_PRINT_CR3 | DBGFPGDMP_FLAGS_PAGE_INFO | DBGFPGDMP_FLAGS_SHADOW;
1829 fFlags |= cr4 & (X86_CR4_PAE | X86_CR4_PSE);
1830 if (fLongMode)
1831 fFlags |= DBGFPGDMP_FLAGS_LME;
1832
1833 return DBGFR3PagingDumpEx(pVM->pUVM, pVCpu->idCpu, fFlags, cr3, 0, fLongMode ? UINT64_MAX : UINT32_MAX, cMaxDepth, pHlp);
1834}
1835
1836
1837/**
1838 * Maps the guest page.
1839 *
1840 * @returns VBox status code.
1841 * @param pState The dumper state.
1842 * @param GCPhys The physical address of the guest page.
1843 * @param pszDesc The description.
1844 * @param ppv Where to return the pointer.
1845 * @param pLock Where to return the mapping lock. Hand this to
1846 * PGMPhysReleasePageMappingLock when done.
1847 */
1848static int pgmR3DumpHierarchyGstMapPage(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, const char *pszDesc,
1849 void const **ppv, PPGMPAGEMAPLOCK pLock)
1850{
1851 int rc = PGMPhysGCPhys2CCPtrReadOnly(pState->pVM, GCPhys, ppv, pLock);
1852 if (RT_FAILURE(rc))
1853 {
1854 pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! Failed to map %s at GCPhys=%RGp: %Rrc!\n",
1855 pState->cchAddress, pState->u64Address, pszDesc, GCPhys, rc);
1856 return rc;
1857 }
1858 return VINF_SUCCESS;
1859}
1860
1861
1862/**
1863 * Figures out which guest page this is and dumps a summary.
1864 *
1865 * @param pState The dumper state.
1866 * @param GCPhys The page address.
1867 * @param cbPage The page size.
1868 */
1869static void pgmR3DumpHierarchyGstPageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, uint32_t cbPage)
1870{
1871 char szPage[80];
1872 pgmLock(pState->pVM);
1873 PCPGMPAGE pPage = pgmPhysGetPage(pState->pVM, GCPhys);
1874 if (pPage)
1875 RTStrPrintf(szPage, sizeof(szPage), " %R[pgmpage]", pPage);
1876 else
1877 strcpy(szPage, " not found");
1878 pgmUnlock(pState->pVM);
1879 pState->pHlp->pfnPrintf(pState->pHlp, "%s", szPage);
1880 NOREF(cbPage);
1881}
1882
1883
1884/**
1885 * Checks the entry for reserved bits.
1886 *
1887 * @param pState The dumper state.
1888 * @param u64Entry The entry to check.
1889 */
1890static void pgmR3DumpHierarchyGstCheckReservedHighBits(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t u64Entry)
1891{
1892 uint32_t uRsvd = (u64Entry & pState->u64HighReservedBits) >> 52;
1893 if (uRsvd)
1894 pState->pHlp->pfnPrintf(pState->pHlp, " %u:52=%03x%s",
1895 pState->uLastRsvdBit, uRsvd, pState->fLme ? "" : "!");
1896 /** @todo check the valid physical bits as well. */
1897}
1898
1899
1900/**
1901 * Dumps a PAE shadow page table.
1902 *
1903 * @returns VBox status code (VINF_SUCCESS).
1904 * @param pState The dumper state.
1905 * @param GCPhys The page table address.
1906 */
1907static int pgmR3DumpHierarchyGstPaePT(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys)
1908{
1909 PCX86PTPAE pPT;
1910 PGMPAGEMAPLOCK Lock;
1911 int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page table", (void const **)&pPT, &Lock);
1912 if (RT_FAILURE(rc))
1913 return rc;
1914
1915 uint32_t iFirst, iLast;
1916 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
1917 for (uint32_t i = iFirst; i <= iLast; i++)
1918 {
1919 X86PTEPAE Pte = pPT->a[i];
1920 if (Pte.n.u1Present)
1921 {
1922 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PT_PAE_SHIFT);
1923 pState->pHlp->pfnPrintf(pState->pHlp,
1924 pState->fLme /*P R S A D G WT CD AT NX 4M a p ? */
1925 ? "%016llx 3 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx"
1926 : "%08llx 2 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx",
1927 pState->u64Address,
1928 Pte.n.u1Write ? 'W' : 'R',
1929 Pte.n.u1User ? 'U' : 'S',
1930 Pte.n.u1Accessed ? 'A' : '-',
1931 Pte.n.u1Dirty ? 'D' : '-',
1932 Pte.n.u1Global ? 'G' : '-',
1933 Pte.n.u1WriteThru ? "WT" : "--",
1934 Pte.n.u1CacheDisable? "CD" : "--",
1935 Pte.n.u1PAT ? "AT" : "--",
1936 Pte.n.u1NoExecute ? "NX" : "--",
1937 Pte.u & RT_BIT(9) ? '1' : '0',
1938 Pte.u & RT_BIT(10) ? '1' : '0',
1939 Pte.u & RT_BIT(11) ? '1' : '0',
1940 Pte.u & X86_PTE_PAE_PG_MASK);
1941 if (pState->fDumpPageInfo)
1942 pgmR3DumpHierarchyGstPageInfo(pState, Pte.u & X86_PTE_PAE_PG_MASK, _4K);
1943 pgmR3DumpHierarchyGstCheckReservedHighBits(pState, Pte.u);
1944 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1945 pState->cLeaves++;
1946 }
1947 }
1948
1949 PGMPhysReleasePageMappingLock(pState->pVM, &Lock);
1950 return VINF_SUCCESS;
1951}
1952
1953
1954/**
1955 * Dumps a PAE shadow page directory table.
1956 *
1957 * @returns VBox status code (VINF_SUCCESS).
1958 * @param pState The dumper state.
1959 * @param GCPhys The physical address of the table.
1960 * @param cMaxDepth The maximum depth.
1961 */
1962static int pgmR3DumpHierarchyGstPaePD(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, unsigned cMaxDepth)
1963{
1964 PCX86PDPAE pPD;
1965 PGMPAGEMAPLOCK Lock;
1966 int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page directory", (void const **)&pPD, &Lock);
1967 if (RT_FAILURE(rc))
1968 return rc;
1969
1970 Assert(cMaxDepth > 0);
1971 cMaxDepth--;
1972
1973 uint32_t iFirst, iLast;
1974 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
1975 for (uint32_t i = iFirst; i <= iLast; i++)
1976 {
1977 X86PDEPAE Pde = pPD->a[i];
1978 if (Pde.n.u1Present)
1979 {
1980 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PD_PAE_SHIFT);
1981 if (Pde.b.u1Size)
1982 {
1983 pState->pHlp->pfnPrintf(pState->pHlp,
1984 pState->fLme /*P R S A D G WT CD AT NX 2M a p ? phys*/
1985 ? "%016llx 2 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx"
1986 : "%08llx 1 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx",
1987 pState->u64Address,
1988 Pde.b.u1Write ? 'W' : 'R',
1989 Pde.b.u1User ? 'U' : 'S',
1990 Pde.b.u1Accessed ? 'A' : '-',
1991 Pde.b.u1Dirty ? 'D' : '-',
1992 Pde.b.u1Global ? 'G' : '-',
1993 Pde.b.u1WriteThru ? "WT" : "--",
1994 Pde.b.u1CacheDisable ? "CD" : "--",
1995 Pde.b.u1PAT ? "AT" : "--",
1996 Pde.b.u1NoExecute ? "NX" : "--",
1997 Pde.u & RT_BIT_64(9) ? '1' : '0',
1998 Pde.u & RT_BIT_64(10) ? '1' : '0',
1999 Pde.u & RT_BIT_64(11) ? '1' : '0',
2000 Pde.u & X86_PDE2M_PAE_PG_MASK);
2001 if (pState->fDumpPageInfo)
2002 pgmR3DumpHierarchyGstPageInfo(pState, Pde.u & X86_PDE2M_PAE_PG_MASK, _2M);
2003 pgmR3DumpHierarchyGstCheckReservedHighBits(pState, Pde.u);
2004 if ((Pde.u >> 13) & 0xff)
2005 pState->pHlp->pfnPrintf(pState->pHlp, " 20:13=%02llx%s", (Pde.u >> 13) & 0x0ff, pState->fLme ? "" : "!");
2006 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2007
2008 pState->cLeaves++;
2009 }
2010 else
2011 {
2012 pState->pHlp->pfnPrintf(pState->pHlp,
2013 pState->fLme /*P R S A D G WT CD AT NX 4M a p ? phys */
2014 ? "%016llx 2 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx"
2015 : "%08llx 1 | P %c %c %c %c %c %s %s .. %s .. %c%c%c %016llx",
2016 pState->u64Address,
2017 Pde.n.u1Write ? 'W' : 'R',
2018 Pde.n.u1User ? 'U' : 'S',
2019 Pde.n.u1Accessed ? 'A' : '-',
2020 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
2021 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
2022 Pde.n.u1WriteThru ? "WT" : "--",
2023 Pde.n.u1CacheDisable ? "CD" : "--",
2024 Pde.n.u1NoExecute ? "NX" : "--",
2025 Pde.u & RT_BIT_64(9) ? '1' : '0',
2026 Pde.u & RT_BIT_64(10) ? '1' : '0',
2027 Pde.u & RT_BIT_64(11) ? '1' : '0',
2028 Pde.u & X86_PDE_PAE_PG_MASK);
2029 if (pState->fDumpPageInfo)
2030 pgmR3DumpHierarchyGstPageInfo(pState, Pde.u & X86_PDE_PAE_PG_MASK, _4K);
2031 pgmR3DumpHierarchyGstCheckReservedHighBits(pState, Pde.u);
2032 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2033
2034 if (cMaxDepth)
2035 {
2036 int rc2 = pgmR3DumpHierarchyGstPaePT(pState, Pde.u & X86_PDE_PAE_PG_MASK);
2037 if (rc2 < rc && RT_SUCCESS(rc))
2038 rc = rc2;
2039 }
2040 else
2041 pState->cLeaves++;
2042 }
2043 }
2044 }
2045
2046 PGMPhysReleasePageMappingLock(pState->pVM, &Lock);
2047 return rc;
2048}
2049
2050
2051/**
2052 * Dumps a PAE shadow page directory pointer table.
2053 *
2054 * @returns VBox status code (VINF_SUCCESS).
2055 * @param pState The dumper state.
2056 * @param GCPhys The physical address of the table.
2057 * @param cMaxDepth The maximum depth.
2058 */
2059static int pgmR3DumpHierarchyGstPaePDPT(PPGMR3DUMPHIERARCHYSTATE pState, RTGCPHYS GCPhys, unsigned cMaxDepth)
2060{
2061 /* Fend of addresses that are out of range in PAE mode - simplifies the code below. */
2062 if (!pState->fLme && pState->u64Address >= _4G)
2063 return VINF_SUCCESS;
2064
2065 PCX86PDPT pPDPT;
2066 PGMPAGEMAPLOCK Lock;
2067 int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page directory pointer table", (void const **)&pPDPT, &Lock);
2068 if (RT_FAILURE(rc))
2069 return rc;
2070
2071 Assert(cMaxDepth > 0);
2072 cMaxDepth--;
2073
2074 uint32_t iFirst, iLast;
2075 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PDPT_SHIFT,
2076 pState->fLme ? X86_PG_AMD64_PDPE_ENTRIES : X86_PG_PAE_PDPE_ENTRIES,
2077 &iFirst, &iLast);
2078 for (uint32_t i = iFirst; i <= iLast; i++)
2079 {
2080 X86PDPE Pdpe = pPDPT->a[i];
2081 if (Pdpe.n.u1Present)
2082 {
2083 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PDPT_SHIFT);
2084 if (pState->fLme)
2085 {
2086 /** @todo Do 1G pages. */
2087 pState->pHlp->pfnPrintf(pState->pHlp, /*P R S A D G WT CD AT NX .. a p ? */
2088 "%016llx 1 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
2089 pState->u64Address,
2090 Pdpe.lm.u1Write ? 'W' : 'R',
2091 Pdpe.lm.u1User ? 'U' : 'S',
2092 Pdpe.lm.u1Accessed ? 'A' : '-',
2093 Pdpe.lm.u3Reserved & 1 ? '?' : '.', /* ignored */
2094 Pdpe.lm.u3Reserved & 4 ? '!' : '.', /* mbz */
2095 Pdpe.lm.u1WriteThru ? "WT" : "--",
2096 Pdpe.lm.u1CacheDisable ? "CD" : "--",
2097 Pdpe.lm.u3Reserved & 2 ? "!" : "..",/* mbz */
2098 Pdpe.lm.u1NoExecute ? "NX" : "--",
2099 Pdpe.u & RT_BIT_64(9) ? '1' : '0',
2100 Pdpe.u & RT_BIT_64(10) ? '1' : '0',
2101 Pdpe.u & RT_BIT_64(11) ? '1' : '0',
2102 Pdpe.u & X86_PDPE_PG_MASK);
2103 if (pState->fDumpPageInfo)
2104 pgmR3DumpHierarchyGstPageInfo(pState, Pdpe.u & X86_PDPE_PG_MASK, _4K);
2105 pgmR3DumpHierarchyGstCheckReservedHighBits(pState, Pdpe.u);
2106 }
2107 else
2108 {
2109 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX .. a p ? */
2110 "%08llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
2111 pState->u64Address,
2112 Pdpe.n.u2Reserved & 1 ? '!' : '.', /* mbz */
2113 Pdpe.n.u2Reserved & 2 ? '!' : '.', /* mbz */
2114 Pdpe.n.u4Reserved & 1 ? '!' : '.', /* mbz */
2115 Pdpe.n.u4Reserved & 2 ? '!' : '.', /* mbz */
2116 Pdpe.n.u4Reserved & 8 ? '!' : '.', /* mbz */
2117 Pdpe.n.u1WriteThru ? "WT" : "--",
2118 Pdpe.n.u1CacheDisable ? "CD" : "--",
2119 Pdpe.n.u4Reserved & 2 ? "!" : "..", /* mbz */
2120 Pdpe.lm.u1NoExecute ? "!!" : "..",/* mbz */
2121 Pdpe.u & RT_BIT_64(9) ? '1' : '0',
2122 Pdpe.u & RT_BIT_64(10) ? '1' : '0',
2123 Pdpe.u & RT_BIT_64(11) ? '1' : '0',
2124 Pdpe.u & X86_PDPE_PG_MASK);
2125 if (pState->fDumpPageInfo)
2126 pgmR3DumpHierarchyGstPageInfo(pState, Pdpe.u & X86_PDPE_PG_MASK, _4K);
2127 pgmR3DumpHierarchyGstCheckReservedHighBits(pState, Pdpe.u);
2128 }
2129 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2130
2131 if (cMaxDepth)
2132 {
2133 int rc2 = pgmR3DumpHierarchyGstPaePD(pState, Pdpe.u & X86_PDPE_PG_MASK, cMaxDepth);
2134 if (rc2 < rc && RT_SUCCESS(rc))
2135 rc = rc2;
2136 }
2137 else
2138 pState->cLeaves++;
2139 }
2140 }
2141
2142 PGMPhysReleasePageMappingLock(pState->pVM, &Lock);
2143 return rc;
2144}
2145
2146
2147/**
2148 * Dumps a 32-bit shadow page table.
2149 *
2150 * @returns VBox status code (VINF_SUCCESS).
2151 * @param pVM Pointer to the VM.
2152 * @param GCPhys The physical address of the table.
2153 * @param cMaxDepth The maximum depth.
2154 */
2155static int pgmR3DumpHierarchyGstPaePML4(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS GCPhys, unsigned cMaxDepth)
2156{
2157 PCX86PML4 pPML4;
2158 PGMPAGEMAPLOCK Lock;
2159 int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page map level 4", (void const **)&pPML4, &Lock);
2160 if (RT_FAILURE(rc))
2161 return rc;
2162
2163 Assert(cMaxDepth);
2164 cMaxDepth--;
2165
2166 /*
2167 * This is a bit tricky as we're working on unsigned addresses while the
2168 * AMD64 spec uses signed tricks.
2169 */
2170 uint32_t iFirst = (pState->u64FirstAddress >> X86_PML4_SHIFT) & X86_PML4_MASK;
2171 uint32_t iLast = (pState->u64LastAddress >> X86_PML4_SHIFT) & X86_PML4_MASK;
2172 if ( pState->u64LastAddress <= UINT64_C(0x00007fffffffffff)
2173 || pState->u64FirstAddress >= UINT64_C(0xffff800000000000))
2174 { /* Simple, nothing to adjust */ }
2175 else if (pState->u64FirstAddress <= UINT64_C(0x00007fffffffffff))
2176 iLast = X86_PG_AMD64_ENTRIES / 2 - 1;
2177 else if (pState->u64LastAddress >= UINT64_C(0xffff800000000000))
2178 iFirst = X86_PG_AMD64_ENTRIES / 2;
2179 else
2180 iFirst = X86_PG_AMD64_ENTRIES; /* neither address is canonical */
2181
2182 for (uint32_t i = iFirst; i <= iLast; i++)
2183 {
2184 X86PML4E Pml4e = pPML4->a[i];
2185 if (Pml4e.n.u1Present)
2186 {
2187 pState->u64Address = ((uint64_t)i << X86_PML4_SHIFT)
2188 | (i >= RT_ELEMENTS(pPML4->a) / 2 ? UINT64_C(0xffff000000000000) : 0);
2189 pState->pHlp->pfnPrintf(pState->pHlp, /*P R S A D G WT CD AT NX 4M a p ? */
2190 "%016llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
2191 pState->u64Address,
2192 Pml4e.n.u1Write ? 'W' : 'R',
2193 Pml4e.n.u1User ? 'U' : 'S',
2194 Pml4e.n.u1Accessed ? 'A' : '-',
2195 Pml4e.n.u3Reserved & 1 ? '?' : '.', /* ignored */
2196 Pml4e.n.u3Reserved & 4 ? '!' : '.', /* mbz */
2197 Pml4e.n.u1WriteThru ? "WT" : "--",
2198 Pml4e.n.u1CacheDisable ? "CD" : "--",
2199 Pml4e.n.u3Reserved & 2 ? "!" : "..",/* mbz */
2200 Pml4e.n.u1NoExecute ? "NX" : "--",
2201 Pml4e.u & RT_BIT_64(9) ? '1' : '0',
2202 Pml4e.u & RT_BIT_64(10) ? '1' : '0',
2203 Pml4e.u & RT_BIT_64(11) ? '1' : '0',
2204 Pml4e.u & X86_PML4E_PG_MASK);
2205 if (pState->fDumpPageInfo)
2206 pgmR3DumpHierarchyGstPageInfo(pState, Pml4e.u & X86_PML4E_PG_MASK, _4K);
2207 pgmR3DumpHierarchyGstCheckReservedHighBits(pState, Pml4e.u);
2208 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2209
2210 if (cMaxDepth)
2211 {
2212 int rc2 = pgmR3DumpHierarchyGstPaePDPT(pState, Pml4e.u & X86_PML4E_PG_MASK, cMaxDepth);
2213 if (rc2 < rc && RT_SUCCESS(rc))
2214 rc = rc2;
2215 }
2216 else
2217 pState->cLeaves++;
2218 }
2219 }
2220
2221 PGMPhysReleasePageMappingLock(pState->pVM, &Lock);
2222 return rc;
2223}
2224
2225
2226/**
2227 * Dumps a 32-bit shadow page table.
2228 *
2229 * @returns VBox status code (VINF_SUCCESS).
2230 * @param pState The dumper state.
2231 * @param GCPhys The physical address of the table.
2232 */
2233static int pgmR3DumpHierarchyGst32BitPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS GCPhys)
2234{
2235 PCX86PT pPT;
2236 PGMPAGEMAPLOCK Lock;
2237 int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page table", (void const **)&pPT, &Lock);
2238 if (RT_FAILURE(rc))
2239 return rc;
2240
2241 uint32_t iFirst, iLast;
2242 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
2243 for (uint32_t i = iFirst; i <= iLast; i++)
2244 {
2245 X86PTE Pte = pPT->a[i];
2246 if (Pte.n.u1Present)
2247 {
2248 pState->u64Address = u64BaseAddress + (i << X86_PT_SHIFT);
2249 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d */
2250 "%08llx 1 | P %c %c %c %c %c %s %s %s .. 4K %c%c%c %08x",
2251 pState->u64Address,
2252 Pte.n.u1Write ? 'W' : 'R',
2253 Pte.n.u1User ? 'U' : 'S',
2254 Pte.n.u1Accessed ? 'A' : '-',
2255 Pte.n.u1Dirty ? 'D' : '-',
2256 Pte.n.u1Global ? 'G' : '-',
2257 Pte.n.u1WriteThru ? "WT" : "--",
2258 Pte.n.u1CacheDisable ? "CD" : "--",
2259 Pte.n.u1PAT ? "AT" : "--",
2260 Pte.u & RT_BIT_32(9) ? '1' : '0',
2261 Pte.u & RT_BIT_32(10) ? '1' : '0',
2262 Pte.u & RT_BIT_32(11) ? '1' : '0',
2263 Pte.u & X86_PDE_PG_MASK);
2264 if (pState->fDumpPageInfo)
2265 pgmR3DumpHierarchyGstPageInfo(pState, Pte.u & X86_PDE_PG_MASK, _4K);
2266 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2267 }
2268 }
2269
2270 PGMPhysReleasePageMappingLock(pState->pVM, &Lock);
2271 return VINF_SUCCESS;
2272}
2273
2274
2275/**
2276 * Dumps a 32-bit shadow page directory and page tables.
2277 *
2278 * @returns VBox status code (VINF_SUCCESS).
2279 * @param pState The dumper state.
2280 * @param GCPhys The physical address of the table.
2281 * @param cMaxDepth The maximum depth.
2282 */
2283static int pgmR3DumpHierarchyGst32BitPD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS GCPhys, unsigned cMaxDepth)
2284{
2285 if (pState->u64Address >= _4G)
2286 return VINF_SUCCESS;
2287
2288 PCX86PD pPD;
2289 PGMPAGEMAPLOCK Lock;
2290 int rc = pgmR3DumpHierarchyGstMapPage(pState, GCPhys, "Page directory", (void const **)&pPD, &Lock);
2291 if (RT_FAILURE(rc))
2292 return rc;
2293
2294 Assert(cMaxDepth > 0);
2295 cMaxDepth--;
2296
2297 uint32_t iFirst, iLast;
2298 pgmR3DumpHierarchyCalcRange(pState, X86_PD_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
2299 for (uint32_t i = iFirst; i <= iLast; i++)
2300 {
2301 X86PDE Pde = pPD->a[i];
2302 if (Pde.n.u1Present)
2303 {
2304 pState->u64Address = (uint32_t)i << X86_PD_SHIFT;
2305 if (Pde.b.u1Size && pState->fPse)
2306 {
2307 uint64_t u64Phys = ((uint64_t)(Pde.u & X86_PDE4M_PG_HIGH_MASK) << X86_PDE4M_PG_HIGH_SHIFT)
2308 | (Pde.u & X86_PDE4M_PG_MASK);
2309 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d phys */
2310 "%08llx 0 | P %c %c %c %c %c %s %s %s .. 4M %c%c%c %08llx",
2311 pState->u64Address,
2312 Pde.b.u1Write ? 'W' : 'R',
2313 Pde.b.u1User ? 'U' : 'S',
2314 Pde.b.u1Accessed ? 'A' : '-',
2315 Pde.b.u1Dirty ? 'D' : '-',
2316 Pde.b.u1Global ? 'G' : '-',
2317 Pde.b.u1WriteThru ? "WT" : "--",
2318 Pde.b.u1CacheDisable ? "CD" : "--",
2319 Pde.b.u1PAT ? "AT" : "--",
2320 Pde.u & RT_BIT_32(9) ? '1' : '0',
2321 Pde.u & RT_BIT_32(10) ? '1' : '0',
2322 Pde.u & RT_BIT_32(11) ? '1' : '0',
2323 u64Phys);
2324 if (pState->fDumpPageInfo)
2325 pgmR3DumpHierarchyGstPageInfo(pState, u64Phys, _4M);
2326 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2327 pState->cLeaves++;
2328 }
2329 else
2330 {
2331 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d phys */
2332 "%08llx 0 | P %c %c %c %c %c %s %s .. .. .. %c%c%c %08x",
2333 pState->u64Address,
2334 Pde.n.u1Write ? 'W' : 'R',
2335 Pde.n.u1User ? 'U' : 'S',
2336 Pde.n.u1Accessed ? 'A' : '-',
2337 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
2338 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
2339 Pde.n.u1WriteThru ? "WT" : "--",
2340 Pde.n.u1CacheDisable ? "CD" : "--",
2341 Pde.u & RT_BIT_32(9) ? '1' : '0',
2342 Pde.u & RT_BIT_32(10) ? '1' : '0',
2343 Pde.u & RT_BIT_32(11) ? '1' : '0',
2344 Pde.u & X86_PDE_PG_MASK);
2345 if (pState->fDumpPageInfo)
2346 pgmR3DumpHierarchyGstPageInfo(pState, Pde.u & X86_PDE_PG_MASK, _4K);
2347 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
2348
2349 if (cMaxDepth)
2350 {
2351 int rc2 = pgmR3DumpHierarchyGst32BitPT(pState, Pde.u & X86_PDE_PG_MASK);
2352 if (rc2 < rc && RT_SUCCESS(rc))
2353 rc = rc2;
2354 }
2355 else
2356 pState->cLeaves++;
2357 }
2358 }
2359 }
2360
2361 PGMPhysReleasePageMappingLock(pState->pVM, &Lock);
2362 return rc;
2363}
2364
2365
2366/**
2367 * Internal worker that initiates the actual dump.
2368 *
2369 * @returns VBox status code.
2370 * @param pState The dumper state.
2371 * @param cr3 The CR3 value.
2372 * @param cMaxDepth The max depth.
2373 */
2374static int pgmR3DumpHierarchyGstDoIt(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t cr3, unsigned cMaxDepth)
2375{
2376 int rc;
2377 unsigned const cch = pState->cchAddress;
2378 uint64_t const cr3Mask = pState->fEpt ? X86_CR3_AMD64_PAGE_MASK
2379 : pState->fLme ? X86_CR3_AMD64_PAGE_MASK
2380 : pState->fPae ? X86_CR3_PAE_PAGE_MASK
2381 : X86_CR3_PAGE_MASK;
2382 if (pState->fPrintCr3)
2383 {
2384 const char * const pszMode = pState->fEpt ? "Extended Page Tables"
2385 : pState->fLme ? "Long Mode"
2386 : pState->fPae ? "PAE Mode"
2387 : pState->fPse ? "32-bit w/ PSE"
2388 : "32-bit";
2389 pState->pHlp->pfnPrintf(pState->pHlp, "cr3=%0*llx", cch, cr3);
2390 if (pState->fDumpPageInfo)
2391 pgmR3DumpHierarchyGstPageInfo(pState, cr3 & X86_CR3_AMD64_PAGE_MASK, _4K);
2392 pState->pHlp->pfnPrintf(pState->pHlp, " %s%s%s\n",
2393 pszMode,
2394 pState->fNp ? " + Nested Paging" : "",
2395 pState->fNxe ? " + NX" : "");
2396 }
2397
2398
2399 if (pState->fEpt)
2400 {
2401 if (pState->fPrintHeader)
2402 pState->pHlp->pfnPrintf(pState->pHlp,
2403 "%-*s R - Readable\n"
2404 "%-*s | W - Writeable\n"
2405 "%-*s | | X - Executable\n"
2406 "%-*s | | | EMT - EPT memory type\n"
2407 "%-*s | | | | PAT - Ignored PAT?\n"
2408 "%-*s | | | | | AVL1 - 4 available bits\n"
2409 "%-*s | | | | | | AVL2 - 12 available bits\n"
2410 "%-*s Level | | | | | | | page \n"
2411 /* xxxx n **** R W X EMT PAT AVL1 AVL2 xxxxxxxxxxxxx
2412 R W X 7 0 f fff 0123456701234567 */
2413 ,
2414 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address");
2415
2416 pState->pHlp->pfnPrintf(pState->pHlp, "EPT dumping is not yet implemented, sorry.\n");
2417 /** @todo implemented EPT dumping. */
2418 rc = VERR_NOT_IMPLEMENTED;
2419 }
2420 else
2421 {
2422 if (pState->fPrintHeader)
2423 pState->pHlp->pfnPrintf(pState->pHlp,
2424 "%-*s P - Present\n"
2425 "%-*s | R/W - Read (0) / Write (1)\n"
2426 "%-*s | | U/S - User (1) / Supervisor (0)\n"
2427 "%-*s | | | A - Accessed\n"
2428 "%-*s | | | | D - Dirty\n"
2429 "%-*s | | | | | G - Global\n"
2430 "%-*s | | | | | | WT - Write thru\n"
2431 "%-*s | | | | | | | CD - Cache disable\n"
2432 "%-*s | | | | | | | | AT - Attribute table (PAT)\n"
2433 "%-*s | | | | | | | | | NX - No execute (K8)\n"
2434 "%-*s | | | | | | | | | | 4K/4M/2M - Page size.\n"
2435 "%-*s | | | | | | | | | | | AVL - 3 available bits.\n"
2436 "%-*s Level | | | | | | | | | | | | Page\n"
2437 /* xxxx n **** P R S A D G WT CD AT NX 4M AVL xxxxxxxxxxxxx
2438 - W U - - - -- -- -- -- -- 010 */
2439 ,
2440 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "",
2441 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address");
2442 if (pState->fLme)
2443 rc = pgmR3DumpHierarchyGstPaePML4(pState, cr3 & cr3Mask, cMaxDepth);
2444 else if (pState->fPae)
2445 rc = pgmR3DumpHierarchyGstPaePDPT(pState, cr3 & cr3Mask, cMaxDepth);
2446 else
2447 rc = pgmR3DumpHierarchyGst32BitPD(pState, cr3 & cr3Mask, cMaxDepth);
2448 }
2449
2450 if (!pState->cLeaves)
2451 pState->pHlp->pfnPrintf(pState->pHlp, "not present\n");
2452 return rc;
2453}
2454
2455
2456/**
2457 * dbgfR3PagingDumpEx worker.
2458 *
2459 * @returns VBox status code.
2460 * @param pVM Pointer to the VM.
2461 * @param cr3 The CR3 register value.
2462 * @param fFlags The flags, DBGFPGDMP_FLAGS_XXX.
2463 * @param FirstAddr The start address.
2464 * @param LastAddr The address to stop after.
2465 * @param cMaxDepth The max depth.
2466 * @param pHlp The output callbacks. Defaults to log if NULL.
2467 *
2468 * @internal
2469 */
2470VMMR3_INT_DECL(int) PGMR3DumpHierarchyGst(PVM pVM, uint64_t cr3, uint32_t fFlags, RTGCPTR FirstAddr, RTGCPTR LastAddr,
2471 uint32_t cMaxDepth, PCDBGFINFOHLP pHlp)
2472{
2473 /* Minimal validation as we're only supposed to service DBGF. */
2474 AssertReturn(~(fFlags & ~DBGFPGDMP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
2475 AssertReturn(!(fFlags & (DBGFPGDMP_FLAGS_CURRENT_MODE | DBGFPGDMP_FLAGS_CURRENT_CR3)), VERR_INVALID_PARAMETER);
2476 AssertReturn(fFlags & DBGFPGDMP_FLAGS_GUEST, VERR_INVALID_PARAMETER);
2477
2478 PGMR3DUMPHIERARCHYSTATE State;
2479 pgmR3DumpHierarchyInitState(&State, pVM, fFlags, FirstAddr, LastAddr, pHlp);
2480 return pgmR3DumpHierarchyGstDoIt(&State, cr3, cMaxDepth);
2481}
2482
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette