VirtualBox

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

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

iprt/asm.h: Cleaned up the ASMMemIsAll8/U32 mess and implmeneted the former in assembly. (Found inverted usage due to bad naming in copyUtf8Block, but it is fortunately an unused method.) Replaces the complicated ASMBitFirstSet based scanning in RTSgBufIsZero with a simple call to the new ASMMemIsZero function.

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