VirtualBox

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

最後變更 在這個檔案從57358是 57358,由 vboxsync 提交於 10 年 前

*: scm cleanup run.

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

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