VirtualBox

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

Last change on this file since 31973 was 31973, checked in by vboxsync, 15 years ago

PGM,DBGC: working shadow page table dumper (sans EPT).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 72.6 KB
Line 
1/* $Id: PGMDbg.cpp 31973 2010-08-26 08:14:34Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor - Debugger & Debugging APIs.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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/pgm.h>
23#include <VBox/stam.h>
24#include "PGMInternal.h"
25#include <VBox/vm.h>
26#include "PGMInline.h"
27#include <iprt/assert.h>
28#include <iprt/asm.h>
29#include <iprt/string.h>
30#include <VBox/log.h>
31#include <VBox/param.h>
32#include <VBox/err.h>
33
34
35/*******************************************************************************
36* Defined Constants And Macros *
37*******************************************************************************/
38/** The max needle size that we will bother searching for
39 * This must not be more than half a page! */
40#define MAX_NEEDLE_SIZE 256
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46/**
47 * State structure for the paging hierarchy dumpers.
48 */
49typedef struct PGMR3DUMPHIERARCHYSTATE
50{
51 /** The VM handle. */
52 PVM pVM;
53 /** Output helpers. */
54 PCDBGFINFOHLP pHlp;
55 /** Set if PSE, PAE or long mode is enabled. */
56 bool fPse;
57 /** Set if PAE or long mode is enabled. */
58 bool fPae;
59 /** Set if long mode is enabled. */
60 bool fLme;
61 /** Set if nested paging. */
62 bool fNp;
63 /** Set if EPT. */
64 bool fEpt;
65 /** The number or chars the address needs. */
66 uint8_t cchAddress;
67 /** Dump the page info as well (shadow page summary / guest physical
68 * page summary). */
69 bool fDumpPageInfo;
70 /** Whether or not to print the header. */
71 bool fPrintHeader;
72 /** The current address. */
73 uint64_t u64Address;
74 /** The last address to dump structures for. */
75 uint64_t u64FirstAddress;
76 /** The last address to dump structures for. */
77 uint64_t u64LastAddress;
78 /** The number of leaf entries that we've printed. */
79 uint64_t cLeaves;
80} PGMR3DUMPHIERARCHYSTATE;
81/** Pointer to the paging hierarchy dumper state. */
82typedef PGMR3DUMPHIERARCHYSTATE *PPGMR3DUMPHIERARCHYSTATE;
83
84
85
86/**
87 * Converts a R3 pointer to a GC physical address.
88 *
89 * Only for the debugger.
90 *
91 * @returns VBox status code.
92 * @retval VINF_SUCCESS on success, *pGCPhys is set.
93 * @retval VERR_INVALID_POINTER if the pointer is not within the GC physical memory.
94 *
95 * @param pVM The VM handle.
96 * @param R3Ptr The R3 pointer to convert.
97 * @param pGCPhys Where to store the GC physical address on success.
98 */
99VMMR3DECL(int) PGMR3DbgR3Ptr2GCPhys(PVM pVM, RTR3PTR R3Ptr, PRTGCPHYS pGCPhys)
100{
101 *pGCPhys = NIL_RTGCPHYS;
102 return VERR_NOT_IMPLEMENTED;
103}
104
105
106/**
107 * Converts a R3 pointer to a HC physical address.
108 *
109 * Only for the debugger.
110 *
111 * @returns VBox status code.
112 * @retval VINF_SUCCESS on success, *pHCPhys is set.
113 * @retval VERR_PGM_PHYS_PAGE_RESERVED it it's a valid GC physical page but has no physical backing.
114 * @retval VERR_INVALID_POINTER if the pointer is not within the GC physical memory.
115 *
116 * @param pVM The VM handle.
117 * @param R3Ptr The R3 pointer to convert.
118 * @param pHCPhys Where to store the HC physical address on success.
119 */
120VMMR3DECL(int) PGMR3DbgR3Ptr2HCPhys(PVM pVM, RTR3PTR R3Ptr, PRTHCPHYS pHCPhys)
121{
122 *pHCPhys = NIL_RTHCPHYS;
123 return VERR_NOT_IMPLEMENTED;
124}
125
126
127/**
128 * Converts a HC physical address to a GC physical address.
129 *
130 * Only for the debugger.
131 *
132 * @returns VBox status code
133 * @retval VINF_SUCCESS on success, *pGCPhys is set.
134 * @retval VERR_INVALID_POINTER if the HC physical address is not within the GC physical memory.
135 *
136 * @param pVM The VM handle.
137 * @param HCPhys The HC physical address to convert.
138 * @param pGCPhys Where to store the GC physical address on success.
139 */
140VMMR3DECL(int) PGMR3DbgHCPhys2GCPhys(PVM pVM, RTHCPHYS HCPhys, PRTGCPHYS pGCPhys)
141{
142 /*
143 * Validate and adjust the input a bit.
144 */
145 if (HCPhys == NIL_RTHCPHYS)
146 return VERR_INVALID_POINTER;
147 unsigned off = HCPhys & PAGE_OFFSET_MASK;
148 HCPhys &= X86_PTE_PAE_PG_MASK;
149 if (HCPhys == 0)
150 return VERR_INVALID_POINTER;
151
152 for (PPGMRAMRANGE pRam = pVM->pgm.s.CTX_SUFF(pRamRanges);
153 pRam;
154 pRam = pRam->CTX_SUFF(pNext))
155 {
156 uint32_t iPage = pRam->cb >> PAGE_SHIFT;
157 while (iPage-- > 0)
158 if (PGM_PAGE_GET_HCPHYS(&pRam->aPages[iPage]) == HCPhys)
159 {
160 *pGCPhys = pRam->GCPhys + (iPage << PAGE_SHIFT) + off;
161 return VINF_SUCCESS;
162 }
163 }
164 return VERR_INVALID_POINTER;
165}
166
167
168/**
169 * Read physical memory API for the debugger, similar to
170 * PGMPhysSimpleReadGCPhys.
171 *
172 * @returns VBox status code.
173 *
174 * @param pVM The VM handle.
175 * @param pvDst Where to store what's read.
176 * @param GCPhysDst Where to start reading from.
177 * @param cb The number of bytes to attempt reading.
178 * @param fFlags Flags, MBZ.
179 * @param pcbRead For store the actual number of bytes read, pass NULL if
180 * partial reads are unwanted.
181 * @todo Unused?
182 */
183VMMR3DECL(int) PGMR3DbgReadGCPhys(PVM pVM, void *pvDst, RTGCPHYS GCPhysSrc, size_t cb, uint32_t fFlags, size_t *pcbRead)
184{
185 /* validate */
186 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
187 AssertReturn(pVM, VERR_INVALID_PARAMETER);
188
189 /* try simple first. */
190 int rc = PGMPhysSimpleReadGCPhys(pVM, pvDst, GCPhysSrc, cb);
191 if (RT_SUCCESS(rc) || !pcbRead)
192 return rc;
193
194 /* partial read that failed, chop it up in pages. */
195 *pcbRead = 0;
196 size_t const cbReq = cb;
197 rc = VINF_SUCCESS;
198 while (cb > 0)
199 {
200 size_t cbChunk = PAGE_SIZE;
201 cbChunk -= GCPhysSrc & PAGE_OFFSET_MASK;
202 if (cbChunk > cb)
203 cbChunk = cb;
204
205 rc = PGMPhysSimpleReadGCPhys(pVM, pvDst, GCPhysSrc, cbChunk);
206
207 /* advance */
208 if (RT_FAILURE(rc))
209 break;
210 *pcbRead += cbChunk;
211 cb -= cbChunk;
212 GCPhysSrc += cbChunk;
213 pvDst = (uint8_t *)pvDst + cbChunk;
214 }
215
216 return *pcbRead && RT_FAILURE(rc) ? -rc : rc;
217}
218
219
220/**
221 * Write physical memory API for the debugger, similar to
222 * PGMPhysSimpleWriteGCPhys.
223 *
224 * @returns VBox status code.
225 *
226 * @param pVM The VM handle.
227 * @param GCPhysDst Where to start writing.
228 * @param pvSrc What to write.
229 * @param cb The number of bytes to attempt writing.
230 * @param fFlags Flags, MBZ.
231 * @param pcbWritten For store the actual number of bytes written, pass NULL
232 * if partial writes are unwanted.
233 * @todo Unused?
234 */
235VMMR3DECL(int) PGMR3DbgWriteGCPhys(PVM pVM, RTGCPHYS GCPhysDst, const void *pvSrc, size_t cb, uint32_t fFlags, size_t *pcbWritten)
236{
237 /* validate */
238 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
239 AssertReturn(pVM, VERR_INVALID_PARAMETER);
240
241 /* try simple first. */
242 int rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysDst, pvSrc, cb);
243 if (RT_SUCCESS(rc) || !pcbWritten)
244 return rc;
245
246 /* partial write that failed, chop it up in pages. */
247 *pcbWritten = 0;
248 rc = VINF_SUCCESS;
249 while (cb > 0)
250 {
251 size_t cbChunk = PAGE_SIZE;
252 cbChunk -= GCPhysDst & PAGE_OFFSET_MASK;
253 if (cbChunk > cb)
254 cbChunk = cb;
255
256 rc = PGMPhysSimpleWriteGCPhys(pVM, GCPhysDst, pvSrc, cbChunk);
257
258 /* advance */
259 if (RT_FAILURE(rc))
260 break;
261 *pcbWritten += cbChunk;
262 cb -= cbChunk;
263 GCPhysDst += cbChunk;
264 pvSrc = (uint8_t const *)pvSrc + cbChunk;
265 }
266
267 return *pcbWritten && RT_FAILURE(rc) ? -rc : rc;
268
269}
270
271
272/**
273 * Read virtual memory API for the debugger, similar to PGMPhysSimpleReadGCPtr.
274 *
275 * @returns VBox status code.
276 *
277 * @param pVM The VM handle.
278 * @param pvDst Where to store what's read.
279 * @param GCPtrDst Where to start reading from.
280 * @param cb The number of bytes to attempt reading.
281 * @param fFlags Flags, MBZ.
282 * @param pcbRead For store the actual number of bytes read, pass NULL if
283 * partial reads are unwanted.
284 * @todo Unused?
285 */
286VMMR3DECL(int) PGMR3DbgReadGCPtr(PVM pVM, void *pvDst, RTGCPTR GCPtrSrc, size_t cb, uint32_t fFlags, size_t *pcbRead)
287{
288 /* validate */
289 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
290 AssertReturn(pVM, VERR_INVALID_PARAMETER);
291
292 /* @todo SMP support! */
293 PVMCPU pVCpu = &pVM->aCpus[0];
294
295/** @todo deal with HMA */
296 /* try simple first. */
297 int rc = PGMPhysSimpleReadGCPtr(pVCpu, pvDst, GCPtrSrc, cb);
298 if (RT_SUCCESS(rc) || !pcbRead)
299 return rc;
300
301 /* partial read that failed, chop it up in pages. */
302 *pcbRead = 0;
303 rc = VINF_SUCCESS;
304 while (cb > 0)
305 {
306 size_t cbChunk = PAGE_SIZE;
307 cbChunk -= GCPtrSrc & PAGE_OFFSET_MASK;
308 if (cbChunk > cb)
309 cbChunk = cb;
310
311 rc = PGMPhysSimpleReadGCPtr(pVCpu, pvDst, GCPtrSrc, cbChunk);
312
313 /* advance */
314 if (RT_FAILURE(rc))
315 break;
316 *pcbRead += cbChunk;
317 cb -= cbChunk;
318 GCPtrSrc += cbChunk;
319 pvDst = (uint8_t *)pvDst + cbChunk;
320 }
321
322 return *pcbRead && RT_FAILURE(rc) ? -rc : rc;
323
324}
325
326
327/**
328 * Write virtual memory API for the debugger, similar to
329 * PGMPhysSimpleWriteGCPtr.
330 *
331 * @returns VBox status code.
332 *
333 * @param pVM The VM handle.
334 * @param GCPtrDst Where to start writing.
335 * @param pvSrc What to write.
336 * @param cb The number of bytes to attempt writing.
337 * @param fFlags Flags, MBZ.
338 * @param pcbWritten For store the actual number of bytes written, pass NULL
339 * if partial writes are unwanted.
340 * @todo Unused?
341 */
342VMMR3DECL(int) PGMR3DbgWriteGCPtr(PVM pVM, RTGCPTR GCPtrDst, void const *pvSrc, size_t cb, uint32_t fFlags, size_t *pcbWritten)
343{
344 /* validate */
345 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
346 AssertReturn(pVM, VERR_INVALID_PARAMETER);
347
348 /* @todo SMP support! */
349 PVMCPU pVCpu = &pVM->aCpus[0];
350
351/** @todo deal with HMA */
352 /* try simple first. */
353 int rc = PGMPhysSimpleWriteGCPtr(pVCpu, GCPtrDst, pvSrc, cb);
354 if (RT_SUCCESS(rc) || !pcbWritten)
355 return rc;
356
357 /* partial write that failed, chop it up in pages. */
358 *pcbWritten = 0;
359 rc = VINF_SUCCESS;
360 while (cb > 0)
361 {
362 size_t cbChunk = PAGE_SIZE;
363 cbChunk -= GCPtrDst & PAGE_OFFSET_MASK;
364 if (cbChunk > cb)
365 cbChunk = cb;
366
367 rc = PGMPhysSimpleWriteGCPtr(pVCpu, GCPtrDst, pvSrc, cbChunk);
368
369 /* advance */
370 if (RT_FAILURE(rc))
371 break;
372 *pcbWritten += cbChunk;
373 cb -= cbChunk;
374 GCPtrDst += cbChunk;
375 pvSrc = (uint8_t const *)pvSrc + cbChunk;
376 }
377
378 return *pcbWritten && RT_FAILURE(rc) ? -rc : rc;
379
380}
381
382
383/**
384 * memchr() with alignment considerations.
385 *
386 * @returns Pointer to matching byte, NULL if none found.
387 * @param pb Where to search. Aligned.
388 * @param b What to search for.
389 * @param cb How much to search .
390 * @param uAlign The alignment restriction of the result.
391 */
392static const uint8_t *pgmR3DbgAlignedMemChr(const uint8_t *pb, uint8_t b, size_t cb, uint32_t uAlign)
393{
394 const uint8_t *pbRet;
395 if (uAlign <= 32)
396 {
397 pbRet = (const uint8_t *)memchr(pb, b, cb);
398 if ((uintptr_t)pbRet & (uAlign - 1))
399 {
400 do
401 {
402 pbRet++;
403 size_t cbLeft = cb - (pbRet - pb);
404 if (!cbLeft)
405 {
406 pbRet = NULL;
407 break;
408 }
409 pbRet = (const uint8_t *)memchr(pbRet, b, cbLeft);
410 } while ((uintptr_t)pbRet & (uAlign - 1));
411 }
412 }
413 else
414 {
415 pbRet = NULL;
416 if (cb)
417 {
418 for (;;)
419 {
420 if (*pb == b)
421 {
422 pbRet = pb;
423 break;
424 }
425 if (cb <= uAlign)
426 break;
427 cb -= uAlign;
428 pb += uAlign;
429 }
430 }
431 }
432 return pbRet;
433}
434
435
436/**
437 * Scans a page for a byte string, keeping track of potential
438 * cross page matches.
439 *
440 * @returns true and *poff on match.
441 * false on mismatch.
442 * @param pbPage Pointer to the current page.
443 * @param poff Input: The offset into the page (aligned).
444 * Output: The page offset of the match on success.
445 * @param cb The number of bytes to search, starting of *poff.
446 * @param uAlign The needle alignment. This is of course less than a page.
447 * @param pabNeedle The byte string to search for.
448 * @param cbNeedle The length of the byte string.
449 * @param pabPrev The buffer that keeps track of a partial match that we
450 * bring over from the previous page. This buffer must be
451 * at least cbNeedle - 1 big.
452 * @param pcbPrev Input: The number of partial matching bytes from the previous page.
453 * Output: The number of partial matching bytes from this page.
454 * Initialize to 0 before the first call to this function.
455 */
456static bool pgmR3DbgScanPage(const uint8_t *pbPage, int32_t *poff, uint32_t cb, uint32_t uAlign,
457 const uint8_t *pabNeedle, size_t cbNeedle,
458 uint8_t *pabPrev, size_t *pcbPrev)
459{
460 /*
461 * Try complete any partial match from the previous page.
462 */
463 if (*pcbPrev > 0)
464 {
465 size_t cbPrev = *pcbPrev;
466 Assert(!*poff);
467 Assert(cbPrev < cbNeedle);
468 if (!memcmp(pbPage, pabNeedle + cbPrev, cbNeedle - cbPrev))
469 {
470 if (cbNeedle - cbPrev > cb)
471 return false;
472 *poff = -(int32_t)cbPrev;
473 return true;
474 }
475
476 /* check out the remainder of the previous page. */
477 const uint8_t *pb = pabPrev;
478 for (;;)
479 {
480 if (cbPrev <= uAlign)
481 break;
482 cbPrev -= uAlign;
483 pb = pgmR3DbgAlignedMemChr(pb + uAlign, *pabNeedle, cbPrev, uAlign);
484 if (!pb)
485 break;
486 cbPrev = *pcbPrev - (pb - pabPrev);
487 if ( !memcmp(pb + 1, &pabNeedle[1], cbPrev - 1)
488 && !memcmp(pbPage, pabNeedle + cbPrev, cbNeedle - cbPrev))
489 {
490 if (cbNeedle - cbPrev > cb)
491 return false;
492 *poff = -(int32_t)cbPrev;
493 return true;
494 }
495 }
496
497 *pcbPrev = 0;
498 }
499
500 /*
501 * Match the body of the page.
502 */
503 const uint8_t *pb = pbPage + *poff;
504 const uint8_t *pbEnd = pb + cb;
505 for (;;)
506 {
507 pb = pgmR3DbgAlignedMemChr(pb, *pabNeedle, cb, uAlign);
508 if (!pb)
509 break;
510 cb = pbEnd - pb;
511 if (cb >= cbNeedle)
512 {
513 /* match? */
514 if (!memcmp(pb + 1, &pabNeedle[1], cbNeedle - 1))
515 {
516 *poff = pb - pbPage;
517 return true;
518 }
519 }
520 else
521 {
522 /* paritial match at the end of the page? */
523 if (!memcmp(pb + 1, &pabNeedle[1], cb - 1))
524 {
525 /* We're copying one byte more that we really need here, but wtf. */
526 memcpy(pabPrev, pb, cb);
527 *pcbPrev = cb;
528 return false;
529 }
530 }
531
532 /* no match, skip ahead. */
533 if (cb <= uAlign)
534 break;
535 pb += uAlign;
536 cb -= uAlign;
537 }
538
539 return false;
540}
541
542
543/**
544 * Scans guest physical memory for a byte string.
545 *
546 * @returns VBox status codes:
547 * @retval VINF_SUCCESS and *pGCPtrHit on success.
548 * @retval VERR_DBGF_MEM_NOT_FOUND if not found.
549 * @retval VERR_INVALID_POINTER if any of the pointer arguments are invalid.
550 * @retval VERR_INVALID_ARGUMENT if any other arguments are invalid.
551 *
552 * @param pVM Pointer to the shared VM structure.
553 * @param GCPhys Where to start searching.
554 * @param cbRange The number of bytes to search.
555 * @param GCPhysAlign The alignment of the needle. Must be a power of two
556 * and less or equal to 4GB.
557 * @param pabNeedle The byte string to search for.
558 * @param cbNeedle The length of the byte string. Max 256 bytes.
559 * @param pGCPhysHit Where to store the address of the first occurence on success.
560 */
561VMMR3DECL(int) PGMR3DbgScanPhysical(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS cbRange, RTGCPHYS GCPhysAlign,
562 const uint8_t *pabNeedle, size_t cbNeedle, PRTGCPHYS pGCPhysHit)
563{
564 /*
565 * Validate and adjust the input a bit.
566 */
567 if (!VALID_PTR(pGCPhysHit))
568 return VERR_INVALID_POINTER;
569 *pGCPhysHit = NIL_RTGCPHYS;
570
571 if ( !VALID_PTR(pabNeedle)
572 || GCPhys == NIL_RTGCPHYS)
573 return VERR_INVALID_POINTER;
574 if (!cbNeedle)
575 return VERR_INVALID_PARAMETER;
576 if (cbNeedle > MAX_NEEDLE_SIZE)
577 return VERR_INVALID_PARAMETER;
578
579 if (!cbRange)
580 return VERR_DBGF_MEM_NOT_FOUND;
581 if (GCPhys + cbNeedle - 1 < GCPhys)
582 return VERR_DBGF_MEM_NOT_FOUND;
583
584 if (!GCPhysAlign)
585 return VERR_INVALID_PARAMETER;
586 if (GCPhysAlign > UINT32_MAX)
587 return VERR_NOT_POWER_OF_TWO;
588 if (GCPhysAlign & (GCPhysAlign - 1))
589 return VERR_INVALID_PARAMETER;
590
591 if (GCPhys & (GCPhysAlign - 1))
592 {
593 RTGCPHYS Adj = GCPhysAlign - (GCPhys & (GCPhysAlign - 1));
594 if ( cbRange <= Adj
595 || GCPhys + Adj < GCPhys)
596 return VERR_DBGF_MEM_NOT_FOUND;
597 GCPhys += Adj;
598 cbRange -= Adj;
599 }
600
601 const bool fAllZero = ASMMemIsAll8(pabNeedle, cbNeedle, 0) == NULL;
602 const uint32_t cIncPages = GCPhysAlign <= PAGE_SIZE
603 ? 1
604 : GCPhysAlign >> PAGE_SHIFT;
605 const RTGCPHYS GCPhysLast = GCPhys + cbRange - 1 >= GCPhys
606 ? GCPhys + cbRange - 1
607 : ~(RTGCPHYS)0;
608
609 /*
610 * Search the memory - ignore MMIO and zero pages, also don't
611 * bother to match across ranges.
612 */
613 pgmLock(pVM);
614 for (PPGMRAMRANGE pRam = pVM->pgm.s.CTX_SUFF(pRamRanges);
615 pRam;
616 pRam = pRam->CTX_SUFF(pNext))
617 {
618 /*
619 * If the search range starts prior to the current ram range record,
620 * adjust the search range and possibly conclude the search.
621 */
622 RTGCPHYS off;
623 if (GCPhys < pRam->GCPhys)
624 {
625 if (GCPhysLast < pRam->GCPhys)
626 break;
627 GCPhys = pRam->GCPhys;
628 off = 0;
629 }
630 else
631 off = GCPhys - pRam->GCPhys;
632 if (off < pRam->cb)
633 {
634 /*
635 * Iterate the relevant pages.
636 */
637 uint8_t abPrev[MAX_NEEDLE_SIZE];
638 size_t cbPrev = 0;
639 const uint32_t cPages = pRam->cb >> PAGE_SHIFT;
640 uint32_t iPage = off >> PAGE_SHIFT;
641 uint32_t offPage = GCPhys & PAGE_OFFSET_MASK;
642 GCPhys &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
643 for (;; offPage = 0)
644 {
645 PPGMPAGE pPage = &pRam->aPages[iPage];
646 if ( ( !PGM_PAGE_IS_ZERO(pPage)
647 || fAllZero)
648 && !PGM_PAGE_IS_BALLOONED(pPage)
649 && !PGM_PAGE_IS_MMIO(pPage))
650 {
651 void const *pvPage;
652 PGMPAGEMAPLOCK Lock;
653 int rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, GCPhys, &pvPage, &Lock);
654 if (RT_SUCCESS(rc))
655 {
656 int32_t offHit = offPage;
657 bool fRc;
658 if (GCPhysAlign < PAGE_SIZE)
659 {
660 uint32_t cbSearch = (GCPhys ^ GCPhysLast) & ~(RTGCPHYS)PAGE_OFFSET_MASK
661 ? PAGE_SIZE - (uint32_t)offPage
662 : (GCPhysLast & PAGE_OFFSET_MASK) + 1 - (uint32_t)offPage;
663 fRc = pgmR3DbgScanPage((uint8_t const *)pvPage, &offHit, cbSearch, (uint32_t)GCPhysAlign,
664 pabNeedle, cbNeedle, &abPrev[0], &cbPrev);
665 }
666 else
667 fRc = memcmp(pvPage, pabNeedle, cbNeedle) == 0
668 && (GCPhysLast - GCPhys) >= cbNeedle;
669 PGMPhysReleasePageMappingLock(pVM, &Lock);
670 if (fRc)
671 {
672 *pGCPhysHit = GCPhys + offHit;
673 pgmUnlock(pVM);
674 return VINF_SUCCESS;
675 }
676 }
677 else
678 cbPrev = 0; /* ignore error. */
679 }
680 else
681 cbPrev = 0;
682
683 /* advance to the next page. */
684 GCPhys += (RTGCPHYS)cIncPages << PAGE_SHIFT;
685 if (GCPhys >= GCPhysLast) /* (may not always hit, but we're run out of ranges.) */
686 {
687 pgmUnlock(pVM);
688 return VERR_DBGF_MEM_NOT_FOUND;
689 }
690 iPage += cIncPages;
691 if ( iPage < cIncPages
692 || iPage >= cPages)
693 break;
694 }
695 }
696 }
697 pgmUnlock(pVM);
698 return VERR_DBGF_MEM_NOT_FOUND;
699}
700
701
702/**
703 * Scans (guest) virtual memory for a byte string.
704 *
705 * @returns VBox status codes:
706 * @retval VINF_SUCCESS and *pGCPtrHit on success.
707 * @retval VERR_DBGF_MEM_NOT_FOUND if not found.
708 * @retval VERR_INVALID_POINTER if any of the pointer arguments are invalid.
709 * @retval VERR_INVALID_ARGUMENT if any other arguments are invalid.
710 *
711 * @param pVM Pointer to the shared VM structure.
712 * @param pVCpu The CPU context to search in.
713 * @param GCPtr Where to start searching.
714 * @param GCPtrAlign The alignment of the needle. Must be a power of two
715 * and less or equal to 4GB.
716 * @param cbRange The number of bytes to search. Max 256 bytes.
717 * @param pabNeedle The byte string to search for.
718 * @param cbNeedle The length of the byte string.
719 * @param pGCPtrHit Where to store the address of the first occurence on success.
720 */
721VMMR3DECL(int) PGMR3DbgScanVirtual(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, RTGCPTR cbRange, RTGCPTR GCPtrAlign,
722 const uint8_t *pabNeedle, size_t cbNeedle, PRTGCUINTPTR pGCPtrHit)
723{
724 VMCPU_ASSERT_EMT(pVCpu);
725
726 /*
727 * Validate and adjust the input a bit.
728 */
729 if (!VALID_PTR(pGCPtrHit))
730 return VERR_INVALID_POINTER;
731 *pGCPtrHit = 0;
732
733 if (!VALID_PTR(pabNeedle))
734 return VERR_INVALID_POINTER;
735 if (!cbNeedle)
736 return VERR_INVALID_PARAMETER;
737 if (cbNeedle > MAX_NEEDLE_SIZE)
738 return VERR_INVALID_PARAMETER;
739
740 if (!cbRange)
741 return VERR_DBGF_MEM_NOT_FOUND;
742 if (GCPtr + cbNeedle - 1 < GCPtr)
743 return VERR_DBGF_MEM_NOT_FOUND;
744
745 if (!GCPtrAlign)
746 return VERR_INVALID_PARAMETER;
747 if (GCPtrAlign > UINT32_MAX)
748 return VERR_NOT_POWER_OF_TWO;
749 if (GCPtrAlign & (GCPtrAlign - 1))
750 return VERR_INVALID_PARAMETER;
751
752 if (GCPtr & (GCPtrAlign - 1))
753 {
754 RTGCPTR Adj = GCPtrAlign - (GCPtr & (GCPtrAlign - 1));
755 if ( cbRange <= Adj
756 || GCPtr + Adj < GCPtr)
757 return VERR_DBGF_MEM_NOT_FOUND;
758 GCPtr += Adj;
759 cbRange -= Adj;
760 }
761
762 /*
763 * Search the memory - ignore MMIO, zero and not-present pages.
764 */
765 const bool fAllZero = ASMMemIsAll8(pabNeedle, cbNeedle, 0) == NULL;
766 PGMMODE enmMode = PGMGetGuestMode(pVCpu);
767 RTGCPTR GCPtrMask = PGMMODE_IS_LONG_MODE(enmMode) ? UINT64_MAX : UINT32_MAX;
768 uint8_t abPrev[MAX_NEEDLE_SIZE];
769 size_t cbPrev = 0;
770 const uint32_t cIncPages = GCPtrAlign <= PAGE_SIZE
771 ? 1
772 : GCPtrAlign >> PAGE_SHIFT;
773 const RTGCPTR GCPtrLast = GCPtr + cbRange - 1 >= GCPtr
774 ? (GCPtr + cbRange - 1) & GCPtrMask
775 : GCPtrMask;
776 RTGCPTR cPages = (((GCPtrLast - GCPtr) + (GCPtr & PAGE_OFFSET_MASK)) >> PAGE_SHIFT) + 1;
777 uint32_t offPage = GCPtr & PAGE_OFFSET_MASK;
778 GCPtr &= ~(RTGCPTR)PAGE_OFFSET_MASK;
779 for (;; offPage = 0)
780 {
781 RTGCPHYS GCPhys;
782 int rc = PGMPhysGCPtr2GCPhys(pVCpu, GCPtr, &GCPhys);
783 if (RT_SUCCESS(rc))
784 {
785 PPGMPAGE pPage = pgmPhysGetPage(&pVM->pgm.s, GCPhys);
786 if ( pPage
787 && ( !PGM_PAGE_IS_ZERO(pPage)
788 || fAllZero)
789 && !PGM_PAGE_IS_BALLOONED(pPage)
790 && !PGM_PAGE_IS_MMIO(pPage))
791 {
792 void const *pvPage;
793 PGMPAGEMAPLOCK Lock;
794 rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, GCPhys, &pvPage, &Lock);
795 if (RT_SUCCESS(rc))
796 {
797 int32_t offHit = offPage;
798 bool fRc;
799 if (GCPtrAlign < PAGE_SIZE)
800 {
801 uint32_t cbSearch = cPages > 0
802 ? PAGE_SIZE - (uint32_t)offPage
803 : (GCPtrLast & PAGE_OFFSET_MASK) + 1 - (uint32_t)offPage;
804 fRc = pgmR3DbgScanPage((uint8_t const *)pvPage, &offHit, cbSearch, (uint32_t)GCPtrAlign,
805 pabNeedle, cbNeedle, &abPrev[0], &cbPrev);
806 }
807 else
808 fRc = memcmp(pvPage, pabNeedle, cbNeedle) == 0
809 && (GCPtrLast - GCPtr) >= cbNeedle;
810 PGMPhysReleasePageMappingLock(pVM, &Lock);
811 if (fRc)
812 {
813 *pGCPtrHit = GCPtr + offHit;
814 return VINF_SUCCESS;
815 }
816 }
817 else
818 cbPrev = 0; /* ignore error. */
819 }
820 else
821 cbPrev = 0;
822 }
823 else
824 cbPrev = 0; /* ignore error. */
825
826 /* advance to the next page. */
827 if (cPages <= cIncPages)
828 break;
829 cPages -= cIncPages;
830 GCPtr += (RTGCPTR)cIncPages << PAGE_SHIFT;
831 }
832 return VERR_DBGF_MEM_NOT_FOUND;
833}
834
835
836/**
837 * The simple way out, too tired to think of a more elegant solution.
838 *
839 * @returns The base address of this page table/directory/whatever.
840 * @param pState The state where we get the current address.
841 * @param cShift The shift count for the table entries.
842 * @param cEntries The number of table entries.
843 * @param piFirst Where to return the table index of the first
844 * entry to dump.
845 * @param piLast Where to return the table index of the last
846 * entry.
847 */
848static uint64_t pgmR3DumpHierarchyCalcRange(PPGMR3DUMPHIERARCHYSTATE pState, uint32_t cShift, uint32_t cEntries,
849 uint32_t *piFirst, uint32_t *piLast)
850{
851 const uint64_t iBase = (pState->u64Address >> cShift) & ~(uint64_t)(cEntries - 1);
852 const uint64_t iFirst = pState->u64FirstAddress >> cShift;
853 const uint64_t iLast = pState->u64LastAddress >> cShift;
854
855 if ( iBase >= iFirst
856 && iBase + cEntries - 1 <= iLast)
857 {
858 /* full range. */
859 *piFirst = 0;
860 *piLast = cEntries - 1;
861 }
862 else if ( iBase + cEntries - 1 < iFirst
863 || iBase > iLast)
864 {
865 /* no match */
866 *piFirst = cEntries;
867 *piLast = 0;
868 }
869 else
870 {
871 /* partial overlap */
872 *piFirst = iBase <= iFirst
873 ? iFirst - iBase
874 : 0;
875 *piLast = iBase + cEntries - 1 <= iLast
876 ? cEntries - 1
877 : iLast - iBase;
878 }
879
880 return iBase << cShift;
881}
882
883
884/**
885 * Maps/finds the shadow page.
886 *
887 * @returns VBox status code.
888 * @param pState The dumper state.
889 * @param HCPhys The physical address of the shadow page.
890 * @param pszDesc The description.
891 * @param fIsMapping Set if it's a mapping.
892 * @param ppv Where to return the pointer.
893 */
894static int pgmR3DumpHierarchyHcMapPage(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, const char *pszDesc,
895 bool fIsMapping, void **ppv)
896{
897 void *pvPage;
898 if (!fIsMapping)
899 {
900 int rc = MMPagePhys2PageTry(pState->pVM, HCPhys, &pvPage);
901 if (RT_FAILURE(rc))
902 {
903 pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! %s at HCPhys=%RHp was not found in the page pool!\n",
904 pState->cchAddress, pState->u64Address, pszDesc, HCPhys);
905 return rc;
906 }
907 }
908 else
909 {
910 pvPage = NULL;
911 for (PPGMMAPPING pMap = pState->pVM->pgm.s.pMappingsR3; pMap; pMap = pMap->pNextR3)
912 {
913 uint64_t off = pState->u64Address - pMap->GCPtr;
914 if (off < pMap->cb)
915 {
916 const int iPDE = (uint32_t)(off >> X86_PD_SHIFT);
917 const int iSub = (int)((off >> X86_PD_PAE_SHIFT) & 1); /* MSC is a pain sometimes */
918 if ((iSub ? pMap->aPTs[iPDE].HCPhysPaePT1 : pMap->aPTs[iPDE].HCPhysPaePT0) != HCPhys)
919 pState->pHlp->pfnPrintf(pState->pHlp,
920 "%0*llx error! Mapping error! PT %d has HCPhysPT=%RHp not %RHp is in the PD.\n",
921 pState->cchAddress, pState->u64Address, iPDE,
922 iSub ? pMap->aPTs[iPDE].HCPhysPaePT1 : pMap->aPTs[iPDE].HCPhysPaePT0, HCPhys);
923 pvPage = &pMap->aPTs[iPDE].paPaePTsR3[iSub];
924 break;
925 }
926 }
927 if (!pvPage)
928 {
929 pState->pHlp->pfnPrintf(pState->pHlp, "%0*llx error! PT mapping %s at HCPhys=%RHp was not found in the page pool!\n",
930 pState->cchAddress, pState->u64Address, HCPhys);
931 return VERR_INVALID_PARAMETER;
932 }
933 }
934 *ppv = pvPage;
935 return VINF_SUCCESS;
936}
937
938
939/**
940 * Dumps the a shadow page summary or smth.
941 *
942 * @param pState The dumper state.
943 * @param HCPhys The page address.
944 */
945static void pgmR3DumpHierarchyHcShwPageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys)
946{
947 /* later */
948 NOREF(pState); NOREF(HCPhys);
949}
950
951
952/**
953 * Figures out which guest page this is and dumps a summary.
954 *
955 * @param pState The dumper state.
956 * @param HCPhys The page address.
957 * @param cbPage The page size.
958 */
959static void pgmR3DumpHierarchyHcPageInfo(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, uint32_t cbPage)
960{
961 /* later */
962 NOREF(pState); NOREF(HCPhys); NOREF(cbPage);
963 RTGCPHYS GCPhys;
964 int rc = PGMR3DbgHCPhys2GCPhys(pState->pVM, HCPhys, &GCPhys);
965 if (RT_SUCCESS(rc))
966 {
967 pgmLock(pState->pVM);
968 char szPage[80];
969 PCPGMPAGE pPage = pgmPhysGetPage(&pState->pVM->pgm.s, GCPhys);
970 if (pPage)
971 RTStrPrintf(szPage, sizeof(szPage), "%R[pgmpage]", pPage);
972 else
973 strcpy(szPage, "not found");
974 pgmUnlock(pState->pVM);
975 pState->pHlp->pfnPrintf(pState->pHlp, " -> %RGp %s", GCPhys, szPage);
976 }
977 else
978 pState->pHlp->pfnPrintf(pState->pHlp, " not found");
979}
980
981
982/**
983 * Dumps a PAE shadow page table.
984 *
985 * @returns VBox status code (VINF_SUCCESS).
986 * @param pState The dumper state.
987 * @param HCPhys The page table address.
988 * @param fIsMapping Whether it is a mapping.
989 */
990static int pgmR3DumpHierarchyHCPaePT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, bool fIsMapping)
991{
992 PPGMSHWPTPAE pPT;
993 int rc = pgmR3DumpHierarchyHcMapPage(pState, HCPhys, "Page table", fIsMapping, (void **)&pPT);
994 if (RT_FAILURE(rc))
995 return rc;
996
997 uint32_t iFirst, iLast;
998 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
999 for (uint32_t i = iFirst; i <= iLast; i++)
1000 if (PGMSHWPTEPAE_GET_U(pPT->a[i]) & X86_PTE_P)
1001 {
1002 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PT_PAE_SHIFT);
1003 if (PGMSHWPTEPAE_IS_P(pPT->a[i]))
1004 {
1005 X86PTEPAE Pte;
1006 Pte.u = PGMSHWPTEPAE_GET_U(pPT->a[i]);
1007 pState->pHlp->pfnPrintf(pState->pHlp,
1008 pState->fLme /*P R S A D G WT CD AT NX 4M a p ? */
1009 ? "%016llx 3 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx"
1010 : "%08llx 2 | P %c %c %c %c %c %s %s %s %s 4K %c%c%c %016llx",
1011 pState->u64Address,
1012 Pte.n.u1Write ? 'W' : 'R',
1013 Pte.n.u1User ? 'U' : 'S',
1014 Pte.n.u1Accessed ? 'A' : '-',
1015 Pte.n.u1Dirty ? 'D' : '-',
1016 Pte.n.u1Global ? 'G' : '-',
1017 Pte.n.u1WriteThru ? "WT" : "--",
1018 Pte.n.u1CacheDisable? "CD" : "--",
1019 Pte.n.u1PAT ? "AT" : "--",
1020 Pte.n.u1NoExecute ? "NX" : "--",
1021 Pte.u & PGM_PTFLAGS_TRACK_DIRTY ? 'd' : '-',
1022 Pte.u & RT_BIT(10) ? '1' : '0',
1023 Pte.u & PGM_PTFLAGS_CSAM_VALIDATED? 'v' : '-',
1024 Pte.u & X86_PTE_PAE_PG_MASK);
1025 if (pState->fDumpPageInfo)
1026 pgmR3DumpHierarchyHcPageInfo(pState, Pte.u & X86_PTE_PAE_PG_MASK, _4K);
1027 if ((Pte.u >> 52) & 0x7ff)
1028 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx%s", (Pte.u >> 52) & 0x7ff, pState->fLme ? "" : "!");
1029 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1030 }
1031 else if ( (PGMSHWPTEPAE_GET_U(pPT->a[i]) & (pState->pVM->pgm.s.HCPhysInvMmioPg | X86_PTE_PAE_MBZ_MASK_NO_NX))
1032 == (pState->pVM->pgm.s.HCPhysInvMmioPg | X86_PTE_PAE_MBZ_MASK_NO_NX))
1033 pState->pHlp->pfnPrintf(pState->pHlp,
1034 pState->fLme
1035 ? "%016llx 3 | invalid / MMIO optimization\n"
1036 : "%08llx 2 | invalid / MMIO optimization\n",
1037 pState->u64Address);
1038 else
1039 pState->pHlp->pfnPrintf(pState->pHlp,
1040 pState->fLme
1041 ? "%016llx 3 | invalid: %RX64\n"
1042 : "%08llx 2 | invalid: %RX64\n",
1043 pState->u64Address, PGMSHWPTEPAE_GET_U(pPT->a[i]));
1044 pState->cLeaves++;
1045 }
1046 return VINF_SUCCESS;
1047}
1048
1049
1050/**
1051 * Dumps a PAE shadow page directory table.
1052 *
1053 * @returns VBox status code (VINF_SUCCESS).
1054 * @param pVM The VM handle.
1055 * @param HCPhys The physical address of the page directory table.
1056 * @param u64Address The virtual address of the page table starts.
1057 * @param cr4 The CR4, PSE is currently used.
1058 * @param fLongMode Set if this a long mode table; clear if it's a legacy mode table.
1059 * @param cMaxDepth The maxium depth.
1060 * @param pHlp Pointer to the output functions.
1061 */
1062static int pgmR3DumpHierarchyHCPaePD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1063{
1064 PX86PDPAE pPD;
1065 int rc = pgmR3DumpHierarchyHcMapPage(pState, HCPhys, "Page directory", false, (void **)&pPD);
1066 if (RT_FAILURE(rc))
1067 return rc;
1068
1069 Assert(cMaxDepth > 0);
1070 cMaxDepth--;
1071
1072 uint32_t iFirst, iLast;
1073 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_PAE_SHIFT, X86_PG_PAE_ENTRIES, &iFirst, &iLast);
1074 for (uint32_t i = iFirst; i <= iLast; i++)
1075 {
1076 X86PDEPAE Pde = pPD->a[i];
1077 if (Pde.n.u1Present)
1078 {
1079 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PD_PAE_SHIFT);
1080 if (Pde.b.u1Size)
1081 {
1082 pState->pHlp->pfnPrintf(pState->pHlp,
1083 pState->fLme /*P R S A D G WT CD AT NX 2M a p ? phys*/
1084 ? "%016llx 2 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx"
1085 : "%08llx 1 | P %c %c %c %c %c %s %s %s %s 2M %c%c%c %016llx",
1086 pState->u64Address,
1087 Pde.b.u1Write ? 'W' : 'R',
1088 Pde.b.u1User ? 'U' : 'S',
1089 Pde.b.u1Accessed ? 'A' : '-',
1090 Pde.b.u1Dirty ? 'D' : '-',
1091 Pde.b.u1Global ? 'G' : '-',
1092 Pde.b.u1WriteThru ? "WT" : "--",
1093 Pde.b.u1CacheDisable? "CD" : "--",
1094 Pde.b.u1PAT ? "AT" : "--",
1095 Pde.b.u1NoExecute ? "NX" : "--",
1096 Pde.u & RT_BIT_64(9) ? '1' : '0',
1097 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1098 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1099 Pde.u & X86_PDE2M_PAE_PG_MASK);
1100 if (pState->fDumpPageInfo)
1101 pgmR3DumpHierarchyHcPageInfo(pState, Pde.u & X86_PDE2M_PAE_PG_MASK, _2M);
1102 if ((Pde.u >> 52) & 0x7ff)
1103 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx%s", (Pde.u >> 52) & 0x7ff, pState->fLme ? "" : "!");
1104 if ((Pde.u >> 13) & 0xff)
1105 pState->pHlp->pfnPrintf(pState->pHlp, " 20:13=%02llx%s", (Pde.u >> 13) & 0x0ff, pState->fLme ? "" : "!");
1106 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1107
1108 pState->cLeaves++;
1109 }
1110 else
1111 {
1112 pState->pHlp->pfnPrintf(pState->pHlp,
1113 pState->fLme /*P R S A D G WT CD AT NX 4M a p ? phys */
1114 ? "%016llx 2 | P %c %c %c %c %c %s %s .. %s 4K %c%c%c %016llx"
1115 : "%08llx 1 | P %c %c %c %c %c %s %s .. %s 4K %c%c%c %016llx",
1116 pState->u64Address,
1117 Pde.n.u1Write ? 'W' : 'R',
1118 Pde.n.u1User ? 'U' : 'S',
1119 Pde.n.u1Accessed ? 'A' : '-',
1120 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
1121 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
1122 Pde.n.u1WriteThru ? "WT" : "--",
1123 Pde.n.u1CacheDisable? "CD" : "--",
1124 Pde.n.u1NoExecute ? "NX" : "--",
1125 Pde.u & RT_BIT_64(9) ? '1' : '0',
1126 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1127 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1128 Pde.u & X86_PDE_PAE_PG_MASK_FULL);
1129 if (pState->fDumpPageInfo)
1130 pgmR3DumpHierarchyHcShwPageInfo(pState, Pde.u & X86_PDE_PAE_PG_MASK_FULL);
1131 if ((Pde.u >> 52) & 0x7ff)
1132 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx!", (Pde.u >> 52) & 0x7ff);
1133 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1134
1135 if (cMaxDepth)
1136 {
1137 int rc2 = pgmR3DumpHierarchyHCPaePT(pState, Pde.u & X86_PDE_PAE_PG_MASK_FULL, !!(Pde.u & PGM_PDFLAGS_MAPPING));
1138 if (rc2 < rc && RT_SUCCESS(rc))
1139 rc = rc2;
1140 }
1141 else
1142 pState->cLeaves++;
1143 }
1144 }
1145 }
1146 return rc;
1147}
1148
1149
1150/**
1151 * Dumps a PAE shadow page directory pointer table.
1152 *
1153 * @returns VBox status code (VINF_SUCCESS).
1154 * @param pVM The VM handle.
1155 * @param HCPhys The physical address of the page directory pointer table.
1156 * @param u64Address The virtual address of the page table starts.
1157 * @param cr4 The CR4, PSE is currently used.
1158 * @param fLongMode Set if this a long mode table; clear if it's a legacy mode table.
1159 * @param cMaxDepth The maxium depth.
1160 * @param pHlp Pointer to the output functions.
1161 */
1162static int pgmR3DumpHierarchyHCPaePDPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1163{
1164 /* Fend of addresses that are out of range in PAE mode - simplifies the code below. */
1165 if (!pState->fLme && pState->u64Address >= _4G)
1166 return VINF_SUCCESS;
1167
1168 PX86PDPT pPDPT;
1169 int rc = pgmR3DumpHierarchyHcMapPage(pState, HCPhys, "Page directory pointer table", false, (void **)&pPDPT);
1170 if (RT_FAILURE(rc))
1171 return rc;
1172
1173 Assert(cMaxDepth > 0);
1174 cMaxDepth--;
1175
1176 uint32_t iFirst, iLast;
1177 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PDPT_SHIFT,
1178 pState->fLme ? X86_PG_AMD64_PDPE_ENTRIES : X86_PG_PAE_PDPE_ENTRIES,
1179 &iFirst, &iLast);
1180 for (uint32_t i = iFirst; i <= iLast; i++)
1181 {
1182 X86PDPE Pdpe = pPDPT->a[i];
1183 if (Pdpe.n.u1Present)
1184 {
1185 pState->u64Address = u64BaseAddress + ((uint64_t)i << X86_PDPT_SHIFT);
1186 if (pState->fLme)
1187 {
1188 pState->pHlp->pfnPrintf(pState->pHlp, /*P R S A D G WT CD AT NX .. a p ? */
1189 "%016llx 1 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
1190 pState->u64Address,
1191 Pdpe.lm.u1Write ? 'W' : 'R',
1192 Pdpe.lm.u1User ? 'U' : 'S',
1193 Pdpe.lm.u1Accessed ? 'A' : '-',
1194 Pdpe.lm.u3Reserved & 1? '?' : '.', /* ignored */
1195 Pdpe.lm.u3Reserved & 4? '!' : '.', /* mbz */
1196 Pdpe.lm.u1WriteThru ? "WT" : "--",
1197 Pdpe.lm.u1CacheDisable? "CD" : "--",
1198 Pdpe.lm.u3Reserved & 2? "!" : "..",/* mbz */
1199 Pdpe.lm.u1NoExecute ? "NX" : "--",
1200 Pdpe.u & RT_BIT(9) ? '1' : '0',
1201 Pdpe.u & PGM_PLXFLAGS_PERMANENT ? 'p' : '-',
1202 Pdpe.u & RT_BIT(11) ? '1' : '0',
1203 Pdpe.u & X86_PDPE_PG_MASK_FULL);
1204 if (pState->fDumpPageInfo)
1205 pgmR3DumpHierarchyHcShwPageInfo(pState, Pdpe.u & X86_PDPE_PG_MASK_FULL);
1206 if ((Pdpe.u >> 52) & 0x7ff)
1207 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx", (Pdpe.u >> 52) & 0x7ff);
1208 }
1209 else
1210 {
1211 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX .. a p ? */
1212 "%08llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
1213 pState->u64Address,
1214 Pdpe.n.u2Reserved & 1? '!' : '.', /* mbz */
1215 Pdpe.n.u2Reserved & 2? '!' : '.', /* mbz */
1216 Pdpe.n.u4Reserved & 1? '!' : '.', /* mbz */
1217 Pdpe.n.u4Reserved & 2? '!' : '.', /* mbz */
1218 Pdpe.n.u4Reserved & 8? '!' : '.', /* mbz */
1219 Pdpe.n.u1WriteThru ? "WT" : "--",
1220 Pdpe.n.u1CacheDisable? "CD" : "--",
1221 Pdpe.n.u4Reserved & 2? "!" : "..",/* mbz */
1222 Pdpe.lm.u1NoExecute ? "!!" : "..",/* mbz */
1223 Pdpe.u & RT_BIT(9) ? '1' : '0',
1224 Pdpe.u & PGM_PLXFLAGS_PERMANENT ? 'p' : '-',
1225 Pdpe.u & RT_BIT(11) ? '1' : '0',
1226 Pdpe.u & X86_PDPE_PG_MASK_FULL);
1227 if (pState->fDumpPageInfo)
1228 pgmR3DumpHierarchyHcShwPageInfo(pState, Pdpe.u & X86_PDPE_PG_MASK_FULL);
1229 if ((Pdpe.u >> 52) & 0xfff)
1230 pState->pHlp->pfnPrintf(pState->pHlp, " 63:52=%03llx!", (Pdpe.u >> 52) & 0xfff);
1231 }
1232 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1233
1234 if (cMaxDepth)
1235 {
1236 int rc2 = pgmR3DumpHierarchyHCPaePD(pState, Pdpe.u & X86_PDPE_PG_MASK_FULL, cMaxDepth);
1237 if (rc2 < rc && RT_SUCCESS(rc))
1238 rc = rc2;
1239 }
1240 else
1241 pState->cLeaves++;
1242 }
1243 }
1244 return rc;
1245}
1246
1247
1248/**
1249 * Dumps a 32-bit shadow page table.
1250 *
1251 * @returns VBox status code (VINF_SUCCESS).
1252 * @param pVM The VM handle.
1253 * @param HCPhys The physical address of the table.
1254 * @param cMaxDepth The maxium depth.
1255 */
1256static int pgmR3DumpHierarchyHcPaePML4(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1257{
1258 PX86PML4 pPML4;
1259 int rc = pgmR3DumpHierarchyHcMapPage(pState, HCPhys, "Page map level 4", false, (void **)&pPML4);
1260 if (RT_FAILURE(rc))
1261 return rc;
1262
1263 Assert(cMaxDepth);
1264 cMaxDepth--;
1265
1266 const uint64_t u64BaseAddress = u64BaseAddress & ~(RT_BIT_64(X86_PML4_SHIFT) - 1);
1267 uint32_t iFirst = u64BaseAddress >= pState->u64FirstAddress ? 0
1268 : RT_MIN((pState->u64FirstAddress -u64BaseAddress) >> X86_PML4_SHIFT, X86_PG_PAE_ENTRIES);
1269 uint32_t iLast = RT_MIN((pState->u64LastAddress - u64BaseAddress) >> X86_PML4_SHIFT, X86_PG_PAE_ENTRIES-1);
1270 for (uint32_t i = iFirst; i <= iLast; i++)
1271 {
1272 X86PML4E Pml4e = pPML4->a[i];
1273 if (Pml4e.n.u1Present)
1274 {
1275 pState->u64Address = ((uint64_t)i << X86_PML4_SHIFT)
1276 | (i >= RT_ELEMENTS(pPML4->a) / 2 ? UINT64_C(0xffff000000000000) : 0);
1277 pState->pHlp->pfnPrintf(pState->pHlp, /*P R S A D G WT CD AT NX 4M a p ? */
1278 "%016llx 0 | P %c %c %c %c %c %s %s %s %s .. %c%c%c %016llx",
1279 pState->u64Address,
1280 Pml4e.n.u1Write ? 'W' : 'R',
1281 Pml4e.n.u1User ? 'U' : 'S',
1282 Pml4e.n.u1Accessed ? 'A' : '-',
1283 Pml4e.n.u3Reserved & 1? '?' : '.', /* ignored */
1284 Pml4e.n.u3Reserved & 4? '!' : '.', /* mbz */
1285 Pml4e.n.u1WriteThru ? "WT" : "--",
1286 Pml4e.n.u1CacheDisable? "CD" : "--",
1287 Pml4e.n.u3Reserved & 2? "!" : "..",/* mbz */
1288 Pml4e.n.u1NoExecute ? "NX" : "--",
1289 Pml4e.u & RT_BIT(9) ? '1' : '0',
1290 Pml4e.u & PGM_PLXFLAGS_PERMANENT ? 'p' : '-',
1291 Pml4e.u & RT_BIT(11) ? '1' : '0',
1292 Pml4e.u & X86_PML4E_PG_MASK);
1293 if (pState->fDumpPageInfo)
1294 pgmR3DumpHierarchyHcShwPageInfo(pState, Pml4e.u & X86_PML4E_PG_MASK);
1295 if ((Pml4e.u >> 52) & 0x7ff)
1296 pState->pHlp->pfnPrintf(pState->pHlp, " 62:52=%03llx!", (Pml4e.u >> 52) & 0x7ff);
1297 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1298
1299 if (cMaxDepth)
1300 {
1301 int rc2 = pgmR3DumpHierarchyHCPaePDPT(pState, Pml4e.u & X86_PML4E_PG_MASK, cMaxDepth);
1302 if (rc2 < rc && RT_SUCCESS(rc))
1303 rc = rc2;
1304 }
1305 else
1306 pState->cLeaves++;
1307 }
1308 }
1309 return rc;
1310}
1311
1312
1313/**
1314 * Dumps a 32-bit shadow page table.
1315 *
1316 * @returns VBox status code (VINF_SUCCESS).
1317 * @param pVM The VM handle.
1318 * @param pPT Pointer to the page table.
1319 * @param u32Address The virtual address this table starts at.
1320 * @param pHlp Pointer to the output functions.
1321 */
1322static int pgmR3DumpHierarchyHC32BitPT(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, bool fMapping)
1323{
1324 PX86PT pPT;
1325 int rc = pgmR3DumpHierarchyHcMapPage(pState, HCPhys, "Page table", fMapping, (void **)&pPT);
1326 if (RT_FAILURE(rc))
1327 return rc;
1328
1329 uint32_t iFirst, iLast;
1330 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PT_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
1331 for (uint32_t i = iFirst; i <= iLast; i++)
1332 {
1333 X86PTE Pte = pPT->a[i];
1334 if (Pte.n.u1Present)
1335 {
1336 pState->u64Address = u64BaseAddress + (i << X86_PT_SHIFT);
1337 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d */
1338 "%08llx 1 | P %c %c %c %c %c %s %s %s .. 4K %c%c%c %08x",
1339 pState->u64Address,
1340 Pte.n.u1Write ? 'W' : 'R',
1341 Pte.n.u1User ? 'U' : 'S',
1342 Pte.n.u1Accessed ? 'A' : '-',
1343 Pte.n.u1Dirty ? 'D' : '-',
1344 Pte.n.u1Global ? 'G' : '-',
1345 Pte.n.u1WriteThru ? "WT" : "--",
1346 Pte.n.u1CacheDisable? "CD" : "--",
1347 Pte.n.u1PAT ? "AT" : "--",
1348 Pte.u & PGM_PTFLAGS_TRACK_DIRTY ? 'd' : '-',
1349 Pte.u & RT_BIT(10) ? '1' : '0',
1350 Pte.u & PGM_PTFLAGS_CSAM_VALIDATED ? 'v' : '-',
1351 Pte.u & X86_PDE_PG_MASK);
1352 if (pState->fDumpPageInfo)
1353 pgmR3DumpHierarchyHcPageInfo(pState, Pte.u & X86_PDE_PG_MASK, _4K);
1354 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1355 }
1356 }
1357 return VINF_SUCCESS;
1358}
1359
1360
1361/**
1362 * Dumps a 32-bit shadow page directory and page tables.
1363 *
1364 * @returns VBox status code (VINF_SUCCESS).
1365 * @param pVM The VM handle.
1366 * @param cr3 The root of the hierarchy.
1367 * @param cr4 The CR4, PSE is currently used.
1368 * @param cMaxDepth How deep into the hierarchy the dumper should go.
1369 * @param pHlp Pointer to the output functions.
1370 */
1371static int pgmR3DumpHierarchyHC32BitPD(PPGMR3DUMPHIERARCHYSTATE pState, RTHCPHYS HCPhys, unsigned cMaxDepth)
1372{
1373 if (pState->u64Address >= _4G)
1374 return VINF_SUCCESS;
1375
1376 PX86PD pPD;
1377 int rc = pgmR3DumpHierarchyHcMapPage(pState, HCPhys, "Page directory", false, (void **)&pPD);
1378 if (RT_FAILURE(rc))
1379 return rc;
1380
1381 Assert(cMaxDepth > 0);
1382 cMaxDepth--;
1383
1384 uint32_t iFirst, iLast;
1385 uint64_t u64BaseAddress = pgmR3DumpHierarchyCalcRange(pState, X86_PD_SHIFT, X86_PG_ENTRIES, &iFirst, &iLast);
1386 for (uint32_t i = iFirst; i <= iLast; i++)
1387 {
1388 X86PDE Pde = pPD->a[i];
1389 if (Pde.n.u1Present)
1390 {
1391 pState->u64Address = (uint32_t)i << X86_PD_SHIFT;
1392 if (Pde.b.u1Size && pState->fPse)
1393 {
1394 uint64_t u64Phys = ((uint64_t)(Pde.u & X86_PDE4M_PG_HIGH_MASK) << X86_PDE4M_PG_HIGH_SHIFT)
1395 | (Pde.u & X86_PDE4M_PG_MASK);
1396 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d phys */
1397 "%08llx 0 | P %c %c %c %c %c %s %s %s .. 4M %c%c%c %08llx",
1398 pState->u64Address,
1399 Pde.b.u1Write ? 'W' : 'R',
1400 Pde.b.u1User ? 'U' : 'S',
1401 Pde.b.u1Accessed ? 'A' : '-',
1402 Pde.b.u1Dirty ? 'D' : '-',
1403 Pde.b.u1Global ? 'G' : '-',
1404 Pde.b.u1WriteThru ? "WT" : "--",
1405 Pde.b.u1CacheDisable? "CD" : "--",
1406 Pde.b.u1PAT ? "AT" : "--",
1407 Pde.u & RT_BIT_32(9) ? '1' : '0',
1408 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1409 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1410 u64Phys);
1411 if (pState->fDumpPageInfo)
1412 pgmR3DumpHierarchyHcPageInfo(pState, u64Phys, _4M);
1413 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1414 pState->cLeaves++;
1415 }
1416 else
1417 {
1418 pState->pHlp->pfnPrintf(pState->pHlp,/*P R S A D G WT CD AT NX 4M a m d phys */
1419 "%08llx 0 | P %c %c %c %c %c %s %s .. .. 4K %c%c%c %08x",
1420 pState->u64Address,
1421 Pde.n.u1Write ? 'W' : 'R',
1422 Pde.n.u1User ? 'U' : 'S',
1423 Pde.n.u1Accessed ? 'A' : '-',
1424 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
1425 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
1426 Pde.n.u1WriteThru ? "WT" : "--",
1427 Pde.n.u1CacheDisable? "CD" : "--",
1428 Pde.u & RT_BIT_32(9) ? '1' : '0',
1429 Pde.u & PGM_PDFLAGS_MAPPING ? 'm' : '-',
1430 Pde.u & PGM_PDFLAGS_TRACK_DIRTY ? 'd' : '-',
1431 Pde.u & X86_PDE_PG_MASK);
1432 if (pState->fDumpPageInfo)
1433 pgmR3DumpHierarchyHcShwPageInfo(pState, Pde.u & X86_PDE_PG_MASK);
1434 pState->pHlp->pfnPrintf(pState->pHlp, "\n");
1435
1436 if (cMaxDepth)
1437 {
1438 int rc2 = pgmR3DumpHierarchyHC32BitPT(pState, Pde.u & X86_PDE_PG_MASK, !!(Pde.u & PGM_PDFLAGS_MAPPING));
1439 if (rc2 < rc && RT_SUCCESS(rc))
1440 rc = rc2;
1441 }
1442 else
1443 pState->cLeaves++;
1444 }
1445 }
1446 }
1447
1448 return rc;
1449}
1450
1451
1452/**
1453 * Internal worker that initiates the actual dump.
1454 *
1455 * @returns VBox status code.
1456 * @param pState The dumper state.
1457 * @param cr3 The CR3 value.
1458 * @param cMaxDepth The max depth.
1459 */
1460static int pdmR3DumpHierarchyHcDoIt(PPGMR3DUMPHIERARCHYSTATE pState, uint64_t cr3, unsigned cMaxDepth)
1461{
1462 int rc;
1463 const unsigned cch = pState->cchAddress;
1464 if (pState->fEpt)
1465 {
1466 if (pState->fPrintHeader)
1467 pState->pHlp->pfnPrintf(pState->pHlp,
1468 "cr3=%0*llx Extended Page Tables\n"
1469 "%-*s R - Readable\n"
1470 "%-*s | W - Writeable\n"
1471 "%-*s | | X - Executable\n"
1472 "%-*s | | | EMT - EPT memory type\n"
1473 "%-*s | | | | PAT - Ignored PAT?\n"
1474 "%-*s | | | | | AVL1 - 4 available bits\n"
1475 "%-*s | | | | | | AVL2 - 12 available bits\n"
1476 "%-*s Level | | | | | | | page \n"
1477 /* xxxx n **** R W X EMT PAT AVL1 AVL2 xxxxxxxxxxxxx
1478 R W X 7 0 f fff 0123456701234567 */
1479 ,
1480 cch, cr3,
1481 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address");
1482
1483 pState->pHlp->pfnPrintf(pState->pHlp, "EPT dumping is not yet implemented, sorry.\n");
1484 /** @todo implemented EPT dumping. */
1485 rc = VERR_NOT_IMPLEMENTED;
1486 }
1487 else
1488 {
1489 if (pState->fPrintHeader)
1490 pState->pHlp->pfnPrintf(pState->pHlp,
1491 "cr3=%0*llx %s%s\n"
1492 "%-*s P - Present\n"
1493 "%-*s | R/W - Read (0) / Write (1)\n"
1494 "%-*s | | U/S - User (1) / Supervisor (0)\n"
1495 "%-*s | | | A - Accessed\n"
1496 "%-*s | | | | D - Dirty\n"
1497 "%-*s | | | | | G - Global\n"
1498 "%-*s | | | | | | WT - Write thru\n"
1499 "%-*s | | | | | | | CD - Cache disable\n"
1500 "%-*s | | | | | | | | AT - Attribute table (PAT)\n"
1501 "%-*s | | | | | | | | | NX - No execute (K8)\n"
1502 "%-*s | | | | | | | | | | 4K/4M/2M - Page size.\n"
1503 "%-*s | | | | | | | | | | | AVL - a=allocated; m=mapping; d=track dirty;\n"
1504 "%-*s | | | | | | | | | | | | p=permanent; v=validated;\n"
1505 "%-*s Level | | | | | | | | | | | | Page\n"
1506 /* xxxx n **** P R S A D G WT CD AT NX 4M AVL xxxxxxxxxxxxx
1507 - W U - - - -- -- -- -- -- 010 */
1508 ,
1509 cch, cr3,
1510 pState->fLme ? "Long Mode" : pState->fPae ? "PAE" : pState->fPse ? "32-bit w/ PSE" : "32-bit",
1511 pState->fNp ? " Nested Paging" : "",
1512 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "",
1513 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address");
1514 if (pState->fLme)
1515 rc = pgmR3DumpHierarchyHcPaePML4(pState, cr3 & X86_CR3_PAGE_MASK, cMaxDepth);
1516 else if (pState->fPae)
1517 rc = pgmR3DumpHierarchyHCPaePDPT(pState, cr3 & X86_CR3_PAE_PAGE_MASK, cMaxDepth);
1518 else
1519 rc = pgmR3DumpHierarchyHC32BitPD(pState, cr3 & X86_CR3_PAGE_MASK, cMaxDepth);
1520 }
1521 return rc;
1522}
1523
1524
1525/**
1526 * dbgfR3PagingDumpEx worker.
1527 *
1528 * @returns VBox status code.
1529 * @param pVM The VM handle.
1530 * @param cr3 The CR3 register value.
1531 * @param fFlags The flags, DBGFPGDMP_FLAGS_XXX.
1532 * @param u64FirstAddr The start address.
1533 * @param u64LastAddr The address to stop after.
1534 * @param cMaxDepth The max depth.
1535 * @param pHlp The output callbacks. Defaults to log if NULL.
1536 *
1537 * @internal
1538 */
1539VMMR3_INT_DECL(int) PGMR3DumpHierarchyHCEx(PVM pVM, uint64_t cr3, uint32_t fFlags, uint64_t u64FirstAddr, uint64_t u64LastAddr,
1540 uint32_t cMaxDepth, PCDBGFINFOHLP pHlp)
1541{
1542 /* Minimal validation as we're only supposed to service DBGF. */
1543 AssertReturn(~(fFlags & ~DBGFPGDMP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
1544 AssertReturn(!(fFlags & (DBGFPGDMP_FLAGS_CURRENT_MODE | DBGFPGDMP_FLAGS_CURRENT_CR3)), VERR_INVALID_PARAMETER);
1545 AssertReturn(fFlags & DBGFPGDMP_FLAGS_SHADOW, VERR_INVALID_PARAMETER);
1546
1547 PGMR3DUMPHIERARCHYSTATE State;
1548 State.pVM = pVM;
1549 State.pHlp = pHlp ? pHlp : DBGFR3InfoLogHlp();
1550 State.fPse = !!(fFlags & (DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME));
1551 State.fPae = !!(fFlags & (DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME));
1552 State.fLme = !!(fFlags & DBGFPGDMP_FLAGS_LME);
1553 State.fNp = !!(fFlags & DBGFPGDMP_FLAGS_NP);
1554 State.fEpt = !!(fFlags & DBGFPGDMP_FLAGS_EPT);
1555 State.cchAddress = State.fLme ? 16 : 8;
1556 State.fDumpPageInfo = !!(fFlags & DBGFPGDMP_FLAGS_PAGE_INFO);
1557 State.fPrintHeader = !!(fFlags & DBGFPGDMP_FLAGS_HEADER);
1558 State.u64Address = u64FirstAddr;
1559 State.u64FirstAddress = u64FirstAddr;
1560 State.u64LastAddress = u64LastAddr;
1561 State.cLeaves = 0;
1562 return pdmR3DumpHierarchyHcDoIt(&State, cr3, cMaxDepth);
1563}
1564
1565
1566/**
1567 * Dumps a page table hierarchy use only physical addresses and cr4/lm flags.
1568 *
1569 * @returns VBox status code (VINF_SUCCESS).
1570 * @param pVM The VM handle.
1571 * @param cr3 The root of the hierarchy.
1572 * @param cr4 The cr4, only PAE and PSE is currently used.
1573 * @param fLongMode Set if long mode, false if not long mode.
1574 * @param cMaxDepth Number of levels to dump.
1575 * @param pHlp Pointer to the output functions.
1576 */
1577VMMR3DECL(int) PGMR3DumpHierarchyHC(PVM pVM, uint64_t cr3, uint64_t cr4, bool fLongMode, unsigned cMaxDepth, PCDBGFINFOHLP pHlp)
1578{
1579 if (!cMaxDepth)
1580 return VINF_SUCCESS;
1581
1582 PGMR3DUMPHIERARCHYSTATE State;
1583 State.pVM = pVM;
1584 State.pHlp = pHlp ? pHlp : DBGFR3InfoLogHlp();
1585 State.fPse = (cr4 & X86_CR4_PSE) || (cr4 & X86_CR4_PAE) || fLongMode;
1586 State.fPae = (cr4 & X86_CR4_PAE) || fLongMode;
1587 State.fLme = fLongMode;
1588 State.fNp = false;
1589 State.fEpt = false;
1590 State.cchAddress = fLongMode ? 16 : 8;
1591 State.fDumpPageInfo = false;
1592 State.fPrintHeader = true;
1593 State.u64Address = 0;
1594 State.u64FirstAddress = 0;
1595 State.u64LastAddress = fLongMode ? UINT64_MAX : UINT32_MAX;
1596 State.cLeaves = 0;
1597 return pdmR3DumpHierarchyHcDoIt(&State, cr3, cMaxDepth);
1598}
1599
1600
1601
1602/**
1603 * Dumps a 32-bit shadow page table.
1604 *
1605 * @returns VBox status code (VINF_SUCCESS).
1606 * @param pVM The VM handle.
1607 * @param pPT Pointer to the page table.
1608 * @param u32Address The virtual address this table starts at.
1609 * @param PhysSearch Address to search for.
1610 */
1611int pgmR3DumpHierarchyGC32BitPT(PVM pVM, PX86PT pPT, uint32_t u32Address, RTGCPHYS PhysSearch)
1612{
1613 for (unsigned i = 0; i < RT_ELEMENTS(pPT->a); i++)
1614 {
1615 X86PTE Pte = pPT->a[i];
1616 if (Pte.n.u1Present)
1617 {
1618 Log(( /*P R S A D G WT CD AT NX 4M a m d */
1619 "%08x 1 | P %c %c %c %c %c %s %s %s .. 4K %c%c%c %08x\n",
1620 u32Address + (i << X86_PT_SHIFT),
1621 Pte.n.u1Write ? 'W' : 'R',
1622 Pte.n.u1User ? 'U' : 'S',
1623 Pte.n.u1Accessed ? 'A' : '-',
1624 Pte.n.u1Dirty ? 'D' : '-',
1625 Pte.n.u1Global ? 'G' : '-',
1626 Pte.n.u1WriteThru ? "WT" : "--",
1627 Pte.n.u1CacheDisable? "CD" : "--",
1628 Pte.n.u1PAT ? "AT" : "--",
1629 Pte.u & PGM_PTFLAGS_TRACK_DIRTY ? 'd' : '-',
1630 Pte.u & RT_BIT(10) ? '1' : '0',
1631 Pte.u & PGM_PTFLAGS_CSAM_VALIDATED ? 'v' : '-',
1632 Pte.u & X86_PDE_PG_MASK));
1633
1634 if ((Pte.u & X86_PDE_PG_MASK) == PhysSearch)
1635 {
1636 uint64_t fPageShw = 0;
1637 RTHCPHYS pPhysHC = 0;
1638
1639 /** @todo SMP support!! */
1640 PGMShwGetPage(&pVM->aCpus[0], (RTGCPTR)(u32Address + (i << X86_PT_SHIFT)), &fPageShw, &pPhysHC);
1641 Log(("Found %RGp at %RGv -> flags=%llx\n", PhysSearch, (RTGCPTR)(u32Address + (i << X86_PT_SHIFT)), fPageShw));
1642 }
1643 }
1644 }
1645 return VINF_SUCCESS;
1646}
1647
1648
1649/**
1650 * Dumps a 32-bit guest page directory and page tables.
1651 *
1652 * @returns VBox status code (VINF_SUCCESS).
1653 * @param pVM The VM handle.
1654 * @param cr3 The root of the hierarchy.
1655 * @param cr4 The CR4, PSE is currently used.
1656 * @param PhysSearch Address to search for.
1657 */
1658VMMR3DECL(int) PGMR3DumpHierarchyGC(PVM pVM, uint64_t cr3, uint64_t cr4, RTGCPHYS PhysSearch)
1659{
1660 bool fLongMode = false;
1661 const unsigned cch = fLongMode ? 16 : 8; NOREF(cch);
1662 PX86PD pPD = 0;
1663 PGMPAGEMAPLOCK LockCr3;
1664
1665 int rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, cr3 & X86_CR3_PAGE_MASK, (const void **)&pPD, &LockCr3);
1666 if ( RT_FAILURE(rc)
1667 || !pPD)
1668 {
1669 Log(("Page directory at %#x was not found in the page pool!\n", cr3 & X86_CR3_PAGE_MASK));
1670 return VERR_INVALID_PARAMETER;
1671 }
1672
1673 Log(("cr3=%08x cr4=%08x%s\n"
1674 "%-*s P - Present\n"
1675 "%-*s | R/W - Read (0) / Write (1)\n"
1676 "%-*s | | U/S - User (1) / Supervisor (0)\n"
1677 "%-*s | | | A - Accessed\n"
1678 "%-*s | | | | D - Dirty\n"
1679 "%-*s | | | | | G - Global\n"
1680 "%-*s | | | | | | WT - Write thru\n"
1681 "%-*s | | | | | | | CD - Cache disable\n"
1682 "%-*s | | | | | | | | AT - Attribute table (PAT)\n"
1683 "%-*s | | | | | | | | | NX - No execute (K8)\n"
1684 "%-*s | | | | | | | | | | 4K/4M/2M - Page size.\n"
1685 "%-*s | | | | | | | | | | | AVL - a=allocated; m=mapping; d=track dirty;\n"
1686 "%-*s | | | | | | | | | | | | p=permanent; v=validated;\n"
1687 "%-*s Level | | | | | | | | | | | | Page\n"
1688 /* xxxx n **** P R S A D G WT CD AT NX 4M AVL xxxxxxxxxxxxx
1689 - W U - - - -- -- -- -- -- 010 */
1690 , cr3, cr4, fLongMode ? " Long Mode" : "",
1691 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "",
1692 cch, "", cch, "", cch, "", cch, "", cch, "", cch, "", cch, "Address"));
1693
1694 for (unsigned i = 0; i < RT_ELEMENTS(pPD->a); i++)
1695 {
1696 X86PDE Pde = pPD->a[i];
1697 if (Pde.n.u1Present)
1698 {
1699 const uint32_t u32Address = i << X86_PD_SHIFT;
1700
1701 if ((cr4 & X86_CR4_PSE) && Pde.b.u1Size)
1702 Log(( /*P R S A D G WT CD AT NX 4M a m d */
1703 "%08x 0 | P %c %c %c %c %c %s %s %s .. 4M %c%c%c %08x\n",
1704 u32Address,
1705 Pde.b.u1Write ? 'W' : 'R',
1706 Pde.b.u1User ? 'U' : 'S',
1707 Pde.b.u1Accessed ? 'A' : '-',
1708 Pde.b.u1Dirty ? 'D' : '-',
1709 Pde.b.u1Global ? 'G' : '-',
1710 Pde.b.u1WriteThru ? "WT" : "--",
1711 Pde.b.u1CacheDisable? "CD" : "--",
1712 Pde.b.u1PAT ? "AT" : "--",
1713 Pde.u & RT_BIT(9) ? '1' : '0',
1714 Pde.u & RT_BIT(10) ? '1' : '0',
1715 Pde.u & RT_BIT(11) ? '1' : '0',
1716 pgmGstGet4MBPhysPage(&pVM->pgm.s, Pde)));
1717 /** @todo PhysSearch */
1718 else
1719 {
1720 Log(( /*P R S A D G WT CD AT NX 4M a m d */
1721 "%08x 0 | P %c %c %c %c %c %s %s .. .. 4K %c%c%c %08x\n",
1722 u32Address,
1723 Pde.n.u1Write ? 'W' : 'R',
1724 Pde.n.u1User ? 'U' : 'S',
1725 Pde.n.u1Accessed ? 'A' : '-',
1726 Pde.n.u1Reserved0 ? '?' : '.', /* ignored */
1727 Pde.n.u1Reserved1 ? '?' : '.', /* ignored */
1728 Pde.n.u1WriteThru ? "WT" : "--",
1729 Pde.n.u1CacheDisable? "CD" : "--",
1730 Pde.u & RT_BIT(9) ? '1' : '0',
1731 Pde.u & RT_BIT(10) ? '1' : '0',
1732 Pde.u & RT_BIT(11) ? '1' : '0',
1733 Pde.u & X86_PDE_PG_MASK));
1734 ////if (cMaxDepth >= 1)
1735 {
1736 /** @todo what about using the page pool for mapping PTs? */
1737 RTGCPHYS GCPhys = Pde.u & X86_PDE_PG_MASK;
1738 PX86PT pPT = NULL;
1739 PGMPAGEMAPLOCK LockPT;
1740
1741 rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, GCPhys, (const void **)&pPT, &LockPT);
1742
1743 int rc2 = VERR_INVALID_PARAMETER;
1744 if (pPT)
1745 rc2 = pgmR3DumpHierarchyGC32BitPT(pVM, pPT, u32Address, PhysSearch);
1746 else
1747 Log(("%08x error! Page table at %#x was not found in the page pool!\n", u32Address, GCPhys));
1748
1749 if (rc == VINF_SUCCESS)
1750 PGMPhysReleasePageMappingLock(pVM, &LockPT);
1751
1752 if (rc2 < rc && RT_SUCCESS(rc))
1753 rc = rc2;
1754 }
1755 }
1756 }
1757 }
1758 PGMPhysReleasePageMappingLock(pVM, &LockCr3);
1759 return rc;
1760}
1761
1762
1763/**
1764 * dbgfR3PagingDumpEx worker.
1765 *
1766 * @returns VBox status code.
1767 * @param pVM The VM handle.
1768 * @param cr3 The CR3 register value.
1769 * @param fFlags The flags, DBGFPGDMP_FLAGS_XXX.
1770 * @param FirstAddr The start address.
1771 * @param LastAddr The address to stop after.
1772 * @param cMaxDepth The max depth.
1773 * @param pHlp The output callbacks. Defaults to log if NULL.
1774 *
1775 * @internal
1776 */
1777VMMR3_INT_DECL(int) PGMR3DumpHierarchyGCEx(PVM pVM, uint64_t cr3, uint32_t fFlags, RTGCPTR FirstAddr, RTGCPTR LastAddr,
1778 uint32_t cMaxDepth, PCDBGFINFOHLP pHlp)
1779{
1780 /* Minimal validation as we're only supposed to service DBGF. */
1781 AssertReturn(~(fFlags & ~DBGFPGDMP_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
1782 AssertReturn(!(fFlags & (DBGFPGDMP_FLAGS_CURRENT_MODE | DBGFPGDMP_FLAGS_CURRENT_CR3)), VERR_INVALID_PARAMETER);
1783 AssertReturn(fFlags & DBGFPGDMP_FLAGS_GUEST, VERR_INVALID_PARAMETER);
1784
1785 PGMR3DUMPHIERARCHYSTATE State;
1786 State.pVM = pVM;
1787 State.pHlp = pHlp ? pHlp : DBGFR3InfoLogHlp();
1788 State.fPse = !!(fFlags & (DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME));
1789 State.fPae = !!(fFlags & (DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME));
1790 State.fLme = !!(fFlags & DBGFPGDMP_FLAGS_LME);
1791 State.fNp = !!(fFlags & DBGFPGDMP_FLAGS_NP);
1792 State.fEpt = !!(fFlags & DBGFPGDMP_FLAGS_EPT);
1793 State.cchAddress = State.fLme ? 16 : 8;
1794 State.fDumpPageInfo = !!(fFlags & DBGFPGDMP_FLAGS_PAGE_INFO);
1795 State.fPrintHeader = !!(fFlags & DBGFPGDMP_FLAGS_HEADER);
1796 State.u64Address = FirstAddr;
1797 State.u64FirstAddress = FirstAddr;
1798 State.u64LastAddress = LastAddr;
1799 State.cLeaves = 0;
1800 //return pdmR3DumpHierarchyGcDoIt(&State, cr3, cMaxDepth);
1801 return VERR_NOT_IMPLEMENTED;
1802}
1803
Note: See TracBrowser for help on using the repository browser.

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