VirtualBox

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

Last change on this file since 58123 was 58123, checked in by vboxsync, 10 years ago

VMM: Made @param pVCpu more uniform and to the point.

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