VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCEmulateCodeView.cpp@ 9894

Last change on this file since 9894 was 9848, checked in by vboxsync, 17 years ago

Fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 151.4 KB
Line 
1/** $Id: DBGCEmulateCodeView.cpp 9848 2008-06-20 11:26:03Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, CodeView / WinDbg Emulation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_DBGC
26#include <VBox/dbg.h>
27#include <VBox/dbgf.h>
28#include <VBox/pgm.h>
29#include <VBox/selm.h>
30#include <VBox/cpum.h>
31#include <VBox/dis.h>
32#include <VBox/param.h>
33#include <VBox/err.h>
34#include <VBox/log.h>
35
36#include <iprt/alloc.h>
37#include <iprt/alloca.h>
38#include <iprt/string.h>
39#include <iprt/assert.h>
40#include <iprt/ctype.h>
41
42#include <stdlib.h>
43#include <stdio.h>
44
45#include "DBGCInternal.h"
46
47
48/*******************************************************************************
49* Internal Functions *
50*******************************************************************************/
51static DECLCALLBACK(int) dbgcCmdBrkAccess(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
52static DECLCALLBACK(int) dbgcCmdBrkClear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
53static DECLCALLBACK(int) dbgcCmdBrkDisable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
54static DECLCALLBACK(int) dbgcCmdBrkEnable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
55static DECLCALLBACK(int) dbgcCmdBrkList(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
56static DECLCALLBACK(int) dbgcCmdBrkSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
57static DECLCALLBACK(int) dbgcCmdBrkREM(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
58static DECLCALLBACK(int) dbgcCmdDumpMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
59static DECLCALLBACK(int) dbgcCmdDumpDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
60static DECLCALLBACK(int) dbgcCmdDumpIDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
61static DECLCALLBACK(int) dbgcCmdDumpPageDir(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
62static DECLCALLBACK(int) dbgcCmdDumpPageDirBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
63static DECLCALLBACK(int) dbgcCmdDumpPageTable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
64static DECLCALLBACK(int) dbgcCmdDumpPageTableBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
65static DECLCALLBACK(int) dbgcCmdDumpTSS(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
66static DECLCALLBACK(int) dbgcCmdGo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
67static DECLCALLBACK(int) dbgcCmdListSource(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
68static DECLCALLBACK(int) dbgcCmdListNear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
69static DECLCALLBACK(int) dbgcCmdMemoryInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
70static DECLCALLBACK(int) dbgcCmdReg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
71static DECLCALLBACK(int) dbgcCmdRegGuest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
72static DECLCALLBACK(int) dbgcCmdRegHyper(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
73static DECLCALLBACK(int) dbgcCmdRegTerse(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
74static DECLCALLBACK(int) dbgcCmdSearchMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
75static DECLCALLBACK(int) dbgcCmdSearchMemType(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
76static DECLCALLBACK(int) dbgcCmdStack(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
77static DECLCALLBACK(int) dbgcCmdTrace(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
78static DECLCALLBACK(int) dbgcCmdUnassemble(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
79
80
81/*******************************************************************************
82* Global Variables *
83*******************************************************************************/
84/** 'ba' arguments. */
85static const DBGCVARDESC g_aArgBrkAcc[] =
86{
87 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
88 { 1, 1, DBGCVAR_CAT_STRING, 0, "access", "The access type: x=execute, rw=read/write (alias r), w=write, i=not implemented." },
89 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "size", "The access size: 1, 2, 4, or 8. 'x' access requires 1, and 8 requires amd64 long mode." },
90 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
91 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
92 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "max passes", "The number of passes after which we stop triggering the breakpoint. (~0 is default)" },
93 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
94};
95
96
97/** 'bc', 'bd', 'be' arguments. */
98static const DBGCVARDESC g_aArgBrks[] =
99{
100 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
101 { 0, ~0, DBGCVAR_CAT_NUMBER, 0, "#bp", "Breakpoint number." },
102 { 0, 1, DBGCVAR_CAT_STRING, 0, "all", "All breakpoints." },
103};
104
105
106/** 'bp' arguments. */
107static const DBGCVARDESC g_aArgBrkSet[] =
108{
109 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
110 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
111 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
112 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "max passes", "The number of passes after which we stop triggering the breakpoint. (~0 is default)" },
113 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
114};
115
116
117/** 'br' arguments. */
118static const DBGCVARDESC g_aArgBrkREM[] =
119{
120 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
121 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
122 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
123 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "max passes", "The number of passes after which we stop triggering the breakpoint. (~0 is default)" },
124 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
125};
126
127
128/** 'd?' arguments. */
129static const DBGCVARDESC g_aArgDumpMem[] =
130{
131 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
132 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start dumping memory." },
133};
134
135
136/** 'dg', 'dga', 'dl', 'dla' arguments. */
137static const DBGCVARDESC g_aArgDumpDT[] =
138{
139 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
140 { 0, ~0, DBGCVAR_CAT_NUMBER, 0, "sel", "Selector or selector range." },
141 { 0, ~0, DBGCVAR_CAT_POINTER, 0, "address", "Far address which selector should be dumped." },
142};
143
144
145/** 'di', 'dia' arguments. */
146static const DBGCVARDESC g_aArgDumpIDT[] =
147{
148 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
149 { 0, ~0, DBGCVAR_CAT_NUMBER, 0, "int", "The interrupt vector or interrupt vector range." },
150};
151
152
153/** 'dpd*' arguments. */
154static const DBGCVARDESC g_aArgDumpPD[] =
155{
156 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
157 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "index", "Index into the page directory." },
158 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address which page directory entry to start dumping from. Range is applied to the page directory." },
159};
160
161
162/** 'dpda' arguments. */
163static const DBGCVARDESC g_aArgDumpPDAddr[] =
164{
165 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
166 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address of the page directory entry to start dumping from." },
167};
168
169
170/** 'dpt?' arguments. */
171static const DBGCVARDESC g_aArgDumpPT[] =
172{
173 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
174 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address which page directory entry to start dumping from." },
175};
176
177
178/** 'dpta' arguments. */
179static const DBGCVARDESC g_aArgDumpPTAddr[] =
180{
181 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
182 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address of the page table entry to start dumping from." },
183};
184
185
186/** 'dt' arguments. */
187static const DBGCVARDESC g_aArgDumpTSS[] =
188{
189 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
190 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "tss", "TSS selector number." },
191 { 0, 1, DBGCVAR_CAT_POINTER, 0, "tss:ign|addr", "TSS address. If the selector is a TSS selector, the offset will be ignored." }
192};
193
194
195/** 'ln' arguments. */
196static const DBGCVARDESC g_aArgListNear[] =
197{
198 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
199 { 0, ~0, DBGCVAR_CAT_POINTER, 0, "address", "Address of the symbol to look up." },
200 { 0, ~0, DBGCVAR_CAT_SYMBOL, 0, "symbol", "Symbol to lookup." },
201};
202
203/** 'ln' return. */
204static const DBGCVARDESC g_RetListNear =
205{
206 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "The last resolved symbol/address with adjusted range."
207};
208
209
210/** 'ls' arguments. */
211static const DBGCVARDESC g_aArgListSource[] =
212{
213 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
214 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start looking for source lines." },
215};
216
217
218/** 'm' argument. */
219static const DBGCVARDESC g_aArgMemoryInfo[] =
220{
221 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
222 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Pointer to obtain info about." },
223};
224
225
226/** 'r' arguments. */
227static const DBGCVARDESC g_aArgReg[] =
228{
229 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
230 { 0, 1, DBGCVAR_CAT_SYMBOL, 0, "register", "Register to show or set." },
231 { 0, 1, DBGCVAR_CAT_NUMBER_NO_RANGE, DBGCVD_FLAGS_DEP_PREV, "value", "New register value." },
232};
233
234
235/** 's' arguments. */
236static const DBGCVARDESC g_aArgSearchMem[] =
237{
238 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
239 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-b", "Byte string." },
240 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-w", "Word string." },
241 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-d", "DWord string." },
242 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-q", "QWord string." },
243 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-a", "ASCII string." },
244 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-u", "Unicode string." },
245 { 0, 1, DBGCVAR_CAT_OPTION_NUMBER, 0, "-n <Hits>", "Maximum number of hits." },
246 { 0, 1, DBGCVAR_CAT_GC_POINTER, 0, "range", "Register to show or set." },
247 { 0, ~0, DBGCVAR_CAT_ANY, 0, "pattern", "Pattern to search for." },
248};
249
250
251/** 's?' arguments. */
252static const DBGCVARDESC g_aArgSearchMemType[] =
253{
254 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
255 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "range", "Register to show or set." },
256 { 1, ~0, DBGCVAR_CAT_ANY, 0, "pattern", "Pattern to search for." },
257};
258
259
260/** 'u' arguments. */
261static const DBGCVARDESC g_aArgUnassemble[] =
262{
263 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
264 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start disassembling." },
265};
266
267
268/** Command descriptors for the CodeView / WinDbg emulation.
269 * The emulation isn't attempting to be identical, only somewhat similar.
270 */
271const DBGCCMD g_aCmdsCodeView[] =
272{
273 /* pszCmd, cArgsMin, cArgsMax, paArgDescs, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
274 { "ba", 3, 6, &g_aArgBrkAcc[0], RT_ELEMENTS(g_aArgBrkAcc), NULL, 0, dbgcCmdBrkAccess, "<access> <size> <address> [passes [max passes]] [cmds]",
275 "Sets a data access breakpoint." },
276 { "bc", 1, ~0, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), NULL, 0, dbgcCmdBrkClear, "all | <bp#> [bp# []]", "Enabled a set of breakpoints." },
277 { "bd", 1, ~0, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), NULL, 0, dbgcCmdBrkDisable, "all | <bp#> [bp# []]", "Disables a set of breakpoints." },
278 { "be", 1, ~0, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), NULL, 0, dbgcCmdBrkEnable, "all | <bp#> [bp# []]", "Enabled a set of breakpoints." },
279 { "bl", 0, 0, NULL, 0, NULL, 0, dbgcCmdBrkList, "", "Lists all the breakpoints." },
280 { "bp", 1, 4, &g_aArgBrkSet[0], RT_ELEMENTS(g_aArgBrkSet), NULL, 0, dbgcCmdBrkSet, "<address> [passes [max passes]] [cmds]",
281 "Sets a breakpoint (int 3)." },
282 { "br", 1, 4, &g_aArgBrkREM[0], RT_ELEMENTS(g_aArgBrkREM), NULL, 0, dbgcCmdBrkREM, "<address> [passes [max passes]] [cmds]",
283 "Sets a recompiler specific breakpoint." },
284 { "d", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), NULL, 0, dbgcCmdDumpMem, "[addr]", "Dump memory using last element size." },
285 { "da", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), NULL, 0, dbgcCmdDumpMem, "[addr]", "Dump memory as ascii string." },
286 { "db", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), NULL, 0, dbgcCmdDumpMem, "[addr]", "Dump memory in bytes." },
287 { "dd", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), NULL, 0, dbgcCmdDumpMem, "[addr]", "Dump memory in double words." },
288 { "dg", 0, ~0, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), NULL, 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the global descriptor table (GDT)." },
289 { "dga", 0, ~0, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), NULL, 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the global descriptor table (GDT) including not-present entries." },
290 { "di", 0, ~0, &g_aArgDumpIDT[0], RT_ELEMENTS(g_aArgDumpIDT), NULL, 0, dbgcCmdDumpIDT, "[int [..]]", "Dump the interrupt descriptor table (IDT)." },
291 { "dia", 0, ~0, &g_aArgDumpIDT[0], RT_ELEMENTS(g_aArgDumpIDT), NULL, 0, dbgcCmdDumpIDT, "[int [..]]", "Dump the interrupt descriptor table (IDT) including not-present entries." },
292 { "dl", 0, ~0, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), NULL, 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the local descriptor table (LDT)." },
293 { "dla", 0, ~0, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), NULL, 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the local descriptor table (LDT) including not-present entries." },
294 { "dpd", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), NULL, 0, dbgcCmdDumpPageDir, "[addr] [index]", "Dumps page directory entries of the default context." },
295 { "dpda", 0, 1, &g_aArgDumpPDAddr[0],RT_ELEMENTS(g_aArgDumpPDAddr),NULL, 0, dbgcCmdDumpPageDir, "[addr]", "Dumps specified page directory." },
296 { "dpdb", 1, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), NULL, 0, dbgcCmdDumpPageDirBoth, "[addr] [index]", "Dumps page directory entries of the guest and the hypervisor. " },
297 { "dpdg", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), NULL, 0, dbgcCmdDumpPageDir, "[addr] [index]", "Dumps page directory entries of the guest." },
298 { "dpdh", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), NULL, 0, dbgcCmdDumpPageDir, "[addr] [index]", "Dumps page directory entries of the hypervisor. " },
299 { "dpt", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), NULL, 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the default context." },
300 { "dpta", 1, 1, &g_aArgDumpPTAddr[0],RT_ELEMENTS(g_aArgDumpPTAddr), NULL, 0, dbgcCmdDumpPageTable,"<addr>", "Dumps specified page table." },
301 { "dptb", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), NULL, 0, dbgcCmdDumpPageTableBoth,"<addr>", "Dumps page table entries of the guest and the hypervisor." },
302 { "dptg", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), NULL, 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the guest." },
303 { "dpth", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), NULL, 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the hypervisor." },
304 { "dq", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), NULL, 0, dbgcCmdDumpMem, "[addr]", "Dump memory in quad words." },
305 { "dt", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), NULL, 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the task state segment (TSS)." },
306 { "dw", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), NULL, 0, dbgcCmdDumpMem, "[addr]", "Dump memory in words." },
307 { "g", 0, 0, NULL, 0, NULL, 0, dbgcCmdGo, "", "Continue execution." },
308 { "k", 0, 0, NULL, 0, NULL, 0, dbgcCmdStack, "", "Callstack." },
309 { "kg", 0, 0, NULL, 0, NULL, 0, dbgcCmdStack, "", "Callstack - guest." },
310 { "kh", 0, 0, NULL, 0, NULL, 0, dbgcCmdStack, "", "Callstack - hypervisor." },
311 { "ln", 0, ~0, &g_aArgListNear[0], RT_ELEMENTS(g_aArgListNear), &g_RetListNear, 0, dbgcCmdListNear, "[addr/sym [..]]", "List symbols near to the address. Default address is CS:EIP." },
312 { "ls", 0, 1, &g_aArgListSource[0],RT_ELEMENTS(g_aArgListSource),NULL, 0, dbgcCmdListSource, "[addr]", "Source." },
313 { "m", 1, 1, &g_aArgMemoryInfo[0],RT_ELEMENTS(g_aArgMemoryInfo),NULL, 0, dbgcCmdMemoryInfo, "<addr>", "Display information about that piece of memory." },
314 { "r", 0, 2, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), NULL, 0, dbgcCmdReg, "[reg [newval]]", "Show or set register(s) - active reg set." },
315 { "rg", 0, 2, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), NULL, 0, dbgcCmdRegGuest, "[reg [newval]]", "Show or set register(s) - guest reg set." },
316 { "rh", 0, 2, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), NULL, 0, dbgcCmdRegHyper, "[reg [newval]]", "Show or set register(s) - hypervisor reg set." },
317 { "rt", 0, 0, NULL, 0, NULL, 0, dbgcCmdRegTerse, "", "Toggles terse / verbose register info." },
318 { "s", 0, ~0, &g_aArgSearchMem[0], RT_ELEMENTS(g_aArgSearchMem), NULL, 0, dbgcCmdSearchMem, "[options] <range> <pattern>", "Continue last search." },
319 { "sa", 2, ~0, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType), NULL, 0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for an ascii string." },
320 { "sb", 2, ~0, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType), NULL, 0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more bytes." },
321 { "sd", 2, ~0, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType), NULL, 0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more double words." },
322 { "sq", 2, ~0, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType), NULL, 0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more quad words." },
323 { "su", 2, ~0, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType), NULL, 0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for an unicode string." },
324 { "sw", 2, ~0, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType), NULL, 0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more words." },
325 { "t", 0, 0, NULL, 0, NULL, 0, dbgcCmdTrace, "", "Instruction trace (step into)." },
326 { "u", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble),NULL, 0, dbgcCmdUnassemble, "[addr]", "Unassemble." },
327};
328
329/** The number of commands in the CodeView/WinDbg emulation. */
330const unsigned g_cCmdsCodeView = RT_ELEMENTS(g_aCmdsCodeView);
331
332
333
334/**
335 * The 'go' command.
336 *
337 * @returns VBox status.
338 * @param pCmd Pointer to the command descriptor (as registered).
339 * @param pCmdHlp Pointer to command helper functions.
340 * @param pVM Pointer to the current VM (if any).
341 * @param paArgs Pointer to (readonly) array of arguments.
342 * @param cArgs Number of arguments in the array.
343 */
344static DECLCALLBACK(int) dbgcCmdGo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
345{
346 /*
347 * Check if the VM is halted or not before trying to resume it.
348 */
349 if (!DBGFR3IsHalted(pVM))
350 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "warning: The VM is already running...\n");
351 else
352 {
353 int rc = DBGFR3Resume(pVM);
354 if (VBOX_FAILURE(rc))
355 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Executing DBGFR3Resume().");
356 }
357
358 NOREF(pCmd);
359 NOREF(paArgs);
360 NOREF(cArgs);
361 NOREF(pResult);
362 return 0;
363}
364
365
366/**
367 * The 'ba' command.
368 *
369 * @returns VBox status.
370 * @param pCmd Pointer to the command descriptor (as registered).
371 * @param pCmdHlp Pointer to command helper functions.
372 * @param pVM Pointer to the current VM (if any).
373 * @param paArgs Pointer to (readonly) array of arguments.
374 * @param cArgs Number of arguments in the array.
375 */
376static DECLCALLBACK(int) dbgcCmdBrkAccess(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR /*pResult*/)
377{
378 /*
379 * Interpret access type.
380 */
381 if ( !strchr("xrwi", paArgs[0].u.pszString[0])
382 || paArgs[0].u.pszString[1])
383 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid access type '%s' for '%s'. Valid types are 'e', 'r', 'w' and 'i'.\n",
384 paArgs[0].u.pszString, pCmd->pszCmd);
385 uint8_t fType = 0;
386 switch (paArgs[0].u.pszString[0])
387 {
388 case 'x': fType = X86_DR7_RW_EO; break;
389 case 'r': fType = X86_DR7_RW_RW; break;
390 case 'w': fType = X86_DR7_RW_WO; break;
391 case 'i': fType = X86_DR7_RW_IO; break;
392 }
393
394 /*
395 * Validate size.
396 */
397 if (fType == X86_DR7_RW_EO && paArgs[1].u.u64Number != 1)
398 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid access size %RX64 for '%s'. 'x' access type requires size 1!\n",
399 paArgs[1].u.u64Number, pCmd->pszCmd);
400 switch (paArgs[1].u.u64Number)
401 {
402 case 1:
403 case 2:
404 case 4:
405 break;
406 /*case 8: - later*/
407 default:
408 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid access size %RX64 for '%s'. 1, 2 or 4!\n",
409 paArgs[1].u.u64Number, pCmd->pszCmd);
410 }
411 uint8_t cb = (uint8_t)paArgs[1].u.u64Number;
412
413 /*
414 * Convert the pointer to a DBGF address.
415 */
416 DBGFADDRESS Address;
417 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[2], &Address);
418 if (VBOX_FAILURE(rc))
419 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Couldn't convert '%DV' to a DBGF address, rc=%Vrc.\n", &paArgs[2], rc);
420
421 /*
422 * Pick out the optional arguments.
423 */
424 uint64_t iHitTrigger = 0;
425 uint64_t iHitDisable = ~0;
426 const char *pszCmds = NULL;
427 unsigned iArg = 3;
428 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
429 {
430 iHitTrigger = paArgs[iArg].u.u64Number;
431 iArg++;
432 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
433 {
434 iHitDisable = paArgs[iArg].u.u64Number;
435 iArg++;
436 }
437 }
438 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
439 {
440 pszCmds = paArgs[iArg].u.pszString;
441 iArg++;
442 }
443
444 /*
445 * Try set the breakpoint.
446 */
447 RTUINT iBp;
448 rc = DBGFR3BpSetReg(pVM, &Address, iHitTrigger, iHitDisable, fType, cb, &iBp);
449 if (VBOX_SUCCESS(rc))
450 {
451 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
452 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
453 if (VBOX_SUCCESS(rc))
454 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Set access breakpoint %u at %VGv\n", iBp, Address.FlatPtr);
455 if (rc == VERR_DBGC_BP_EXISTS)
456 {
457 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
458 if (VBOX_SUCCESS(rc))
459 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Updated access breakpoint %u at %VGv\n", iBp, Address.FlatPtr);
460 }
461 int rc2 = DBGFR3BpClear(pDbgc->pVM, iBp);
462 AssertRC(rc2);
463 }
464 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Failed to set access breakpoint at %VGv, rc=%Vrc.\n", Address.FlatPtr, rc);
465}
466
467
468/**
469 * The 'bc' command.
470 *
471 * @returns VBox status.
472 * @param pCmd Pointer to the command descriptor (as registered).
473 * @param pCmdHlp Pointer to command helper functions.
474 * @param pVM Pointer to the current VM (if any).
475 * @param paArgs Pointer to (readonly) array of arguments.
476 * @param cArgs Number of arguments in the array.
477 */
478static DECLCALLBACK(int) dbgcCmdBrkClear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR /*pResult*/)
479{
480 /*
481 * Enumerate the arguments.
482 */
483 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
484 int rc = VINF_SUCCESS;
485 for (unsigned iArg = 0; iArg < cArgs && VBOX_SUCCESS(rc); iArg++)
486 {
487 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
488 {
489 /* one */
490 RTUINT iBp = (RTUINT)paArgs[iArg].u.u64Number;
491 if (iBp != paArgs[iArg].u.u64Number)
492 {
493 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Breakpoint id %RX64 is too large!\n", paArgs[iArg].u.u64Number);
494 break;
495 }
496 int rc2 = DBGFR3BpClear(pVM, iBp);
497 if (VBOX_FAILURE(rc2))
498 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc2, "DBGFR3BpClear failed for breakpoint %u!\n", iBp);
499 if (VBOX_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
500 dbgcBpDelete(pDbgc, iBp);
501 }
502 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
503 {
504 /* all */
505 PDBGCBP pBp = pDbgc->pFirstBp;
506 while (pBp)
507 {
508 RTUINT iBp = pBp->iBp;
509 pBp = pBp->pNext;
510
511 int rc2 = DBGFR3BpClear(pVM, iBp);
512 if (VBOX_FAILURE(rc2))
513 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc2, "DBGFR3BpClear failed for breakpoint %u!\n", iBp);
514 if (VBOX_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
515 dbgcBpDelete(pDbgc, iBp);
516 }
517 }
518 else
519 {
520 /* invalid parameter */
521 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid argument '%s' to '%s'!\n", paArgs[iArg].u.pszString, pCmd->pszCmd);
522 break;
523 }
524 }
525 return rc;
526}
527
528
529/**
530 * The 'bd' command.
531 *
532 * @returns VBox status.
533 * @param pCmd Pointer to the command descriptor (as registered).
534 * @param pCmdHlp Pointer to command helper functions.
535 * @param pVM Pointer to the current VM (if any).
536 * @param paArgs Pointer to (readonly) array of arguments.
537 * @param cArgs Number of arguments in the array.
538 */
539static DECLCALLBACK(int) dbgcCmdBrkDisable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR /*pResult*/)
540{
541 /*
542 * Enumerate the arguments.
543 */
544 int rc = VINF_SUCCESS;
545 for (unsigned iArg = 0; iArg < cArgs && VBOX_SUCCESS(rc); iArg++)
546 {
547 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
548 {
549 /* one */
550 RTUINT iBp = (RTUINT)paArgs[iArg].u.u64Number;
551 if (iBp != paArgs[iArg].u.u64Number)
552 {
553 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Breakpoint id %RX64 is too large!\n", paArgs[iArg].u.u64Number);
554 break;
555 }
556 rc = DBGFR3BpDisable(pVM, iBp);
557 if (VBOX_FAILURE(rc))
558 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3BpDisable failed for breakpoint %u!\n", iBp);
559 }
560 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
561 {
562 /* all */
563 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
564 for (PDBGCBP pBp = pDbgc->pFirstBp; pBp; pBp = pBp->pNext)
565 {
566 rc = DBGFR3BpDisable(pVM, pBp->iBp);
567 if (VBOX_FAILURE(rc))
568 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3BpDisable failed for breakpoint %u!\n", pBp->iBp);
569 }
570 }
571 else
572 {
573 /* invalid parameter */
574 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid argument '%s' to '%s'!\n", paArgs[iArg].u.pszString, pCmd->pszCmd);
575 break;
576 }
577 }
578 return rc;
579}
580
581
582/**
583 * The 'be' command.
584 *
585 * @returns VBox status.
586 * @param pCmd Pointer to the command descriptor (as registered).
587 * @param pCmdHlp Pointer to command helper functions.
588 * @param pVM Pointer to the current VM (if any).
589 * @param paArgs Pointer to (readonly) array of arguments.
590 * @param cArgs Number of arguments in the array.
591 */
592static DECLCALLBACK(int) dbgcCmdBrkEnable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR /*pResult*/)
593{
594 /*
595 * Enumerate the arguments.
596 */
597 int rc = VINF_SUCCESS;
598 for (unsigned iArg = 0; iArg < cArgs && VBOX_SUCCESS(rc); iArg++)
599 {
600 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
601 {
602 /* one */
603 RTUINT iBp = (RTUINT)paArgs[iArg].u.u64Number;
604 if (iBp != paArgs[iArg].u.u64Number)
605 {
606 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Breakpoint id %RX64 is too large!\n", paArgs[iArg].u.u64Number);
607 break;
608 }
609 rc = DBGFR3BpEnable(pVM, iBp);
610 if (VBOX_FAILURE(rc))
611 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3BpEnable failed for breakpoint %u!\n", iBp);
612 }
613 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
614 {
615 /* all */
616 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
617 for (PDBGCBP pBp = pDbgc->pFirstBp; pBp; pBp = pBp->pNext)
618 {
619 rc = DBGFR3BpEnable(pVM, pBp->iBp);
620 if (VBOX_FAILURE(rc))
621 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3BpEnable failed for breakpoint %u!\n", pBp->iBp);
622 }
623 }
624 else
625 {
626 /* invalid parameter */
627 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid argument '%s' to '%s'!\n", paArgs[iArg].u.pszString, pCmd->pszCmd);
628 break;
629 }
630 }
631 return rc;
632}
633
634
635/**
636 * Breakpoint enumeration callback function.
637 *
638 * @returns VBox status code. Any failure will stop the enumeration.
639 * @param pVM The VM handle.
640 * @param pvUser The user argument.
641 * @param pBp Pointer to the breakpoint information. (readonly)
642 */
643static DECLCALLBACK(int) dbgcEnumBreakpointsCallback(PVM pVM, void *pvUser, PCDBGFBP pBp)
644{
645 PDBGC pDbgc = (PDBGC)pvUser;
646 PDBGCBP pDbgcBp = dbgcBpGet(pDbgc, pBp->iBp);
647
648 /*
649 * BP type and size.
650 */
651 char chType;
652 char cb = 1;
653 switch (pBp->enmType)
654 {
655 case DBGFBPTYPE_INT3:
656 chType = 'p';
657 break;
658 case DBGFBPTYPE_REG:
659 switch (pBp->u.Reg.fType)
660 {
661 case X86_DR7_RW_EO: chType = 'x'; break;
662 case X86_DR7_RW_WO: chType = 'w'; break;
663 case X86_DR7_RW_IO: chType = 'i'; break;
664 case X86_DR7_RW_RW: chType = 'r'; break;
665 default: chType = '?'; break;
666
667 }
668 cb = pBp->u.Reg.cb;
669 break;
670 case DBGFBPTYPE_REM:
671 chType = 'r';
672 break;
673 default:
674 chType = '?';
675 break;
676 }
677
678 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%2u %c %d %c %VGv %04RX64 (%04RX64 to ",
679 pBp->iBp, pBp->fEnabled ? 'e' : 'd', cb, chType,
680 pBp->GCPtr, pBp->cHits, pBp->iHitTrigger);
681 if (pBp->iHitDisable == ~(uint64_t)0)
682 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "~0) ");
683 else
684 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%04RX64)");
685
686 /*
687 * Try resolve the address.
688 */
689 DBGFSYMBOL Sym;
690 RTGCINTPTR off;
691 int rc = DBGFR3SymbolByAddr(pVM, pBp->GCPtr, &off, &Sym);
692 if (VBOX_SUCCESS(rc))
693 {
694 if (!off)
695 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s", Sym.szName);
696 else if (off > 0)
697 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s+%VGv", Sym.szName, off);
698 else
699 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s+%VGv", Sym.szName, -off);
700 }
701
702 /*
703 * The commands.
704 */
705 if (pDbgcBp)
706 {
707 if (pDbgcBp->cchCmd)
708 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n cmds: '%s'\n",
709 pDbgcBp->szCmd);
710 else
711 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n");
712 }
713 else
714 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " [unknown bp]\n");
715
716 return VINF_SUCCESS;
717}
718
719
720/**
721 * The 'bl' command.
722 *
723 * @returns VBox status.
724 * @param pCmd Pointer to the command descriptor (as registered).
725 * @param pCmdHlp Pointer to command helper functions.
726 * @param pVM Pointer to the current VM (if any).
727 * @param paArgs Pointer to (readonly) array of arguments.
728 * @param cArgs Number of arguments in the array.
729 */
730static DECLCALLBACK(int) dbgcCmdBrkList(PCDBGCCMD /*pCmd*/, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR /*paArgs*/, unsigned /*cArgs*/, PDBGCVAR /*pResult*/)
731{
732 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
733
734 /*
735 * Enumerate the breakpoints.
736 */
737 int rc = DBGFR3BpEnum(pVM, dbgcEnumBreakpointsCallback, pDbgc);
738 if (VBOX_FAILURE(rc))
739 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3BpEnum failed.\n");
740 return rc;
741}
742
743
744/**
745 * The 'bp' command.
746 *
747 * @returns VBox status.
748 * @param pCmd Pointer to the command descriptor (as registered).
749 * @param pCmdHlp Pointer to command helper functions.
750 * @param pVM Pointer to the current VM (if any).
751 * @param paArgs Pointer to (readonly) array of arguments.
752 * @param cArgs Number of arguments in the array.
753 */
754static DECLCALLBACK(int) dbgcCmdBrkSet(PCDBGCCMD /*pCmd*/, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR /*pResult*/)
755{
756 /*
757 * Convert the pointer to a DBGF address.
758 */
759 DBGFADDRESS Address;
760 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
761 if (VBOX_FAILURE(rc))
762 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Couldn't convert '%DV' to a DBGF address, rc=%Vrc.\n", &paArgs[0], rc);
763
764 /*
765 * Pick out the optional arguments.
766 */
767 uint64_t iHitTrigger = 0;
768 uint64_t iHitDisable = ~0;
769 const char *pszCmds = NULL;
770 unsigned iArg = 1;
771 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
772 {
773 iHitTrigger = paArgs[iArg].u.u64Number;
774 iArg++;
775 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
776 {
777 iHitDisable = paArgs[iArg].u.u64Number;
778 iArg++;
779 }
780 }
781 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
782 {
783 pszCmds = paArgs[iArg].u.pszString;
784 iArg++;
785 }
786
787 /*
788 * Try set the breakpoint.
789 */
790 RTUINT iBp;
791 rc = DBGFR3BpSet(pVM, &Address, iHitTrigger, iHitDisable, &iBp);
792 if (VBOX_SUCCESS(rc))
793 {
794 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
795 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
796 if (VBOX_SUCCESS(rc))
797 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Set breakpoint %u at %VGv\n", iBp, Address.FlatPtr);
798 if (rc == VERR_DBGC_BP_EXISTS)
799 {
800 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
801 if (VBOX_SUCCESS(rc))
802 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Updated breakpoint %u at %VGv\n", iBp, Address.FlatPtr);
803 }
804 int rc2 = DBGFR3BpClear(pDbgc->pVM, iBp);
805 AssertRC(rc2);
806 }
807 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Failed to set breakpoint at %VGv, rc=%Vrc.\n", Address.FlatPtr, rc);
808}
809
810
811/**
812 * The 'br' command.
813 *
814 * @returns VBox status.
815 * @param pCmd Pointer to the command descriptor (as registered).
816 * @param pCmdHlp Pointer to command helper functions.
817 * @param pVM Pointer to the current VM (if any).
818 * @param paArgs Pointer to (readonly) array of arguments.
819 * @param cArgs Number of arguments in the array.
820 */
821static DECLCALLBACK(int) dbgcCmdBrkREM(PCDBGCCMD /*pCmd*/, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR /*pResult*/)
822{
823 /*
824 * Convert the pointer to a DBGF address.
825 */
826 DBGFADDRESS Address;
827 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
828 if (VBOX_FAILURE(rc))
829 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Couldn't convert '%DV' to a DBGF address, rc=%Vrc.\n", &paArgs[0], rc);
830
831 /*
832 * Pick out the optional arguments.
833 */
834 uint64_t iHitTrigger = 0;
835 uint64_t iHitDisable = ~0;
836 const char *pszCmds = NULL;
837 unsigned iArg = 1;
838 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
839 {
840 iHitTrigger = paArgs[iArg].u.u64Number;
841 iArg++;
842 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
843 {
844 iHitDisable = paArgs[iArg].u.u64Number;
845 iArg++;
846 }
847 }
848 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
849 {
850 pszCmds = paArgs[iArg].u.pszString;
851 iArg++;
852 }
853
854 /*
855 * Try set the breakpoint.
856 */
857 RTUINT iBp;
858 rc = DBGFR3BpSetREM(pVM, &Address, iHitTrigger, iHitDisable, &iBp);
859 if (VBOX_SUCCESS(rc))
860 {
861 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
862 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
863 if (VBOX_SUCCESS(rc))
864 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Set REM breakpoint %u at %VGv\n", iBp, Address.FlatPtr);
865 if (rc == VERR_DBGC_BP_EXISTS)
866 {
867 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
868 if (VBOX_SUCCESS(rc))
869 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Updated REM breakpoint %u at %VGv\n", iBp, Address.FlatPtr);
870 }
871 int rc2 = DBGFR3BpClear(pDbgc->pVM, iBp);
872 AssertRC(rc2);
873 }
874 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Failed to set REM breakpoint at %VGv, rc=%Vrc.\n", Address.FlatPtr, rc);
875}
876
877
878/**
879 * The 'u' command.
880 *
881 * @returns VBox status.
882 * @param pCmd Pointer to the command descriptor (as registered).
883 * @param pCmdHlp Pointer to command helper functions.
884 * @param pVM Pointer to the current VM (if any).
885 * @param paArgs Pointer to (readonly) array of arguments.
886 * @param cArgs Number of arguments in the array.
887 */
888static DECLCALLBACK(int) dbgcCmdUnassemble(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
889{
890 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
891
892 /*
893 * Validate input.
894 */
895 if ( cArgs > 1
896 || (cArgs == 1 && !DBGCVAR_ISPOINTER(paArgs[0].enmType)))
897 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: The parser doesn't do its job properly yet.. It might help to use the '%%' operator.\n");
898 if (!pVM && !cArgs && !DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
899 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Don't know where to start disassembling...\n");
900 if (!pVM && cArgs && DBGCVAR_ISGCPOINTER(paArgs[0].enmType))
901 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: GC address but no VM.\n");
902
903 /*
904 * Find address.
905 */
906 unsigned fFlags = DBGF_DISAS_FLAGS_NO_ADDRESS;
907 if (!cArgs)
908 {
909 if (!DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
910 {
911 PCPUMCTX pCtx;
912 int rc = CPUMQueryGuestCtxPtr(pVM, &pCtx);
913 AssertRC(rc);
914
915 if ( pDbgc->fRegCtxGuest
916 && CPUMIsGuestIn64BitCodeEx(pCtx))
917 {
918 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FLAT;
919 pDbgc->SourcePos.u.GCFlat = CPUMGetGuestRIP(pVM);
920 }
921 else
922 {
923 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FAR;
924 pDbgc->SourcePos.u.GCFar.off = pDbgc->fRegCtxGuest ? CPUMGetGuestEIP(pVM) : CPUMGetHyperEIP(pVM);
925 pDbgc->SourcePos.u.GCFar.sel = pDbgc->fRegCtxGuest ? CPUMGetGuestCS(pVM) : CPUMGetHyperCS(pVM);
926 }
927
928 if (pDbgc->fRegCtxGuest)
929 fFlags |= DBGF_DISAS_FLAGS_CURRENT_GUEST;
930 else
931 fFlags |= DBGF_DISAS_FLAGS_CURRENT_HYPER;
932 }
933 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_NONE;
934 }
935 else
936 pDbgc->DisasmPos = paArgs[0];
937
938 /*
939 * Range.
940 */
941 switch (pDbgc->DisasmPos.enmRangeType)
942 {
943 case DBGCVAR_RANGE_NONE:
944 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
945 pDbgc->DisasmPos.u64Range = 10;
946 break;
947
948 case DBGCVAR_RANGE_ELEMENTS:
949 if (pDbgc->DisasmPos.u64Range > 2048)
950 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Too many lines requested. Max is 2048 lines.\n");
951 break;
952
953 case DBGCVAR_RANGE_BYTES:
954 if (pDbgc->DisasmPos.u64Range > 65536)
955 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The requested range is too big. Max is 64KB.\n");
956 break;
957
958 default:
959 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: Unknown range type %d.\n", pDbgc->DisasmPos.enmRangeType);
960 }
961
962 /*
963 * Convert physical and host addresses to guest addresses.
964 */
965 int rc;
966 switch (pDbgc->DisasmPos.enmType)
967 {
968 case DBGCVAR_TYPE_GC_FLAT:
969 case DBGCVAR_TYPE_GC_FAR:
970 break;
971 case DBGCVAR_TYPE_GC_PHYS:
972 case DBGCVAR_TYPE_HC_FLAT:
973 case DBGCVAR_TYPE_HC_PHYS:
974 case DBGCVAR_TYPE_HC_FAR:
975 {
976 DBGCVAR VarTmp;
977 rc = pCmdHlp->pfnEval(pCmdHlp, &VarTmp, "%%(%Dv)", &pDbgc->DisasmPos);
978 if (VBOX_FAILURE(rc))
979 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: failed to evaluate '%%(%Dv)' -> %Vrc .\n", &pDbgc->DisasmPos, rc);
980 pDbgc->DisasmPos = VarTmp;
981 break;
982 }
983 default: AssertFailed(); break;
984 }
985
986 /*
987 * Print address.
988 * todo: Change to list near.
989 */
990#if 0
991 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%DV:\n", &pDbgc->DisasmPos);
992 if (VBOX_FAILURE(rc))
993 return rc;
994#endif
995
996 /*
997 * Do the disassembling.
998 */
999 unsigned cTries = 32;
1000 int iRangeLeft = (int)pDbgc->DisasmPos.u64Range;
1001 if (iRangeLeft == 0) /* klugde for 'r'. */
1002 iRangeLeft = -1;
1003 for (;;)
1004 {
1005 /*
1006 * Disassemble the instruction.
1007 */
1008 char szDis[256];
1009 uint32_t cbInstr = 1;
1010 if (pDbgc->DisasmPos.enmType == DBGCVAR_TYPE_GC_FLAT)
1011 rc = DBGFR3DisasInstrEx(pVM, DBGF_SEL_FLAT, pDbgc->DisasmPos.u.GCFlat, fFlags, &szDis[0], sizeof(szDis), &cbInstr);
1012 else
1013 rc = DBGFR3DisasInstrEx(pVM, pDbgc->DisasmPos.u.GCFar.sel, pDbgc->DisasmPos.u.GCFar.off, fFlags, &szDis[0], sizeof(szDis), &cbInstr);
1014 if (VBOX_SUCCESS(rc))
1015 {
1016 /* print it */
1017 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%-16DV %s\n", &pDbgc->DisasmPos, &szDis[0]);
1018 if (VBOX_FAILURE(rc))
1019 return rc;
1020 }
1021 else
1022 {
1023 /* bitch. */
1024 int rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Failed to disassemble instruction, skipping one byte.\n");
1025 if (VBOX_FAILURE(rc))
1026 return rc;
1027 if (cTries-- > 0)
1028 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Too many disassembly failures. Giving up.\n");
1029 cbInstr = 1;
1030 }
1031
1032 /* advance */
1033 if (iRangeLeft < 0) /* 'r' */
1034 break;
1035 if (pDbgc->DisasmPos.enmRangeType == DBGCVAR_RANGE_ELEMENTS)
1036 iRangeLeft--;
1037 else
1038 iRangeLeft -= cbInstr;
1039 rc = pCmdHlp->pfnEval(pCmdHlp, &pDbgc->DisasmPos, "(%Dv) + %x", &pDbgc->DisasmPos, cbInstr);
1040 if (VBOX_FAILURE(rc))
1041 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->DisasmPos, cbInstr);
1042 if (iRangeLeft <= 0)
1043 break;
1044 fFlags &= ~(DBGF_DISAS_FLAGS_CURRENT_GUEST | DBGF_DISAS_FLAGS_CURRENT_HYPER);
1045 }
1046
1047 NOREF(pCmd); NOREF(pResult);
1048 return 0;
1049}
1050
1051
1052/**
1053 * The 'ls' command.
1054 *
1055 * @returns VBox status.
1056 * @param pCmd Pointer to the command descriptor (as registered).
1057 * @param pCmdHlp Pointer to command helper functions.
1058 * @param pVM Pointer to the current VM (if any).
1059 * @param paArgs Pointer to (readonly) array of arguments.
1060 * @param cArgs Number of arguments in the array.
1061 */
1062static DECLCALLBACK(int) dbgcCmdListSource(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1063{
1064 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1065
1066 /*
1067 * Validate input.
1068 */
1069 if ( cArgs > 1
1070 || (cArgs == 1 && !DBGCVAR_ISPOINTER(paArgs[0].enmType)))
1071 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: The parser doesn't do its job properly yet.. It might help to use the '%%' operator.\n");
1072 if (!pVM && !cArgs && !DBGCVAR_ISPOINTER(pDbgc->SourcePos.enmType))
1073 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Don't know where to start disassembling...\n");
1074 if (!pVM && cArgs && DBGCVAR_ISGCPOINTER(paArgs[0].enmType))
1075 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: GC address but no VM.\n");
1076
1077 /*
1078 * Find address.
1079 */
1080 if (!cArgs)
1081 {
1082 if (!DBGCVAR_ISPOINTER(pDbgc->SourcePos.enmType))
1083 {
1084 pDbgc->SourcePos.enmType = DBGCVAR_TYPE_GC_FAR;
1085 pDbgc->SourcePos.u.GCFar.off = pDbgc->fRegCtxGuest ? CPUMGetGuestEIP(pVM) : CPUMGetHyperEIP(pVM);
1086 pDbgc->SourcePos.u.GCFar.sel = pDbgc->fRegCtxGuest ? CPUMGetGuestCS(pVM) : CPUMGetHyperCS(pVM);
1087 }
1088 pDbgc->SourcePos.enmRangeType = DBGCVAR_RANGE_NONE;
1089 }
1090 else
1091 pDbgc->SourcePos = paArgs[0];
1092
1093 /*
1094 * Ensure the the source address is flat GC.
1095 */
1096 switch (pDbgc->SourcePos.enmType)
1097 {
1098 case DBGCVAR_TYPE_GC_FLAT:
1099 break;
1100 case DBGCVAR_TYPE_GC_PHYS:
1101 case DBGCVAR_TYPE_GC_FAR:
1102 case DBGCVAR_TYPE_HC_FLAT:
1103 case DBGCVAR_TYPE_HC_PHYS:
1104 case DBGCVAR_TYPE_HC_FAR:
1105 {
1106 int rc = pCmdHlp->pfnEval(pCmdHlp, &pDbgc->SourcePos, "%%(%Dv)", &pDbgc->SourcePos);
1107 if (VBOX_FAILURE(rc))
1108 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid address or address type. (rc=%d)\n", rc);
1109 break;
1110 }
1111 default: AssertFailed(); break;
1112 }
1113
1114 /*
1115 * Range.
1116 */
1117 switch (pDbgc->SourcePos.enmRangeType)
1118 {
1119 case DBGCVAR_RANGE_NONE:
1120 pDbgc->SourcePos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
1121 pDbgc->SourcePos.u64Range = 10;
1122 break;
1123
1124 case DBGCVAR_RANGE_ELEMENTS:
1125 if (pDbgc->SourcePos.u64Range > 2048)
1126 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Too many lines requested. Max is 2048 lines.\n");
1127 break;
1128
1129 case DBGCVAR_RANGE_BYTES:
1130 if (pDbgc->SourcePos.u64Range > 65536)
1131 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The requested range is too big. Max is 64KB.\n");
1132 break;
1133
1134 default:
1135 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: Unknown range type %d.\n", pDbgc->SourcePos.enmRangeType);
1136 }
1137
1138 /*
1139 * Do the disassembling.
1140 */
1141 bool fFirst = 1;
1142 DBGFLINE LinePrev = { 0, 0, "" };
1143 int iRangeLeft = (int)pDbgc->SourcePos.u64Range;
1144 if (iRangeLeft == 0) /* klugde for 'r'. */
1145 iRangeLeft = -1;
1146 for (;;)
1147 {
1148 /*
1149 * Get line info.
1150 */
1151 DBGFLINE Line;
1152 RTGCINTPTR off;
1153 int rc = DBGFR3LineByAddr(pVM, pDbgc->SourcePos.u.GCFlat, &off, &Line);
1154 if (VBOX_FAILURE(rc))
1155 return VINF_SUCCESS;
1156
1157 unsigned cLines = 0;
1158 if (memcmp(&Line, &LinePrev, sizeof(Line)))
1159 {
1160 /*
1161 * Print filenamename
1162 */
1163 if (!fFirst && strcmp(Line.szFilename, LinePrev.szFilename))
1164 fFirst = true;
1165 if (fFirst)
1166 {
1167 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "[%s @ %d]\n", Line.szFilename, Line.uLineNo);
1168 if (VBOX_FAILURE(rc))
1169 return rc;
1170 }
1171
1172 /*
1173 * Try open the file and read the line.
1174 */
1175 FILE *phFile = fopen(Line.szFilename, "r");
1176 if (phFile)
1177 {
1178 /* Skip ahead to the desired line. */
1179 char szLine[4096];
1180 unsigned cBefore = fFirst ? RT_MIN(2, Line.uLineNo - 1) : Line.uLineNo - LinePrev.uLineNo - 1;
1181 if (cBefore > 7)
1182 cBefore = 0;
1183 unsigned cLeft = Line.uLineNo - cBefore;
1184 while (cLeft > 0)
1185 {
1186 szLine[0] = '\0';
1187 if (!fgets(szLine, sizeof(szLine), phFile))
1188 break;
1189 cLeft--;
1190 }
1191 if (!cLeft)
1192 {
1193 /* print the before lines */
1194 for (;;)
1195 {
1196 size_t cch = strlen(szLine);
1197 while (cch > 0 && (szLine[cch - 1] == '\r' || szLine[cch - 1] == '\n' || isspace(szLine[cch - 1])) )
1198 szLine[--cch] = '\0';
1199 if (cBefore-- <= 0)
1200 break;
1201
1202 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " %4d: %s\n", Line.uLineNo - cBefore - 1, szLine);
1203 szLine[0] = '\0';
1204 fgets(szLine, sizeof(szLine), phFile);
1205 cLines++;
1206 }
1207 /* print the actual line */
1208 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%08llx %4d: %s\n", Line.Address, Line.uLineNo, szLine);
1209 }
1210 fclose(phFile);
1211 if (VBOX_FAILURE(rc))
1212 return rc;
1213 fFirst = false;
1214 }
1215 else
1216 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Warning: couldn't open source file '%s'\n", Line.szFilename);
1217
1218 LinePrev = Line;
1219 }
1220
1221
1222 /*
1223 * Advance
1224 */
1225 if (iRangeLeft < 0) /* 'r' */
1226 break;
1227 if (pDbgc->SourcePos.enmRangeType == DBGCVAR_RANGE_ELEMENTS)
1228 iRangeLeft -= cLines;
1229 else
1230 iRangeLeft -= 1;
1231 rc = pCmdHlp->pfnEval(pCmdHlp, &pDbgc->SourcePos, "(%Dv) + %x", &pDbgc->SourcePos, 1);
1232 if (VBOX_FAILURE(rc))
1233 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->SourcePos, 1);
1234 if (iRangeLeft <= 0)
1235 break;
1236 }
1237
1238 NOREF(pCmd); NOREF(pResult);
1239 return 0;
1240}
1241
1242
1243/**
1244 * The 'r' command.
1245 *
1246 * @returns VBox status.
1247 * @param pCmd Pointer to the command descriptor (as registered).
1248 * @param pCmdHlp Pointer to command helper functions.
1249 * @param pVM Pointer to the current VM (if any).
1250 * @param paArgs Pointer to (readonly) array of arguments.
1251 * @param cArgs Number of arguments in the array.
1252 */
1253static DECLCALLBACK(int) dbgcCmdReg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1254{
1255 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1256
1257 if (pDbgc->fRegCtxGuest)
1258 return dbgcCmdRegGuest(pCmd, pCmdHlp, pVM, paArgs, cArgs, pResult);
1259 else
1260 return dbgcCmdRegHyper(pCmd, pCmdHlp, pVM, paArgs, cArgs, pResult);
1261}
1262
1263
1264/**
1265 * Common worker for the dbgcCmdReg*() commands.
1266 *
1267 * @returns VBox status.
1268 * @param pCmd Pointer to the command descriptor (as registered).
1269 * @param pCmdHlp Pointer to command helper functions.
1270 * @param pVM Pointer to the current VM (if any).
1271 * @param paArgs Pointer to (readonly) array of arguments.
1272 * @param cArgs Number of arguments in the array.
1273 * @param pszPrefix The symbol prefix.
1274 */
1275static DECLCALLBACK(int) dbgcCmdRegCommon(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult, const char *pszPrefix)
1276{
1277 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1278
1279 /*
1280 * cArgs == 0: Show all
1281 */
1282 if (cArgs == 0)
1283 {
1284 /*
1285 * Get register context.
1286 */
1287 int rc;
1288 PCPUMCTX pCtx;
1289 PCCPUMCTXCORE pCtxCore;
1290 if (!*pszPrefix)
1291 {
1292 rc = CPUMQueryGuestCtxPtr(pVM, &pCtx);
1293 pCtxCore = CPUMGetGuestCtxCore(pVM);
1294 }
1295 else
1296 {
1297 rc = CPUMQueryHyperCtxPtr(pVM, &pCtx);
1298 pCtxCore = CPUMGetHyperCtxCore(pVM);
1299 }
1300 if (VBOX_FAILURE(rc))
1301 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Getting register context\n");
1302
1303 /*
1304 * Format the flags.
1305 */
1306 static struct
1307 {
1308 const char *pszSet; const char *pszClear; uint32_t fFlag;
1309 } aFlags[] =
1310 {
1311 { "vip",NULL, X86_EFL_VIP },
1312 { "vif",NULL, X86_EFL_VIF },
1313 { "ac", NULL, X86_EFL_AC },
1314 { "vm", NULL, X86_EFL_VM },
1315 { "rf", NULL, X86_EFL_RF },
1316 { "nt", NULL, X86_EFL_NT },
1317 { "ov", "nv", X86_EFL_OF },
1318 { "dn", "up", X86_EFL_DF },
1319 { "ei", "di", X86_EFL_IF },
1320 { "tf", NULL, X86_EFL_TF },
1321 { "ng", "pl", X86_EFL_SF },
1322 { "zr", "nz", X86_EFL_ZF },
1323 { "ac", "na", X86_EFL_AF },
1324 { "po", "pe", X86_EFL_PF },
1325 { "cy", "nc", X86_EFL_CF },
1326 };
1327 char szEFlags[80];
1328 char *psz = szEFlags;
1329 uint32_t efl = pCtxCore->eflags.u32;
1330 for (unsigned i = 0; i < ELEMENTS(aFlags); i++)
1331 {
1332 const char *pszAdd = aFlags[i].fFlag & efl ? aFlags[i].pszSet : aFlags[i].pszClear;
1333 if (pszAdd)
1334 {
1335 strcpy(psz, pszAdd);
1336 psz += strlen(pszAdd);
1337 *psz++ = ' ';
1338 }
1339 }
1340 psz[-1] = '\0';
1341
1342
1343 /*
1344 * Format the registers.
1345 */
1346 if (pDbgc->fRegTerse)
1347 {
1348 if (CPUMIsGuestIn64BitCodeEx(pCtx))
1349 {
1350 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1351 "%srax=%016RX64 %srbx=%016RX64 %srcx=%016RX64 %srdx=%016RX64\n"
1352 "%srsi=%016RX64 %srdi=%016RX64 %sr8 =%016RX64 %sr9 =%016RX64\n"
1353 "%sr10=%016RX64 %sr11=%016RX64 %sr12=%016RX64 %sr13=%016RX64\n"
1354 "%sr14=%016RX64 %sr15=%016RX64\n"
1355 "%srip=%016RX64 %srsp=%016RX64 %srbp=%016RX64 %siopl=%d %*s\n"
1356 "%scs=%04x %sds=%04x %ses=%04x %sfs=%04x %sgs=%04x %sss=%04x %seflags=%08x\n",
1357 pszPrefix, pCtxCore->rax, pszPrefix, pCtxCore->rbx, pszPrefix, pCtxCore->rcx, pszPrefix, pCtxCore->rdx, pszPrefix, pCtxCore->rsi, pszPrefix, pCtxCore->rdi,
1358 pszPrefix, pCtxCore->r8, pszPrefix, pCtxCore->r9, pszPrefix, pCtxCore->r10, pszPrefix, pCtxCore->r11, pszPrefix, pCtxCore->r12, pszPrefix, pCtxCore->r13,
1359 pszPrefix, pCtxCore->r14, pszPrefix, pCtxCore->r15,
1360 pszPrefix, pCtxCore->rip, pszPrefix, pCtxCore->rsp, pszPrefix, pCtxCore->rbp, pszPrefix, X86_EFL_GET_IOPL(efl), *pszPrefix ? 34 : 31, szEFlags,
1361 pszPrefix, (RTSEL)pCtxCore->cs, pszPrefix, (RTSEL)pCtxCore->ds, pszPrefix, (RTSEL)pCtxCore->es,
1362 pszPrefix, (RTSEL)pCtxCore->fs, pszPrefix, (RTSEL)pCtxCore->gs, pszPrefix, (RTSEL)pCtxCore->ss, pszPrefix, efl);
1363 }
1364 else
1365 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1366 "%seax=%08x %sebx=%08x %secx=%08x %sedx=%08x %sesi=%08x %sedi=%08x\n"
1367 "%seip=%08x %sesp=%08x %sebp=%08x %siopl=%d %*s\n"
1368 "%scs=%04x %sds=%04x %ses=%04x %sfs=%04x %sgs=%04x %sss=%04x %seflags=%08x\n",
1369 pszPrefix, pCtxCore->eax, pszPrefix, pCtxCore->ebx, pszPrefix, pCtxCore->ecx, pszPrefix, pCtxCore->edx, pszPrefix, pCtxCore->esi, pszPrefix, pCtxCore->edi,
1370 pszPrefix, pCtxCore->eip, pszPrefix, pCtxCore->esp, pszPrefix, pCtxCore->ebp, pszPrefix, X86_EFL_GET_IOPL(efl), *pszPrefix ? 34 : 31, szEFlags,
1371 pszPrefix, (RTSEL)pCtxCore->cs, pszPrefix, (RTSEL)pCtxCore->ds, pszPrefix, (RTSEL)pCtxCore->es,
1372 pszPrefix, (RTSEL)pCtxCore->fs, pszPrefix, (RTSEL)pCtxCore->gs, pszPrefix, (RTSEL)pCtxCore->ss, pszPrefix, efl);
1373 }
1374 else
1375 {
1376 if (CPUMIsGuestIn64BitCodeEx(pCtx))
1377 {
1378 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1379 "%srax=%016RX64 %srbx=%016RX64 %srcx=%016RX64 %srdx=%016RX64\n"
1380 "%srsi=%016RX64 %srdi=%016RX64 %sr8 =%016RX64 %sr9 =%016RX64\n"
1381 "%sr10=%016RX64 %sr11=%016RX64 %sr12=%016RX64 %sr13=%016RX64\n"
1382 "%sr14=%016RX64 %sr15=%016RX64\n"
1383 "%srip=%016RX64 %srsp=%016RX64 %srbp=%016RX64 %siopl=%d %*s\n"
1384 "%scs={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1385 "%sds={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1386 "%ses={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1387 "%sfs={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1388 "%sgs={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1389 "%sss={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1390 "%scr0=%016RX64 %scr2=%016RX64 %scr3=%016RX64 %scr4=%016RX64\n"
1391 "%sdr0=%016RX64 %sdr1=%016RX64 %sdr2=%016RX64 %sdr3=%016RX64\n"
1392 "%sdr4=%016RX64 %sdr5=%016RX64 %sdr6=%016RX64 %sdr7=%016RX64\n"
1393 "%sgdtr=%016RX64:%04x %sidtr=%016RX64:%04x %seflags=%08x\n"
1394 "%sldtr={%04x base=%08RX64 limit=%08x flags=%08x}\n"
1395 "%str ={%04x base=%08RX64 limit=%08x flags=%08x}\n"
1396 "%sSysEnter={cs=%04llx eip=%08llx esp=%08llx}\n"
1397 ,
1398 pszPrefix, pCtxCore->rax, pszPrefix, pCtxCore->rbx, pszPrefix, pCtxCore->rcx, pszPrefix, pCtxCore->rdx, pszPrefix, pCtxCore->rsi, pszPrefix, pCtxCore->rdi,
1399 pszPrefix, pCtxCore->r8, pszPrefix, pCtxCore->r9, pszPrefix, pCtxCore->r10, pszPrefix, pCtxCore->r11, pszPrefix, pCtxCore->r12, pszPrefix, pCtxCore->r13,
1400 pszPrefix, pCtxCore->r14, pszPrefix, pCtxCore->r15,
1401 pszPrefix, pCtxCore->rip, pszPrefix, pCtxCore->rsp, pszPrefix, pCtxCore->rbp, pszPrefix, X86_EFL_GET_IOPL(efl), *pszPrefix ? 33 : 31, szEFlags,
1402 pszPrefix, (RTSEL)pCtxCore->cs, pCtx->csHid.u64Base, pCtx->csHid.u32Limit, pCtx->csHid.Attr.u,
1403 pszPrefix, (RTSEL)pCtxCore->ds, pCtx->dsHid.u64Base, pCtx->dsHid.u32Limit, pCtx->dsHid.Attr.u,
1404 pszPrefix, (RTSEL)pCtxCore->es, pCtx->esHid.u64Base, pCtx->esHid.u32Limit, pCtx->esHid.Attr.u,
1405 pszPrefix, (RTSEL)pCtxCore->fs, pCtx->fsHid.u64Base, pCtx->fsHid.u32Limit, pCtx->fsHid.Attr.u,
1406 pszPrefix, (RTSEL)pCtxCore->gs, pCtx->gsHid.u64Base, pCtx->gsHid.u32Limit, pCtx->gsHid.Attr.u,
1407 pszPrefix, (RTSEL)pCtxCore->ss, pCtx->ssHid.u64Base, pCtx->ssHid.u32Limit, pCtx->ssHid.Attr.u,
1408 pszPrefix, pCtx->cr0, pszPrefix, pCtx->cr2, pszPrefix, pCtx->cr3, pszPrefix, pCtx->cr4,
1409 pszPrefix, pCtx->dr0, pszPrefix, pCtx->dr1, pszPrefix, pCtx->dr2, pszPrefix, pCtx->dr3,
1410 pszPrefix, pCtx->dr4, pszPrefix, pCtx->dr5, pszPrefix, pCtx->dr6, pszPrefix, pCtx->dr7,
1411 pszPrefix, pCtx->gdtr.pGdt, pCtx->gdtr.cbGdt, pszPrefix, pCtx->idtr.pIdt, pCtx->idtr.cbIdt, pszPrefix, efl,
1412 pszPrefix, (RTSEL)pCtx->ldtr, pCtx->ldtrHid.u64Base, pCtx->ldtrHid.u32Limit, pCtx->ldtrHid.Attr.u,
1413 pszPrefix, (RTSEL)pCtx->tr, pCtx->trHid.u64Base, pCtx->trHid.u32Limit, pCtx->trHid.Attr.u,
1414 pszPrefix, pCtx->SysEnter.cs, pCtx->SysEnter.eip, pCtx->SysEnter.esp);
1415
1416 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1417 "MSR:\n"
1418 "%sEFER =%016RX64\n"
1419 "%sPAT =%016RX64\n"
1420 "%sSTAR =%016RX64\n"
1421 "%sCSTAR =%016RX64\n"
1422 "%sLSTAR =%016RX64\n"
1423 "%sSFMASK =%016RX64\n"
1424 "%sKERNELGSBASE =%016RX64\n",
1425 pszPrefix, pCtx->msrEFER,
1426 pszPrefix, pCtx->msrPAT,
1427 pszPrefix, pCtx->msrSTAR,
1428 pszPrefix, pCtx->msrCSTAR,
1429 pszPrefix, pCtx->msrLSTAR,
1430 pszPrefix, pCtx->msrSFMASK,
1431 pszPrefix, pCtx->msrKERNELGSBASE);
1432 }
1433 else
1434 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1435 "%seax=%08x %sebx=%08x %secx=%08x %sedx=%08x %sesi=%08x %sedi=%08x\n"
1436 "%seip=%08x %sesp=%08x %sebp=%08x %siopl=%d %*s\n"
1437 "%scs={%04x base=%016RX64 limit=%08x flags=%08x} %sdr0=%016RX64 %sdr1=%016RX64\n"
1438 "%sds={%04x base=%016RX64 limit=%08x flags=%08x} %sdr2=%016RX64 %sdr3=%016RX64\n"
1439 "%ses={%04x base=%016RX64 limit=%08x flags=%08x} %sdr4=%016RX64 %sdr5=%016RX64\n"
1440 "%sfs={%04x base=%016RX64 limit=%08x flags=%08x} %sdr6=%016RX64 %sdr7=%016RX64\n"
1441 "%sgs={%04x base=%016RX64 limit=%08x flags=%08x} %scr0=%016RX64 %scr2=%016RX64\n"
1442 "%sss={%04x base=%016RX64 limit=%08x flags=%08x} %scr3=%016RX64 %scr4=%016RX64\n"
1443 "%sgdtr=%016RX64:%04x %sidtr=%016RX64:%04x %seflags=%08x\n"
1444 "%sldtr={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1445 "%str ={%04x base=%016RX64 limit=%08x flags=%08x}\n"
1446 "%sSysEnter={cs=%04llx eip=%08llx esp=%08llx}\n"
1447 "%sFCW=%04x %sFSW=%04x %sFTW=%04x\n"
1448 ,
1449 pszPrefix, pCtxCore->eax, pszPrefix, pCtxCore->ebx, pszPrefix, pCtxCore->ecx, pszPrefix, pCtxCore->edx, pszPrefix, pCtxCore->esi, pszPrefix, pCtxCore->edi,
1450 pszPrefix, pCtxCore->eip, pszPrefix, pCtxCore->esp, pszPrefix, pCtxCore->ebp, pszPrefix, X86_EFL_GET_IOPL(efl), *pszPrefix ? 33 : 31, szEFlags,
1451 pszPrefix, (RTSEL)pCtxCore->cs, pCtx->csHid.u64Base, pCtx->csHid.u32Limit, pCtx->csHid.Attr.u, pszPrefix, pCtx->dr0, pszPrefix, pCtx->dr1,
1452 pszPrefix, (RTSEL)pCtxCore->ds, pCtx->dsHid.u64Base, pCtx->dsHid.u32Limit, pCtx->dsHid.Attr.u, pszPrefix, pCtx->dr2, pszPrefix, pCtx->dr3,
1453 pszPrefix, (RTSEL)pCtxCore->es, pCtx->esHid.u64Base, pCtx->esHid.u32Limit, pCtx->esHid.Attr.u, pszPrefix, pCtx->dr4, pszPrefix, pCtx->dr5,
1454 pszPrefix, (RTSEL)pCtxCore->fs, pCtx->fsHid.u64Base, pCtx->fsHid.u32Limit, pCtx->fsHid.Attr.u, pszPrefix, pCtx->dr6, pszPrefix, pCtx->dr7,
1455 pszPrefix, (RTSEL)pCtxCore->gs, pCtx->gsHid.u64Base, pCtx->gsHid.u32Limit, pCtx->gsHid.Attr.u, pszPrefix, pCtx->cr0, pszPrefix, pCtx->cr2,
1456 pszPrefix, (RTSEL)pCtxCore->ss, pCtx->ssHid.u64Base, pCtx->ssHid.u32Limit, pCtx->ssHid.Attr.u, pszPrefix, pCtx->cr3, pszPrefix, pCtx->cr4,
1457 pszPrefix, pCtx->gdtr.pGdt,pCtx->gdtr.cbGdt, pszPrefix, pCtx->idtr.pIdt, pCtx->idtr.cbIdt, pszPrefix, pCtxCore->eflags,
1458 pszPrefix, (RTSEL)pCtx->ldtr, pCtx->ldtrHid.u64Base, pCtx->ldtrHid.u32Limit, pCtx->ldtrHid.Attr.u,
1459 pszPrefix, (RTSEL)pCtx->tr, pCtx->trHid.u64Base, pCtx->trHid.u32Limit, pCtx->trHid.Attr.u,
1460 pszPrefix, pCtx->SysEnter.cs, pCtx->SysEnter.eip, pCtx->SysEnter.esp,
1461 pszPrefix, pCtx->fpu.FCW, pszPrefix, pCtx->fpu.FSW, pszPrefix, pCtx->fpu.FTW);
1462 }
1463 if (CPUMIsGuestIn64BitCodeEx(pCtx))
1464 {
1465 /*
1466 * Disassemble one instruction at cs:rip.
1467 */
1468 return pCmdHlp->pfnExec(pCmdHlp, "u %016RX64 L 0", pCtx->rip);
1469 }
1470 else
1471 {
1472 /*
1473 * Disassemble one instruction at cs:eip.
1474 */
1475 return pCmdHlp->pfnExec(pCmdHlp, "u %04x:%08x L 0", pCtx->cs, pCtx->eip);
1476 }
1477 }
1478
1479 /*
1480 * cArgs == 1: Show the register.
1481 * cArgs == 2: Modify the register.
1482 */
1483 if ( cArgs == 1
1484 || cArgs == 2)
1485 {
1486 /* locate the register symbol. */
1487 const char *pszReg = paArgs[0].u.pszString;
1488 if ( *pszPrefix
1489 && pszReg[0] != *pszPrefix)
1490 {
1491 /* prepend the prefix. */
1492 char *psz = (char *)alloca(strlen(pszReg) + 2);
1493 psz[0] = *pszPrefix;
1494 strcpy(psz + 1, paArgs[0].u.pszString);
1495 pszReg = psz;
1496 }
1497 PCDBGCSYM pSym = dbgcLookupRegisterSymbol(pDbgc, pszReg);
1498 if (!pSym)
1499 return pCmdHlp->pfnVBoxError(pCmdHlp, VERR_INVALID_PARAMETER /* VERR_DBGC_INVALID_REGISTER */, "Invalid register name '%s'.\n", pszReg);
1500
1501 /* show the register */
1502 if (cArgs == 1)
1503 {
1504 DBGCVAR Var;
1505 memset(&Var, 0, sizeof(Var));
1506 int rc = pSym->pfnGet(pSym, pCmdHlp, DBGCVAR_TYPE_NUMBER, &Var);
1507 if (VBOX_FAILURE(rc))
1508 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Failed getting value for register '%s'.\n", pszReg);
1509 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%s=%Dv\n", pszReg, &Var);
1510 }
1511
1512 /* change the register */
1513 int rc = pSym->pfnSet(pSym, pCmdHlp, &paArgs[1]);
1514 if (VBOX_FAILURE(rc))
1515 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Failed setting value for register '%s'.\n", pszReg);
1516 return VINF_SUCCESS;
1517 }
1518
1519
1520 NOREF(pCmd); NOREF(paArgs); NOREF(pResult);
1521 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Huh? cArgs=%d Expected 0, 1 or 2!\n", cArgs);
1522}
1523
1524
1525/**
1526 * The 'rg' command.
1527 *
1528 * @returns VBox status.
1529 * @param pCmd Pointer to the command descriptor (as registered).
1530 * @param pCmdHlp Pointer to command helper functions.
1531 * @param pVM Pointer to the current VM (if any).
1532 * @param paArgs Pointer to (readonly) array of arguments.
1533 * @param cArgs Number of arguments in the array.
1534 */
1535static DECLCALLBACK(int) dbgcCmdRegGuest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1536{
1537 return dbgcCmdRegCommon(pCmd, pCmdHlp, pVM, paArgs, cArgs, pResult, "");
1538}
1539
1540
1541/**
1542 * The 'rh' command.
1543 *
1544 * @returns VBox status.
1545 * @param pCmd Pointer to the command descriptor (as registered).
1546 * @param pCmdHlp Pointer to command helper functions.
1547 * @param pVM Pointer to the current VM (if any).
1548 * @param paArgs Pointer to (readonly) array of arguments.
1549 * @param cArgs Number of arguments in the array.
1550 */
1551static DECLCALLBACK(int) dbgcCmdRegHyper(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1552{
1553 return dbgcCmdRegCommon(pCmd, pCmdHlp, pVM, paArgs, cArgs, pResult, ".");
1554}
1555
1556
1557/**
1558 * The 'rt' command.
1559 *
1560 * @returns VBox status.
1561 * @param pCmd Pointer to the command descriptor (as registered).
1562 * @param pCmdHlp Pointer to command helper functions.
1563 * @param pVM Pointer to the current VM (if any).
1564 * @param paArgs Pointer to (readonly) array of arguments.
1565 * @param cArgs Number of arguments in the array.
1566 */
1567static DECLCALLBACK(int) dbgcCmdRegTerse(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1568{
1569 NOREF(pCmd); NOREF(pVM); NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
1570
1571 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1572 pDbgc->fRegTerse = !pDbgc->fRegTerse;
1573 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, pDbgc->fRegTerse ? "info: Terse register info.\n" : "info: Verbose register info.\n");
1574}
1575
1576
1577/**
1578 * The 't' command.
1579 *
1580 * @returns VBox status.
1581 * @param pCmd Pointer to the command descriptor (as registered).
1582 * @param pCmdHlp Pointer to command helper functions.
1583 * @param pVM Pointer to the current VM (if any).
1584 * @param paArgs Pointer to (readonly) array of arguments.
1585 * @param cArgs Number of arguments in the array.
1586 */
1587static DECLCALLBACK(int) dbgcCmdTrace(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1588{
1589 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1590
1591 int rc = DBGFR3Step(pVM);
1592 if (VBOX_SUCCESS(rc))
1593 pDbgc->fReady = false;
1594 else
1595 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to single step VM %p\n", pDbgc->pVM);
1596
1597 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
1598 return rc;
1599}
1600
1601
1602/**
1603 * The 'k', 'kg' and 'kh' commands.
1604 *
1605 * @returns VBox status.
1606 * @param pCmd Pointer to the command descriptor (as registered).
1607 * @param pCmdHlp Pointer to command helper functions.
1608 * @param pVM Pointer to the current VM (if any).
1609 * @param paArgs Pointer to (readonly) array of arguments.
1610 * @param cArgs Number of arguments in the array.
1611 */
1612static DECLCALLBACK(int) dbgcCmdStack(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1613{
1614 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1615
1616 /*
1617 * Figure which context we're called for.
1618 */
1619 bool fGuest = pCmd->pszCmd[1] == 'g'
1620 || (!pCmd->pszCmd[1] && pDbgc->fRegCtxGuest);
1621
1622
1623 DBGFSTACKFRAME Frame;
1624 memset(&Frame, 0, sizeof(Frame));
1625 int rc;
1626 if (fGuest)
1627 rc = DBGFR3StackWalkBeginGuest(pVM, &Frame);
1628 else
1629 rc = DBGFR3StackWalkBeginHyper(pVM, &Frame);
1630 if (VBOX_FAILURE(rc))
1631 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Failed to begin stack walk, rc=%Vrc\n", rc);
1632
1633 /*
1634 * Print header.
1635 * 12345678 12345678 0023:87654321 12345678 87654321 12345678 87654321 symbol
1636 */
1637 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "EBP Ret EBP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1638 if (VBOX_FAILURE(rc))
1639 return rc;
1640 do
1641 {
1642 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%08RX32 %08RX32 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1643 (uint32_t)Frame.AddrFrame.off,
1644 (uint32_t)Frame.AddrReturnFrame.off,
1645 (uint32_t)Frame.AddrReturnPC.Sel,
1646 (uint32_t)Frame.AddrReturnPC.off,
1647 Frame.Args.au32[0],
1648 Frame.Args.au32[1],
1649 Frame.Args.au32[2],
1650 Frame.Args.au32[3]);
1651 if (VBOX_FAILURE(rc))
1652 return rc;
1653 if (!Frame.pSymPC)
1654 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " %RTsel:%08RGv", Frame.AddrPC.Sel, Frame.AddrPC.off);
1655 else
1656 {
1657 RTGCINTPTR offDisp = Frame.AddrPC.FlatPtr - Frame.pSymPC->Value; /** @todo this isn't 100% correct for segemnted stuff. */
1658 if (offDisp > 0)
1659 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " %s+%llx", Frame.pSymPC->szName, (int64_t)offDisp);
1660 else if (offDisp < 0)
1661 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " %s-%llx", Frame.pSymPC->szName, -(int64_t)offDisp);
1662 else
1663 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " %s", Frame.pSymPC->szName);
1664 }
1665 if (VBOX_SUCCESS(rc) && Frame.pLinePC)
1666 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " [%s @ 0i%d]", Frame.pLinePC->szFilename, Frame.pLinePC->uLineNo);
1667 if (VBOX_SUCCESS(rc))
1668 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\n");
1669 if (VBOX_FAILURE(rc))
1670 return rc;
1671
1672 /* next */
1673 rc = DBGFR3StackWalkNext(pVM, &Frame);
1674 } while (VBOX_SUCCESS(rc));
1675
1676 NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
1677 return VINF_SUCCESS;
1678}
1679
1680
1681static int dbgcCmdDumpDTWorker64(PDBGCCMDHLP pCmdHlp, PCX86DESC64 pDesc, unsigned iEntry, bool fHyper, bool *fDblEntry)
1682{
1683 /* GUEST64 */
1684 int rc;
1685
1686 const char *pszHyper = fHyper ? " HYPER" : "";
1687 const char *pszPresent = pDesc->Gen.u1Present ? "P " : "NP";
1688 if (pDesc->Gen.u1DescType)
1689 {
1690 static const char * const s_apszTypes[] =
1691 {
1692 "DataRO", /* 0 Read-Only */
1693 "DataRO", /* 1 Read-Only - Accessed */
1694 "DataRW", /* 2 Read/Write */
1695 "DataRW", /* 3 Read/Write - Accessed */
1696 "DownRO", /* 4 Expand-down, Read-Only */
1697 "DownRO", /* 5 Expand-down, Read-Only - Accessed */
1698 "DownRW", /* 6 Expand-down, Read/Write */
1699 "DownRO", /* 7 Expand-down, Read/Write - Accessed */
1700 "CodeEO", /* 8 Execute-Only */
1701 "CodeEO", /* 9 Execute-Only - Accessed */
1702 "CodeER", /* A Execute/Readable */
1703 "CodeER", /* B Execute/Readable - Accessed */
1704 "ConfE0", /* C Conforming, Execute-Only */
1705 "ConfE0", /* D Conforming, Execute-Only - Accessed */
1706 "ConfER", /* E Conforming, Execute/Readable */
1707 "ConfER" /* F Conforming, Execute/Readable - Accessed */
1708 };
1709 const char *pszAccessed = pDesc->Gen.u4Type & RT_BIT(0) ? "A " : "NA";
1710 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
1711 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1712 uint32_t u32Base = pDesc->Gen.u16BaseLow
1713 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
1714 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
1715 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1716 if (pDesc->Gen.u1Granularity)
1717 cbLimit <<= PAGE_SHIFT;
1718
1719 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d L=%d%s\n",
1720 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1721 pDesc->Gen.u2Dpl, pszPresent, pszAccessed, pszGranularity, pszBig,
1722 pDesc->Gen.u1Available, pDesc->Gen.u1Long, pszHyper);
1723 }
1724 else
1725 {
1726 static const char * const s_apszTypes[] =
1727 {
1728 "Ill-0 ", /* 0 0000 Reserved (Illegal) */
1729 "Ill-1 ", /* 1 0001 Available 16-bit TSS */
1730 "LDT ", /* 2 0010 LDT */
1731 "Ill-3 ", /* 3 0011 Busy 16-bit TSS */
1732 "Ill-4 ", /* 4 0100 16-bit Call Gate */
1733 "Ill-5 ", /* 5 0101 Task Gate */
1734 "Ill-6 ", /* 6 0110 16-bit Interrupt Gate */
1735 "Ill-7 ", /* 7 0111 16-bit Trap Gate */
1736 "Ill-8 ", /* 8 1000 Reserved (Illegal) */
1737 "Tss64A", /* 9 1001 Available 32-bit TSS */
1738 "Ill-A ", /* A 1010 Reserved (Illegal) */
1739 "Tss64B", /* B 1011 Busy 32-bit TSS */
1740 "Call64", /* C 1100 32-bit Call Gate */
1741 "Ill-D ", /* D 1101 Reserved (Illegal) */
1742 "Int64 ", /* E 1110 32-bit Interrupt Gate */
1743 "Trap64" /* F 1111 32-bit Trap Gate */
1744 };
1745 switch (pDesc->Gen.u4Type)
1746 {
1747 /* raw */
1748 case X86_SEL_TYPE_SYS_UNDEFINED:
1749 case X86_SEL_TYPE_SYS_UNDEFINED2:
1750 case X86_SEL_TYPE_SYS_UNDEFINED4:
1751 case X86_SEL_TYPE_SYS_UNDEFINED3:
1752 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
1753 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
1754 case X86_SEL_TYPE_SYS_286_CALL_GATE:
1755 case X86_SEL_TYPE_SYS_286_INT_GATE:
1756 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
1757 case X86_SEL_TYPE_SYS_TASK_GATE:
1758 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s %.8Rhxs DPL=%d %s%s\n",
1759 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc,
1760 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1761 break;
1762
1763 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
1764 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
1765 case X86_SEL_TYPE_SYS_LDT:
1766 {
1767 const char *pszBusy = pDesc->Gen.u4Type & RT_BIT(1) ? "B " : "NB";
1768 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1769 const char *pszLong = pDesc->Gen.u1Long ? "LONG" : " ";
1770
1771 uint64_t u32Base = pDesc->Gen.u16BaseLow
1772 | ((uint64_t)pDesc->Gen.u8BaseHigh1 << 16)
1773 | ((uint64_t)pDesc->Gen.u8BaseHigh2 << 24)
1774 | ((uint64_t)pDesc->Gen.u32BaseHigh3 << 32);
1775 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1776
1777 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Bas=%016RX64 Lim=%08x DPL=%d %s %s %s %sAVL=%d R=%d%s\n",
1778 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1779 pDesc->Gen.u2Dpl, pszPresent, pszBusy, pszLong, pszBig,
1780 pDesc->Gen.u1Available, pDesc->Gen.u1Long | (pDesc->Gen.u1DefBig << 1),
1781 pszHyper);
1782 if (fDblEntry) *fDblEntry = true;
1783 break;
1784 }
1785
1786 case X86_SEL_TYPE_SYS_386_CALL_GATE:
1787 {
1788 unsigned cParams = pDesc->au8[0] & 0x1f;
1789 const char *pszCountOf = pDesc->Gen.u4Type & RT_BIT(3) ? "DC" : "WC";
1790 RTSEL sel = pDesc->au16[1];
1791 uint64_t off = pDesc->au16[0]
1792 | ((uint64_t)pDesc->au16[3] << 16)
1793 | ((uint64_t)pDesc->Gen.u32BaseHigh3 << 32);
1794 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Sel:Off=%04x:%016RX64 DPL=%d %s %s=%d%s\n",
1795 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1796 pDesc->Gen.u2Dpl, pszPresent, pszCountOf, cParams, pszHyper);
1797 if (fDblEntry) *fDblEntry = true;
1798 break;
1799 }
1800
1801 case X86_SEL_TYPE_SYS_386_INT_GATE:
1802 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
1803 {
1804 RTSEL sel = pDesc->au16[1];
1805 uint64_t off = pDesc->au16[0]
1806 | ((uint64_t)pDesc->au16[3] << 16)
1807 | ((uint64_t)pDesc->Gen.u32BaseHigh3 << 32);
1808 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Sel:Off=%04x:%016RX64 DPL=%d %s%s\n",
1809 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1810 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1811 if (fDblEntry) *fDblEntry = true;
1812 break;
1813 }
1814
1815 /* impossible, just it's necessary to keep gcc happy. */
1816 default:
1817 return VINF_SUCCESS;
1818 }
1819 }
1820 return VINF_SUCCESS;
1821}
1822
1823
1824/**
1825 * Worker function that displays one descriptor entry (GDT, LDT, IDT).
1826 *
1827 * @returns pfnPrintf status code.
1828 * @param pCmdHlp The DBGC command helpers.
1829 * @param pDesc The descriptor to display.
1830 * @param iEntry The descriptor entry number.
1831 * @param fHyper Whether the selector belongs to the hypervisor or not.
1832 */
1833static int dbgcCmdDumpDTWorker32(PDBGCCMDHLP pCmdHlp, PCX86DESC pDesc, unsigned iEntry, bool fHyper)
1834{
1835 int rc;
1836
1837 const char *pszHyper = fHyper ? " HYPER" : "";
1838 const char *pszPresent = pDesc->Gen.u1Present ? "P " : "NP";
1839 if (pDesc->Gen.u1DescType)
1840 {
1841 static const char * const s_apszTypes[] =
1842 {
1843 "DataRO", /* 0 Read-Only */
1844 "DataRO", /* 1 Read-Only - Accessed */
1845 "DataRW", /* 2 Read/Write */
1846 "DataRW", /* 3 Read/Write - Accessed */
1847 "DownRO", /* 4 Expand-down, Read-Only */
1848 "DownRO", /* 5 Expand-down, Read-Only - Accessed */
1849 "DownRW", /* 6 Expand-down, Read/Write */
1850 "DownRO", /* 7 Expand-down, Read/Write - Accessed */
1851 "CodeEO", /* 8 Execute-Only */
1852 "CodeEO", /* 9 Execute-Only - Accessed */
1853 "CodeER", /* A Execute/Readable */
1854 "CodeER", /* B Execute/Readable - Accessed */
1855 "ConfE0", /* C Conforming, Execute-Only */
1856 "ConfE0", /* D Conforming, Execute-Only - Accessed */
1857 "ConfER", /* E Conforming, Execute/Readable */
1858 "ConfER" /* F Conforming, Execute/Readable - Accessed */
1859 };
1860 const char *pszAccessed = pDesc->Gen.u4Type & RT_BIT(0) ? "A " : "NA";
1861 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
1862 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1863 uint32_t u32Base = pDesc->Gen.u16BaseLow
1864 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
1865 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
1866 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1867 if (pDesc->Gen.u1Granularity)
1868 cbLimit <<= PAGE_SHIFT;
1869
1870 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d L=%d%s\n",
1871 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1872 pDesc->Gen.u2Dpl, pszPresent, pszAccessed, pszGranularity, pszBig,
1873 pDesc->Gen.u1Available, pDesc->Gen.u1Long, pszHyper);
1874 }
1875 else
1876 {
1877 static const char * const s_apszTypes[] =
1878 {
1879 "Ill-0 ", /* 0 0000 Reserved (Illegal) */
1880 "Tss16A", /* 1 0001 Available 16-bit TSS */
1881 "LDT ", /* 2 0010 LDT */
1882 "Tss16B", /* 3 0011 Busy 16-bit TSS */
1883 "Call16", /* 4 0100 16-bit Call Gate */
1884 "TaskG ", /* 5 0101 Task Gate */
1885 "Int16 ", /* 6 0110 16-bit Interrupt Gate */
1886 "Trap16", /* 7 0111 16-bit Trap Gate */
1887 "Ill-8 ", /* 8 1000 Reserved (Illegal) */
1888 "Tss32A", /* 9 1001 Available 32-bit TSS */
1889 "Ill-A ", /* A 1010 Reserved (Illegal) */
1890 "Tss32B", /* B 1011 Busy 32-bit TSS */
1891 "Call32", /* C 1100 32-bit Call Gate */
1892 "Ill-D ", /* D 1101 Reserved (Illegal) */
1893 "Int32 ", /* E 1110 32-bit Interrupt Gate */
1894 "Trap32" /* F 1111 32-bit Trap Gate */
1895 };
1896 switch (pDesc->Gen.u4Type)
1897 {
1898 /* raw */
1899 case X86_SEL_TYPE_SYS_UNDEFINED:
1900 case X86_SEL_TYPE_SYS_UNDEFINED2:
1901 case X86_SEL_TYPE_SYS_UNDEFINED4:
1902 case X86_SEL_TYPE_SYS_UNDEFINED3:
1903 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s %.8Rhxs DPL=%d %s%s\n",
1904 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc,
1905 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1906 break;
1907
1908 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
1909 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
1910 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
1911 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
1912 case X86_SEL_TYPE_SYS_LDT:
1913 {
1914 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
1915 const char *pszBusy = pDesc->Gen.u4Type & RT_BIT(1) ? "B " : "NB";
1916 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1917 uint32_t u32Base = pDesc->Gen.u16BaseLow
1918 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
1919 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
1920 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1921 if (pDesc->Gen.u1Granularity)
1922 cbLimit <<= PAGE_SHIFT;
1923
1924 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d R=%d%s\n",
1925 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1926 pDesc->Gen.u2Dpl, pszPresent, pszBusy, pszGranularity, pszBig,
1927 pDesc->Gen.u1Available, pDesc->Gen.u1Long | (pDesc->Gen.u1DefBig << 1),
1928 pszHyper);
1929 break;
1930 }
1931
1932 case X86_SEL_TYPE_SYS_TASK_GATE:
1933 {
1934 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s TSS=%04x DPL=%d %s%s\n",
1935 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc->au16[1],
1936 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1937 break;
1938 }
1939
1940 case X86_SEL_TYPE_SYS_286_CALL_GATE:
1941 case X86_SEL_TYPE_SYS_386_CALL_GATE:
1942 {
1943 unsigned cParams = pDesc->au8[0] & 0x1f;
1944 const char *pszCountOf = pDesc->Gen.u4Type & RT_BIT(3) ? "DC" : "WC";
1945 RTSEL sel = pDesc->au16[1];
1946 uint32_t off = pDesc->au16[0] | ((uint32_t)pDesc->au16[3] << 16);
1947 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Sel:Off=%04x:%08x DPL=%d %s %s=%d%s\n",
1948 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1949 pDesc->Gen.u2Dpl, pszPresent, pszCountOf, cParams, pszHyper);
1950 break;
1951 }
1952
1953 case X86_SEL_TYPE_SYS_286_INT_GATE:
1954 case X86_SEL_TYPE_SYS_386_INT_GATE:
1955 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
1956 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
1957 {
1958 RTSEL sel = pDesc->au16[1];
1959 uint32_t off = pDesc->au16[0] | ((uint32_t)pDesc->au16[3] << 16);
1960 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %s Sel:Off=%04x:%08x DPL=%d %s%s\n",
1961 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1962 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1963 break;
1964 }
1965
1966 /* impossible, just it's necessary to keep gcc happy. */
1967 default:
1968 return VINF_SUCCESS;
1969 }
1970 }
1971 return rc;
1972}
1973
1974
1975/**
1976 * The 'dg', 'dga', 'dl' and 'dla' commands.
1977 *
1978 * @returns VBox status.
1979 * @param pCmd Pointer to the command descriptor (as registered).
1980 * @param pCmdHlp Pointer to command helper functions.
1981 * @param pVM Pointer to the current VM (if any).
1982 * @param paArgs Pointer to (readonly) array of arguments.
1983 * @param cArgs Number of arguments in the array.
1984 */
1985static DECLCALLBACK(int) dbgcCmdDumpDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1986{
1987 /*
1988 * Validate input.
1989 */
1990 if (!pVM)
1991 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
1992
1993 /*
1994 * Get the CPU mode, check which command variation this is
1995 * and fix a default parameter if needed.
1996 */
1997 CPUMMODE enmMode = CPUMGetGuestMode(pVM);
1998 bool fGdt = pCmd->pszCmd[1] == 'g';
1999 bool fAll = pCmd->pszCmd[2] == 'a';
2000
2001 DBGCVAR Var;
2002 if (!cArgs)
2003 {
2004 cArgs = 1;
2005 paArgs = &Var;
2006 Var.enmType = DBGCVAR_TYPE_NUMBER;
2007 Var.u.u64Number = fGdt ? 0 : 4;
2008 Var.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
2009 Var.u64Range = 1024;
2010 }
2011
2012 /*
2013 * Process the arguments.
2014 */
2015 for (unsigned i = 0; i < cArgs; i++)
2016 {
2017 /*
2018 * Retrive the selector value from the argument.
2019 * The parser may confuse pointers and numbers if more than one
2020 * argument is given, that that into account.
2021 */
2022 /* check that what've got makes sense as we don't trust the parser yet. */
2023 if ( paArgs[i].enmType != DBGCVAR_TYPE_NUMBER
2024 && !DBGCVAR_ISPOINTER(paArgs[i].enmType))
2025 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: arg #%u isn't of number or pointer type but %d.\n", i, paArgs[i].enmType);
2026 uint64_t u64;
2027 unsigned cSels = 1;
2028 switch (paArgs[i].enmType)
2029 {
2030 case DBGCVAR_TYPE_NUMBER:
2031 u64 = paArgs[i].u.u64Number;
2032 if (paArgs[i].enmRangeType != DBGCVAR_RANGE_NONE)
2033 cSels = RT_MIN(paArgs[i].u64Range, 1024);
2034 break;
2035 case DBGCVAR_TYPE_GC_FAR: u64 = paArgs[i].u.GCFar.sel; break;
2036 case DBGCVAR_TYPE_GC_FLAT: u64 = paArgs[i].u.GCFlat; break;
2037 case DBGCVAR_TYPE_GC_PHYS: u64 = paArgs[i].u.GCPhys; break;
2038 case DBGCVAR_TYPE_HC_FAR: u64 = paArgs[i].u.HCFar.sel; break;
2039 case DBGCVAR_TYPE_HC_FLAT: u64 = (uintptr_t)paArgs[i].u.pvHCFlat; break;
2040 case DBGCVAR_TYPE_HC_PHYS: u64 = paArgs[i].u.HCPhys; break;
2041 default: u64 = _64K; break;
2042 }
2043 if (u64 < _64K)
2044 {
2045 unsigned Sel = (RTSEL)u64;
2046
2047 /*
2048 * Dump the specified range.
2049 */
2050 bool fSingle = cSels == 1;
2051 while ( cSels-- > 0
2052 && Sel < _64K)
2053 {
2054 SELMSELINFO SelInfo;
2055 int rc = SELMR3GetSelectorInfo(pVM, Sel, &SelInfo);
2056 if (RT_SUCCESS(rc))
2057 {
2058 if (SelInfo.fRealMode)
2059 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x RealM Bas=%04x Lim=%04x\n",
2060 Sel, (unsigned)SelInfo.GCPtrBase, (unsigned)SelInfo.cbLimit);
2061 else if (fAll || fSingle || SelInfo.Raw.Gen.u1Present)
2062 {
2063 if (enmMode == CPUMMODE_PROTECTED)
2064 rc = dbgcCmdDumpDTWorker32(pCmdHlp, (PX86DESC)&SelInfo.Raw, Sel, SelInfo.fHyper);
2065 else
2066 {
2067 bool fDblSkip = false;
2068 rc = dbgcCmdDumpDTWorker64(pCmdHlp, (PX86DESC64)&SelInfo.Raw, Sel, SelInfo.fHyper, &fDblSkip);
2069 if (fDblSkip)
2070 Sel += 4;
2071 }
2072 }
2073 }
2074 else
2075 {
2076 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %Vrc\n", Sel, rc);
2077 if (!fAll)
2078 return rc;
2079 }
2080 if (RT_FAILURE(rc))
2081 return rc;
2082
2083 /* next */
2084 Sel += 4;
2085 }
2086 }
2087 else
2088 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: %llx is out of bounds\n", u64);
2089 }
2090
2091 NOREF(pResult);
2092 return VINF_SUCCESS;
2093}
2094
2095
2096/**
2097 * The 'di' and 'dia' commands.
2098 *
2099 * @returns VBox status.
2100 * @param pCmd Pointer to the command descriptor (as registered).
2101 * @param pCmdHlp Pointer to command helper functions.
2102 * @param pVM Pointer to the current VM (if any).
2103 * @param paArgs Pointer to (readonly) array of arguments.
2104 * @param cArgs Number of arguments in the array.
2105 */
2106static DECLCALLBACK(int) dbgcCmdDumpIDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2107{
2108 /*
2109 * Validate input.
2110 */
2111 if (!pVM)
2112 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
2113
2114 /*
2115 * Establish some stuff like the current IDTR and CPU mode,
2116 * and fix a default parameter.
2117 */
2118 uint16_t cbLimit;
2119 RTGCUINTPTR GCPtrBase = CPUMGetGuestIDTR(pVM, &cbLimit);
2120 CPUMMODE enmMode = CPUMGetGuestMode(pVM);
2121 unsigned cbEntry;
2122 switch (enmMode)
2123 {
2124 case CPUMMODE_REAL: cbEntry = sizeof(RTFAR16); break;
2125 case CPUMMODE_PROTECTED: cbEntry = sizeof(X86DESC); break;
2126 case CPUMMODE_LONG: cbEntry = sizeof(X86DESC64); break;
2127 default:
2128 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Invalid CPU mode %d.\n", enmMode);
2129 }
2130
2131 bool fAll = pCmd->pszCmd[2] == 'a';
2132 DBGCVAR Var;
2133 if (!cArgs)
2134 {
2135 cArgs = 1;
2136 paArgs = &Var;
2137 Var.enmType = DBGCVAR_TYPE_NUMBER;
2138 Var.u.u64Number = 0;
2139 Var.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
2140 Var.u64Range = 256;
2141 }
2142
2143 /*
2144 * Process the arguments.
2145 */
2146 for (unsigned i = 0; i < cArgs; i++)
2147 {
2148 /* check that what've got makes sense as we don't trust the parser yet. */
2149 if (paArgs[i].enmType != DBGCVAR_TYPE_NUMBER)
2150 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: arg #%u isn't of number type but %d.\n", i, paArgs[i].enmType);
2151 if (paArgs[i].u.u64Number < 256)
2152 {
2153 RTGCUINTPTR iInt = (RTGCUINTPTR)paArgs[i].u.u64Number;
2154 unsigned cInts = paArgs[i].enmRangeType != DBGCVAR_RANGE_NONE
2155 ? paArgs[i].u64Range
2156 : 1;
2157 bool fSingle = cInts == 1;
2158 while ( cInts-- > 0
2159 && iInt < 256)
2160 {
2161 /*
2162 * Try read it.
2163 */
2164 union
2165 {
2166 RTFAR16 Real;
2167 X86DESC Prot;
2168 X86DESC64 Long;
2169 } u;
2170 if (iInt * cbEntry + (cbEntry - 1) > cbLimit)
2171 {
2172 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x not within the IDT\n", (unsigned)iInt);
2173 if (!fAll && !fSingle)
2174 return VINF_SUCCESS;
2175 }
2176 DBGCVAR AddrVar;
2177 AddrVar.enmType = DBGCVAR_TYPE_GC_FLAT;
2178 AddrVar.u.GCFlat = GCPtrBase + iInt * cbEntry;
2179 AddrVar.enmRangeType = DBGCVAR_RANGE_NONE;
2180 int rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &u, cbEntry, &AddrVar, NULL);
2181 if (VBOX_FAILURE(rc))
2182 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading IDT entry %#04x.\n", (unsigned)iInt);
2183
2184 /*
2185 * Display it.
2186 */
2187 switch (enmMode)
2188 {
2189 case CPUMMODE_REAL:
2190 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%04x %RTfp16\n", (unsigned)iInt, u.Real);
2191 /** @todo resolve 16:16 IDTE to a symbol */
2192 break;
2193 case CPUMMODE_PROTECTED:
2194 if (fAll || fSingle || u.Prot.Gen.u1Present)
2195 rc = dbgcCmdDumpDTWorker32(pCmdHlp, &u.Prot, iInt, false);
2196 break;
2197 case CPUMMODE_LONG:
2198 if (fAll || fSingle || u.Long.Gen.u1Present)
2199 rc = dbgcCmdDumpDTWorker64(pCmdHlp, &u.Long, iInt, false, NULL);
2200 break;
2201 default: break; /* to shut up gcc */
2202 }
2203 if (RT_FAILURE(rc))
2204 return rc;
2205
2206 /* next */
2207 iInt++;
2208 }
2209 }
2210 else
2211 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: %llx is out of bounds (max 256)\n", paArgs[i].u.u64Number);
2212 }
2213
2214 NOREF(pResult);
2215 return VINF_SUCCESS;
2216}
2217
2218
2219/**
2220 * The 'da', 'dq', 'dd', 'dw' and 'db' commands.
2221 *
2222 * @returns VBox status.
2223 * @param pCmd Pointer to the command descriptor (as registered).
2224 * @param pCmdHlp Pointer to command helper functions.
2225 * @param pVM Pointer to the current VM (if any).
2226 * @param paArgs Pointer to (readonly) array of arguments.
2227 * @param cArgs Number of arguments in the array.
2228 */
2229static DECLCALLBACK(int) dbgcCmdDumpMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2230{
2231 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2232
2233 /*
2234 * Validate input.
2235 */
2236 if ( cArgs > 1
2237 || (cArgs == 1 && !DBGCVAR_ISPOINTER(paArgs[0].enmType)))
2238 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: The parser doesn't do its job properly yet.. It might help to use the '%%' operator.\n");
2239 if (!pVM)
2240 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
2241
2242 /*
2243 * Figure out the element size.
2244 */
2245 unsigned cbElement;
2246 bool fAscii = false;
2247 switch (pCmd->pszCmd[1])
2248 {
2249 default:
2250 case 'b': cbElement = 1; break;
2251 case 'w': cbElement = 2; break;
2252 case 'd': cbElement = 4; break;
2253 case 'q': cbElement = 8; break;
2254 case 'a':
2255 cbElement = 1;
2256 fAscii = true;
2257 break;
2258 case '\0':
2259 fAscii = !!(pDbgc->cbDumpElement & 0x80000000);
2260 cbElement = pDbgc->cbDumpElement & 0x7fffffff;
2261 if (!cbElement)
2262 cbElement = 1;
2263 break;
2264 }
2265
2266 /*
2267 * Find address.
2268 */
2269 if (!cArgs)
2270 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_NONE;
2271 else
2272 pDbgc->DumpPos = paArgs[0];
2273
2274 /*
2275 * Range.
2276 */
2277 switch (pDbgc->DumpPos.enmRangeType)
2278 {
2279 case DBGCVAR_RANGE_NONE:
2280 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_BYTES;
2281 pDbgc->DumpPos.u64Range = 0x60;
2282 break;
2283
2284 case DBGCVAR_RANGE_ELEMENTS:
2285 if (pDbgc->DumpPos.u64Range > 2048)
2286 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: Too many elements requested. Max is 2048 elements.\n");
2287 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_BYTES;
2288 pDbgc->DumpPos.u64Range = (cbElement ? cbElement : 1) * pDbgc->DumpPos.u64Range;
2289 break;
2290
2291 case DBGCVAR_RANGE_BYTES:
2292 if (pDbgc->DumpPos.u64Range > 65536)
2293 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The requested range is too big. Max is 64KB.\n");
2294 break;
2295
2296 default:
2297 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: Unknown range type %d.\n", pDbgc->DumpPos.enmRangeType);
2298 }
2299
2300 /*
2301 * Do the dumping.
2302 */
2303 pDbgc->cbDumpElement = cbElement | (fAscii << 31);
2304 int cbLeft = (int)pDbgc->DumpPos.u64Range;
2305 uint8_t u8Prev = '\0';
2306 for (;;)
2307 {
2308 /*
2309 * Read memory.
2310 */
2311 char achBuffer[16];
2312 size_t cbReq = RT_MIN((int)sizeof(achBuffer), cbLeft);
2313 size_t cb = RT_MIN((int)sizeof(achBuffer), cbLeft);
2314 int rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &achBuffer, cbReq, &pDbgc->DumpPos, &cb);
2315 if (VBOX_FAILURE(rc))
2316 {
2317 if (u8Prev && u8Prev != '\n')
2318 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\n");
2319 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading memory at %DV.\n", &pDbgc->DumpPos);
2320 }
2321
2322 /*
2323 * Display it.
2324 */
2325 memset(&achBuffer[cb], 0, sizeof(achBuffer) - cb);
2326 if (!fAscii)
2327 {
2328 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%DV:", &pDbgc->DumpPos);
2329 unsigned i;
2330 for (i = 0; i < cb; i += cbElement)
2331 {
2332 const char *pszSpace = " ";
2333 if (cbElement <= 2 && i == 8 && !fAscii)
2334 pszSpace = "-";
2335 switch (cbElement)
2336 {
2337 case 1: pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%s%02x", pszSpace, *(uint8_t *)&achBuffer[i]); break;
2338 case 2: pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%s%04x", pszSpace, *(uint16_t *)&achBuffer[i]); break;
2339 case 4: pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%s%08x", pszSpace, *(uint32_t *)&achBuffer[i]); break;
2340 case 8: pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%s%016llx", pszSpace, *(uint64_t *)&achBuffer[i]); break;
2341 }
2342 }
2343
2344 /* chars column */
2345 if (pDbgc->cbDumpElement == 1)
2346 {
2347 while (i++ < sizeof(achBuffer))
2348 pCmdHlp->pfnPrintf(pCmdHlp, NULL, " ");
2349 pCmdHlp->pfnPrintf(pCmdHlp, NULL, " ");
2350 for (i = 0; i < cb; i += cbElement)
2351 {
2352 uint8_t u8 = *(uint8_t *)&achBuffer[i];
2353 if (isprint(u8) && u8 < 127 && u8 >= 32)
2354 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%c", u8);
2355 else
2356 pCmdHlp->pfnPrintf(pCmdHlp, NULL, ".");
2357 }
2358 }
2359 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\n");
2360 }
2361 else
2362 {
2363 /*
2364 * We print up to the first zero and stop there.
2365 * Only printables + '\t' and '\n' are printed.
2366 */
2367 if (!u8Prev)
2368 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%DV:\n", &pDbgc->DumpPos);
2369 uint8_t u8 = '\0';
2370 unsigned i;
2371 for (i = 0; i < cb; i++)
2372 {
2373 u8Prev = u8;
2374 u8 = *(uint8_t *)&achBuffer[i];
2375 if ( u8 < 127
2376 && ( (isprint(u8) && u8 >= 32)
2377 || u8 == '\t'
2378 || u8 == '\n'))
2379 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%c", u8);
2380 else if (!u8)
2381 break;
2382 else
2383 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\\x%x", u8);
2384 }
2385 if (u8 == '\0')
2386 cb = cbLeft = i + 1;
2387 if (cbLeft - cb <= 0 && u8Prev != '\n')
2388 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\n");
2389 }
2390
2391 /*
2392 * Advance
2393 */
2394 cbLeft -= (int)cb;
2395 rc = pCmdHlp->pfnEval(pCmdHlp, &pDbgc->DumpPos, "(%Dv) + %x", &pDbgc->DumpPos, cb);
2396 if (VBOX_FAILURE(rc))
2397 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->DumpPos, cb);
2398 if (cbLeft <= 0)
2399 break;
2400 }
2401
2402 NOREF(pCmd); NOREF(pResult);
2403 return VINF_SUCCESS;
2404}
2405
2406
2407/**
2408 * Best guess at which paging mode currently applies to the guest
2409 * paging structures.
2410 *
2411 * This have to come up with a decent answer even when the guest
2412 * is in non-paged protected mode or real mode.
2413 *
2414 * @returns cr3.
2415 * @param pDbgc The DBGC instance.
2416 * @param pfPAE Where to store the page address extension indicator.
2417 * @param pfLME Where to store the long mode enabled indicator.
2418 * @param pfPSE Where to store the page size extension indicator.
2419 * @param pfPGE Where to store the page global enabled indicator.
2420 * @param pfNXE Where to store the no-execution enabled inidicator.
2421 */
2422static RTGCPHYS dbgcGetGuestPageMode(PDBGC pDbgc, bool *pfPAE, bool *pfLME, bool *pfPSE, bool *pfPGE, bool *pfNXE)
2423{
2424 RTGCUINTREG cr4 = CPUMGetGuestCR4(pDbgc->pVM);
2425 *pfPSE = !!(cr4 & X86_CR4_PSE);
2426 *pfPGE = !!(cr4 & X86_CR4_PGE);
2427 *pfPAE = !!(cr4 & X86_CR4_PAE);
2428 *pfLME = CPUMGetGuestMode(pDbgc->pVM) == CPUMMODE_LONG;
2429 *pfNXE = false; /* GUEST64 GUESTNX */
2430 return CPUMGetGuestCR3(pDbgc->pVM);
2431}
2432
2433
2434/**
2435 * Determin the shadow paging mode.
2436 *
2437 * @returns cr3.
2438 * @param pDbgc The DBGC instance.
2439 * @param pfPAE Where to store the page address extension indicator.
2440 * @param pfLME Where to store the long mode enabled indicator.
2441 * @param pfPSE Where to store the page size extension indicator.
2442 * @param pfPGE Where to store the page global enabled indicator.
2443 * @param pfNXE Where to store the no-execution enabled inidicator.
2444 */
2445static RTHCPHYS dbgcGetShadowPageMode(PDBGC pDbgc, bool *pfPAE, bool *pfLME, bool *pfPSE, bool *pfPGE, bool *pfNXE)
2446{
2447 *pfPSE = true;
2448 *pfPGE = false;
2449 switch (PGMGetShadowMode(pDbgc->pVM))
2450 {
2451 default:
2452 case PGMMODE_32_BIT:
2453 *pfPAE = *pfLME = *pfNXE = false;
2454 break;
2455 case PGMMODE_PAE:
2456 *pfLME = *pfNXE = false;
2457 *pfPAE = true;
2458 break;
2459 case PGMMODE_PAE_NX:
2460 *pfLME = false;
2461 *pfPAE = *pfNXE = true;
2462 break;
2463 case PGMMODE_AMD64:
2464 *pfNXE = false;
2465 *pfPAE = *pfLME = true;
2466 break;
2467 case PGMMODE_AMD64_NX:
2468 *pfPAE = *pfLME = *pfNXE = true;
2469 break;
2470 }
2471 return PGMGetHyperCR3(pDbgc->pVM);
2472}
2473
2474
2475/**
2476 * The 'dpd', 'dpda', 'dpdb', 'dpdg' and 'dpdh' commands.
2477 *
2478 * @returns VBox status.
2479 * @param pCmd Pointer to the command descriptor (as registered).
2480 * @param pCmdHlp Pointer to command helper functions.
2481 * @param pVM Pointer to the current VM (if any).
2482 * @param paArgs Pointer to (readonly) array of arguments.
2483 * @param cArgs Number of arguments in the array.
2484 */
2485static DECLCALLBACK(int) dbgcCmdDumpPageDir(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2486{
2487 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2488
2489 /*
2490 * Validate input.
2491 */
2492 if ( cArgs > 1
2493 || (cArgs == 1 && pCmd->pszCmd[3] == 'a' && !DBGCVAR_ISPOINTER(paArgs[0].enmType))
2494 || (cArgs == 1 && pCmd->pszCmd[3] != 'a' && !(paArgs[0].enmType == DBGCVAR_TYPE_NUMBER || DBGCVAR_ISPOINTER(paArgs[0].enmType)))
2495 )
2496 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: The parser doesn't do its job properly yet.. It might help to use the '%%' operator.\n");
2497 if (!pVM)
2498 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
2499
2500 /*
2501 * Guest or shadow page directories? Get the paging parameters.
2502 */
2503 bool fGuest = pCmd->pszCmd[3] != 'h';
2504 if (!pCmd->pszCmd[3] || pCmd->pszCmd[3] == 'a')
2505 fGuest = paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
2506 ? pDbgc->fRegCtxGuest
2507 : DBGCVAR_ISGCPOINTER(paArgs[0].enmType);
2508
2509 bool fPAE, fLME, fPSE, fPGE, fNXE;
2510 uint64_t cr3 = fGuest
2511 ? dbgcGetGuestPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE)
2512 : dbgcGetShadowPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE);
2513 const unsigned cbEntry = fPAE ? sizeof(X86PTEPAE) : sizeof(X86PTE);
2514
2515 /*
2516 * Setup default arugment if none was specified.
2517 * Fix address / index confusion.
2518 */
2519 DBGCVAR VarDefault;
2520 if (!cArgs)
2521 {
2522 if (pCmd->pszCmd[3] == 'a')
2523 {
2524 if (fLME || fPAE)
2525 return DBGCCmdHlpPrintf(pCmdHlp, "Default argument for 'dpda' hasn't been fully implemented yet. Try with an address or use one of the other commands.\n");
2526 if (fGuest)
2527 DBGCVAR_INIT_GC_PHYS(&VarDefault, cr3);
2528 else
2529 DBGCVAR_INIT_HC_PHYS(&VarDefault, cr3);
2530 }
2531 else
2532 DBGCVAR_INIT_GC_FLAT(&VarDefault, 0);
2533 paArgs = &VarDefault;
2534 cArgs = 1;
2535 }
2536 else if (paArgs[0].enmType == DBGCVAR_TYPE_NUMBER)
2537 {
2538 Assert(pCmd->pszCmd[3] != 'a');
2539 VarDefault = paArgs[0];
2540 if (VarDefault.u.u64Number <= 1024)
2541 {
2542 if (fPAE)
2543 return DBGCCmdHlpPrintf(pCmdHlp, "PDE indexing is only implemented for 32-bit paging.\n");
2544 if (VarDefault.u.u64Number >= PAGE_SIZE / cbEntry)
2545 return DBGCCmdHlpPrintf(pCmdHlp, "PDE index is out of range [0..%d].\n", PAGE_SIZE / cbEntry - 1);
2546 VarDefault.u.u64Number <<= X86_PD_SHIFT;
2547 }
2548 VarDefault.enmType = DBGCVAR_TYPE_GC_FLAT;
2549 paArgs = &VarDefault;
2550 }
2551
2552 /*
2553 * Locate the PDE to start displaying at.
2554 *
2555 * The 'dpda' command takes the address of a PDE, while the others are guest
2556 * virtual address which PDEs should be displayed. So, 'dpda' is rather simple
2557 * while the others require us to do all the tedious walking thru the paging
2558 * hierarchy to find the intended PDE.
2559 */
2560 unsigned iEntry = ~0U; /* The page directory index. ~0U for 'dpta'. */
2561 DBGCVAR VarGCPtr; /* The GC address corresponding to the current PDE (iEntry != ~0U). */
2562 DBGCVAR VarPDEAddr; /* The address of the current PDE. */
2563 unsigned cEntries; /* The number of entries to display. */
2564 unsigned cEntriesMax; /* The max number of entries to display. */
2565 int rc;
2566 if (pCmd->pszCmd[3] == 'a')
2567 {
2568 VarPDEAddr = paArgs[0];
2569 switch (VarPDEAddr.enmRangeType)
2570 {
2571 case DBGCVAR_RANGE_BYTES: cEntries = VarPDEAddr.u64Range / cbEntry; break;
2572 case DBGCVAR_RANGE_ELEMENTS: cEntries = VarPDEAddr.u64Range; break;
2573 default: cEntries = 10; break;
2574 }
2575 cEntriesMax = PAGE_SIZE / cbEntry;
2576 }
2577 else
2578 {
2579 /*
2580 * Determin the range.
2581 */
2582 switch (paArgs[0].enmRangeType)
2583 {
2584 case DBGCVAR_RANGE_BYTES: cEntries = paArgs[0].u64Range / PAGE_SIZE; break;
2585 case DBGCVAR_RANGE_ELEMENTS: cEntries = paArgs[0].u64Range; break;
2586 default: cEntries = 10; break;
2587 }
2588
2589 /*
2590 * Normalize the input address, it must be a flat GC address.
2591 */
2592 rc = pCmdHlp->pfnEval(pCmdHlp, &VarGCPtr, "%%(%Dv)", &paArgs[0]);
2593 if (VBOX_FAILURE(rc))
2594 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "%%(%Dv)", &paArgs[0]);
2595 if (VarGCPtr.enmType == DBGCVAR_TYPE_HC_FLAT)
2596 {
2597 VarGCPtr.u.GCFlat = (uintptr_t)VarGCPtr.u.pvHCFlat;
2598 VarGCPtr.enmType = DBGCVAR_TYPE_GC_FLAT;
2599 }
2600 if (fPAE)
2601 VarGCPtr.u.GCFlat &= ~(((RTGCPTR)1 << X86_PD_PAE_SHIFT) - 1);
2602 else
2603 VarGCPtr.u.GCFlat &= ~(((RTGCPTR)1 << X86_PD_SHIFT) - 1);
2604
2605 /*
2606 * Do the paging walk until we get to the page directory.
2607 */
2608 DBGCVAR VarCur;
2609 if (fGuest)
2610 DBGCVAR_INIT_GC_PHYS(&VarCur, cr3);
2611 else
2612 DBGCVAR_INIT_HC_PHYS(&VarCur, cr3);
2613 if (fLME)
2614 {
2615 /* Page Map Level 4 Lookup. */
2616 /* Check if it's a valid address first? */
2617 VarCur.u.u64Number &= X86_PTE_PAE_PG_MASK;
2618 VarCur.u.u64Number += (((uint64_t)VarGCPtr.u.GCFlat >> X86_PML4_SHIFT) & X86_PML4_MASK) * sizeof(X86PML4E);
2619 X86PML4E Pml4e;
2620 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pml4e, sizeof(Pml4e), &VarCur, NULL);
2621 if (VBOX_FAILURE(rc))
2622 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PML4E memory at %DV.\n", &VarCur);
2623 if (!Pml4e.n.u1Present)
2624 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory pointer table is not present for %Dv.\n", &VarGCPtr);
2625
2626 VarCur.u.u64Number = Pml4e.u & X86_PML4E_PG_MASK;
2627 Assert(fPAE);
2628 }
2629 if (fPAE)
2630 {
2631 /* Page directory pointer table. */
2632 X86PDPE Pdpe;
2633 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE) * sizeof(Pdpe);
2634 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pdpe, sizeof(Pdpe), &VarCur, NULL);
2635 if (VBOX_FAILURE(rc))
2636 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDPE memory at %DV.\n", &VarCur);
2637 if (!Pdpe.n.u1Present)
2638 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory is not present for %Dv.\n", &VarGCPtr);
2639
2640 iEntry = (VarGCPtr.u.GCFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
2641 VarPDEAddr = VarCur;
2642 VarPDEAddr.u.u64Number = Pdpe.u & X86_PDPE_PG_MASK;
2643 VarPDEAddr.u.u64Number += iEntry * sizeof(X86PDEPAE);
2644 }
2645 else
2646 {
2647 /* 32-bit legacy - CR3 == page directory. */
2648 iEntry = (VarGCPtr.u.GCFlat >> X86_PD_SHIFT) & X86_PD_MASK;
2649 VarPDEAddr = VarCur;
2650 VarPDEAddr.u.u64Number += iEntry * sizeof(X86PDE);
2651 }
2652 cEntriesMax = (PAGE_SIZE - iEntry) / cbEntry;
2653 iEntry /= cbEntry;
2654 }
2655
2656 /* adjust cEntries */
2657 cEntries = RT_MAX(1, cEntries);
2658 cEntries = RT_MIN(cEntries, cEntriesMax);
2659
2660 /*
2661 * The display loop.
2662 */
2663 DBGCCmdHlpPrintf(pCmdHlp, iEntry != ~0U ? "%DV (index %#x):\n" : "%DV:\n",
2664 &VarPDEAddr, iEntry);
2665 do
2666 {
2667 /*
2668 * Read.
2669 */
2670 X86PDEPAE Pde;
2671 Pde.u = 0;
2672 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pde, cbEntry, &VarPDEAddr, NULL);
2673 if (VBOX_FAILURE(rc))
2674 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarPDEAddr);
2675
2676 /*
2677 * Display.
2678 */
2679 if (iEntry != ~0U)
2680 {
2681 DBGCCmdHlpPrintf(pCmdHlp, "%03x %DV: ", iEntry, &VarGCPtr);
2682 iEntry++;
2683 }
2684 if (fPSE && Pde.b.u1Size)
2685 DBGCCmdHlpPrintf(pCmdHlp,
2686 fPAE
2687 ? "%016llx big phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s %s"
2688 : "%08llx big phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s %s",
2689 Pde.u,
2690 Pde.u & X86_PDE_PAE_PG_MASK,
2691 Pde.b.u1Present ? "p " : "np",
2692 Pde.b.u1Write ? "w" : "r",
2693 Pde.b.u1User ? "u" : "s",
2694 Pde.b.u1Accessed ? "a " : "na",
2695 Pde.b.u1Dirty ? "d " : "nd",
2696 Pde.b.u3Available,
2697 Pde.b.u1Global ? (fPGE ? "g" : "G") : " ",
2698 Pde.b.u1WriteThru ? "pwt" : " ",
2699 Pde.b.u1CacheDisable ? "pcd" : " ",
2700 Pde.b.u1PAT ? "pat" : "",
2701 Pde.b.u1NoExecute ? (fNXE ? "nx" : "NX") : " ");
2702 else
2703 DBGCCmdHlpPrintf(pCmdHlp,
2704 fPAE
2705 ? "%016llx 4kb phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s"
2706 : "%08llx 4kb phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s",
2707 Pde.u,
2708 Pde.u & X86_PDE_PAE_PG_MASK,
2709 Pde.n.u1Present ? "p " : "np",
2710 Pde.n.u1Write ? "w" : "r",
2711 Pde.n.u1User ? "u" : "s",
2712 Pde.n.u1Accessed ? "a " : "na",
2713 Pde.u & RT_BIT(6) ? "6 " : " ",
2714 Pde.n.u3Available,
2715 Pde.u & RT_BIT(8) ? "8" : " ",
2716 Pde.n.u1WriteThru ? "pwt" : " ",
2717 Pde.n.u1CacheDisable ? "pcd" : " ",
2718 Pde.u & RT_BIT(7) ? "7" : "",
2719 Pde.n.u1NoExecute ? (fNXE ? "nx" : "NX") : " ");
2720 if (Pde.u & UINT64_C(0x7fff000000000000))
2721 DBGCCmdHlpPrintf(pCmdHlp, " weird=%RX64", (Pde.u & UINT64_C(0x7fff000000000000)));
2722 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
2723 if (VBOX_FAILURE(rc))
2724 return rc;
2725
2726 /*
2727 * Advance.
2728 */
2729 VarPDEAddr.u.u64Number += cbEntry;
2730 if (iEntry != ~0U)
2731 VarGCPtr.u.GCFlat += 1 << (fPAE ? X86_PD_PAE_SHIFT : X86_PD_SHIFT);
2732 } while (cEntries-- > 0);
2733
2734 NOREF(pResult);
2735 return VINF_SUCCESS;
2736}
2737
2738
2739/**
2740 * The 'dpdb' command.
2741 *
2742 * @returns VBox status.
2743 * @param pCmd Pointer to the command descriptor (as registered).
2744 * @param pCmdHlp Pointer to command helper functions.
2745 * @param pVM Pointer to the current VM (if any).
2746 * @param paArgs Pointer to (readonly) array of arguments.
2747 * @param cArgs Number of arguments in the array.
2748 */
2749static DECLCALLBACK(int) dbgcCmdDumpPageDirBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2750{
2751 if (!pVM)
2752 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
2753 int rc1 = pCmdHlp->pfnExec(pCmdHlp, "dpdg %DV", &paArgs[0]);
2754 int rc2 = pCmdHlp->pfnExec(pCmdHlp, "dpdh %DV", &paArgs[0]);
2755 if (VBOX_FAILURE(rc1))
2756 return rc1;
2757 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
2758 return rc2;
2759}
2760
2761
2762/**
2763 * The 'dpg*' commands.
2764 *
2765 * @returns VBox status.
2766 * @param pCmd Pointer to the command descriptor (as registered).
2767 * @param pCmdHlp Pointer to command helper functions.
2768 * @param pVM Pointer to the current VM (if any).
2769 * @param paArgs Pointer to (readonly) array of arguments.
2770 * @param cArgs Number of arguments in the array.
2771 */
2772static DECLCALLBACK(int) dbgcCmdDumpPageTable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
2773{
2774 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2775
2776 /*
2777 * Validate input.
2778 */
2779 if ( cArgs != 1
2780 || (pCmd->pszCmd[3] == 'a' && !DBGCVAR_ISPOINTER(paArgs[0].enmType))
2781 || (pCmd->pszCmd[3] != 'a' && !(paArgs[0].enmType == DBGCVAR_TYPE_NUMBER || DBGCVAR_ISPOINTER(paArgs[0].enmType)))
2782 )
2783 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: The parser doesn't do its job properly yet.. It might help to use the '%%' operator.\n");
2784 if (!pVM)
2785 return DBGCCmdHlpPrintf(pCmdHlp, "error: No VM.\n");
2786
2787 /*
2788 * Guest or shadow page tables? Get the paging parameters.
2789 */
2790 bool fGuest = pCmd->pszCmd[3] != 'h';
2791 if (!pCmd->pszCmd[3] || pCmd->pszCmd[3] == 'a')
2792 fGuest = paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
2793 ? pDbgc->fRegCtxGuest
2794 : DBGCVAR_ISGCPOINTER(paArgs[0].enmType);
2795
2796 bool fPAE, fLME, fPSE, fPGE, fNXE;
2797 uint64_t cr3 = fGuest
2798 ? dbgcGetGuestPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE)
2799 : dbgcGetShadowPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE);
2800 const unsigned cbEntry = fPAE ? sizeof(X86PTEPAE) : sizeof(X86PTE);
2801
2802 /*
2803 * Locate the PTE to start displaying at.
2804 *
2805 * The 'dpta' command takes the address of a PTE, while the others are guest
2806 * virtual address which PTEs should be displayed. So, 'pdta' is rather simple
2807 * while the others require us to do all the tedious walking thru the paging
2808 * hierarchy to find the intended PTE.
2809 */
2810 unsigned iEntry = ~0U; /* The page table index. ~0U for 'dpta'. */
2811 DBGCVAR VarGCPtr; /* The GC address corresponding to the current PTE (iEntry != ~0U). */
2812 DBGCVAR VarPTEAddr; /* The address of the current PTE. */
2813 unsigned cEntries; /* The number of entries to display. */
2814 unsigned cEntriesMax; /* The max number of entries to display. */
2815 int rc;
2816 if (pCmd->pszCmd[3] == 'a')
2817 {
2818 VarPTEAddr = paArgs[0];
2819 switch (VarPTEAddr.enmRangeType)
2820 {
2821 case DBGCVAR_RANGE_BYTES: cEntries = VarPTEAddr.u64Range / cbEntry; break;
2822 case DBGCVAR_RANGE_ELEMENTS: cEntries = VarPTEAddr.u64Range; break;
2823 default: cEntries = 10; break;
2824 }
2825 cEntriesMax = PAGE_SIZE / cbEntry;
2826 }
2827 else
2828 {
2829 /*
2830 * Determin the range.
2831 */
2832 switch (paArgs[0].enmRangeType)
2833 {
2834 case DBGCVAR_RANGE_BYTES: cEntries = paArgs[0].u64Range / PAGE_SIZE; break;
2835 case DBGCVAR_RANGE_ELEMENTS: cEntries = paArgs[0].u64Range; break;
2836 default: cEntries = 10; break;
2837 }
2838
2839 /*
2840 * Normalize the input address, it must be a flat GC address.
2841 */
2842 rc = pCmdHlp->pfnEval(pCmdHlp, &VarGCPtr, "%%(%Dv)", &paArgs[0]);
2843 if (VBOX_FAILURE(rc))
2844 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "%%(%Dv)", &paArgs[0]);
2845 if (VarGCPtr.enmType == DBGCVAR_TYPE_HC_FLAT)
2846 {
2847 VarGCPtr.u.GCFlat = (uintptr_t)VarGCPtr.u.pvHCFlat;
2848 VarGCPtr.enmType = DBGCVAR_TYPE_GC_FLAT;
2849 }
2850 VarGCPtr.u.GCFlat &= ~(RTGCPTR)PAGE_OFFSET_MASK;
2851
2852 /*
2853 * Do the paging walk until we get to the page table.
2854 */
2855 DBGCVAR VarCur;
2856 if (fGuest)
2857 DBGCVAR_INIT_GC_PHYS(&VarCur, cr3);
2858 else
2859 DBGCVAR_INIT_HC_PHYS(&VarCur, cr3);
2860 if (fLME)
2861 {
2862 /* Page Map Level 4 Lookup. */
2863 /* Check if it's a valid address first? */
2864 VarCur.u.u64Number &= X86_PTE_PAE_PG_MASK;
2865 VarCur.u.u64Number += (((uint64_t)VarGCPtr.u.GCFlat >> X86_PML4_SHIFT) & X86_PML4_MASK) * sizeof(X86PML4E);
2866 X86PML4E Pml4e;
2867 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pml4e, sizeof(Pml4e), &VarCur, NULL);
2868 if (VBOX_FAILURE(rc))
2869 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PML4E memory at %DV.\n", &VarCur);
2870 if (!Pml4e.n.u1Present)
2871 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory pointer table is not present for %Dv.\n", &VarGCPtr);
2872
2873 VarCur.u.u64Number = Pml4e.u & X86_PML4E_PG_MASK;
2874 Assert(fPAE);
2875 }
2876 if (fPAE)
2877 {
2878 /* Page directory pointer table. */
2879 X86PDPE Pdpe;
2880 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE) * sizeof(Pdpe);
2881 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pdpe, sizeof(Pdpe), &VarCur, NULL);
2882 if (VBOX_FAILURE(rc))
2883 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDPE memory at %DV.\n", &VarCur);
2884 if (!Pdpe.n.u1Present)
2885 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory is not present for %Dv.\n", &VarGCPtr);
2886
2887 VarCur.u.u64Number = Pdpe.u & X86_PDPE_PG_MASK;
2888
2889 /* Page directory (PAE). */
2890 X86PDEPAE Pde;
2891 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK) * sizeof(Pde);
2892 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pde, sizeof(Pde), &VarCur, NULL);
2893 if (VBOX_FAILURE(rc))
2894 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarCur);
2895 if (!Pde.n.u1Present)
2896 return DBGCCmdHlpPrintf(pCmdHlp, "Page table is not present for %Dv.\n", &VarGCPtr);
2897 if (fPSE && Pde.n.u1Size)
2898 return pCmdHlp->pfnExec(pCmdHlp, "dpd%s %Dv L3", &pCmd->pszCmd[3], &VarGCPtr);
2899
2900 iEntry = (VarGCPtr.u.GCFlat >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK;
2901 VarPTEAddr = VarCur;
2902 VarPTEAddr.u.u64Number = Pde.u & X86_PDE_PAE_PG_MASK;
2903 VarPTEAddr.u.u64Number += iEntry * sizeof(X86PTEPAE);
2904 }
2905 else
2906 {
2907 /* Page directory (legacy). */
2908 X86PDE Pde;
2909 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PD_SHIFT) & X86_PD_MASK) * sizeof(Pde);
2910 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pde, sizeof(Pde), &VarCur, NULL);
2911 if (VBOX_FAILURE(rc))
2912 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarCur);
2913 if (!Pde.n.u1Present)
2914 return DBGCCmdHlpPrintf(pCmdHlp, "Page table is not present for %Dv.\n", &VarGCPtr);
2915 if (fPSE && Pde.n.u1Size)
2916 return pCmdHlp->pfnExec(pCmdHlp, "dpd%s %Dv L3", &pCmd->pszCmd[3], &VarGCPtr);
2917
2918 iEntry = (VarGCPtr.u.GCFlat >> X86_PT_SHIFT) & X86_PT_MASK;
2919 VarPTEAddr = VarCur;
2920 VarPTEAddr.u.u64Number = Pde.u & X86_PDE_PG_MASK;
2921 VarPTEAddr.u.u64Number += iEntry * sizeof(X86PTE);
2922 }
2923 cEntriesMax = (PAGE_SIZE - iEntry) / cbEntry;
2924 iEntry /= cbEntry;
2925 }
2926
2927 /* adjust cEntries */
2928 cEntries = RT_MAX(1, cEntries);
2929 cEntries = RT_MIN(cEntries, cEntriesMax);
2930
2931 /*
2932 * The display loop.
2933 */
2934 DBGCCmdHlpPrintf(pCmdHlp, iEntry != ~0U ? "%DV (base %DV / index %#x):\n" : "%DV:\n",
2935 &VarPTEAddr, &VarGCPtr, iEntry);
2936 do
2937 {
2938 /*
2939 * Read.
2940 */
2941 X86PTEPAE Pte;
2942 Pte.u = 0;
2943 rc = pCmdHlp->pfnMemRead(pCmdHlp, pVM, &Pte, cbEntry, &VarPTEAddr, NULL);
2944 if (VBOX_FAILURE(rc))
2945 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PTE memory at %DV.\n", &VarPTEAddr);
2946
2947 /*
2948 * Display.
2949 */
2950 if (iEntry != ~0U)
2951 {
2952 DBGCCmdHlpPrintf(pCmdHlp, "%03x %DV: ", iEntry, &VarGCPtr);
2953 iEntry++;
2954 }
2955 DBGCCmdHlpPrintf(pCmdHlp,
2956 fPAE
2957 ? "%016llx 4kb phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s %s"
2958 : "%08llx 4kb phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s %s",
2959 Pte.u,
2960 Pte.u & X86_PTE_PAE_PG_MASK,
2961 Pte.n.u1Present ? "p " : "np",
2962 Pte.n.u1Write ? "w" : "r",
2963 Pte.n.u1User ? "u" : "s",
2964 Pte.n.u1Accessed ? "a " : "na",
2965 Pte.n.u1Dirty ? "d " : "nd",
2966 Pte.n.u3Available,
2967 Pte.n.u1Global ? (fPGE ? "g" : "G") : " ",
2968 Pte.n.u1WriteThru ? "pwt" : " ",
2969 Pte.n.u1CacheDisable ? "pcd" : " ",
2970 Pte.n.u1PAT ? "pat" : " ",
2971 Pte.n.u1NoExecute ? (fNXE ? "nx" : "NX") : " "
2972 );
2973 if (Pte.u & UINT64_C(0x7fff000000000000))
2974 DBGCCmdHlpPrintf(pCmdHlp, " weird=%RX64", (Pte.u & UINT64_C(0x7fff000000000000)));
2975 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
2976 if (VBOX_FAILURE(rc))
2977 return rc;
2978
2979 /*
2980 * Advance.
2981 */
2982 VarPTEAddr.u.u64Number += cbEntry;
2983 if (iEntry != ~0U)
2984 VarGCPtr.u.GCFlat += PAGE_SIZE;
2985 } while (cEntries-- > 0);
2986
2987 NOREF(pResult);
2988 return VINF_SUCCESS;
2989}
2990
2991
2992/**
2993 * The 'dptb' command.
2994 *
2995 * @returns VBox status.
2996 * @param pCmd Pointer to the command descriptor (as registered).
2997 * @param pCmdHlp Pointer to command helper functions.
2998 * @param pVM Pointer to the current VM (if any).
2999 * @param paArgs Pointer to (readonly) array of arguments.
3000 * @param cArgs Number of arguments in the array.
3001 */
3002static DECLCALLBACK(int) dbgcCmdDumpPageTableBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
3003{
3004 if (!pVM)
3005 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
3006 int rc1 = pCmdHlp->pfnExec(pCmdHlp, "dptg %DV", &paArgs[0]);
3007 int rc2 = pCmdHlp->pfnExec(pCmdHlp, "dpth %DV", &paArgs[0]);
3008 if (VBOX_FAILURE(rc1))
3009 return rc1;
3010 NOREF(pCmd); NOREF(cArgs); NOREF(pResult);
3011 return rc2;
3012}
3013
3014
3015/**
3016 * The 'dt' command.
3017 *
3018 * @returns VBox status.
3019 * @param pCmd Pointer to the command descriptor (as registered).
3020 * @param pCmdHlp Pointer to command helper functions.
3021 * @param pVM Pointer to the current VM (if any).
3022 * @param paArgs Pointer to (readonly) array of arguments.
3023 * @param cArgs Number of arguments in the array.
3024 */
3025static DECLCALLBACK(int) dbgcCmdDumpTSS(PCDBGCCMD /*pCmd*/, PDBGCCMDHLP pCmdHlp, PVM /*pVM*/, PCDBGCVAR /*paArgs*/, unsigned /*cArgs*/, PDBGCVAR /*pResult*/)
3026{
3027 /*
3028 * We can get a TSS selector (number), a far pointer using a TSS selector, or some kind of TSS pointer.
3029 */
3030
3031 /** @todo */
3032 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "dt is not implemented yet, feel free to do it. \n");
3033}
3034
3035
3036/**
3037 * The 'm' command.
3038 *
3039 * @returns VBox status.
3040 * @param pCmd Pointer to the command descriptor (as registered).
3041 * @param pCmdHlp Pointer to command helper functions.
3042 * @param pVM Pointer to the current VM (if any).
3043 * @param paArgs Pointer to (readonly) array of arguments.
3044 * @param cArgs Number of arguments in the array.
3045 */
3046static DECLCALLBACK(int) dbgcCmdMemoryInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
3047{
3048 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Address: %DV\n", &paArgs[0]);
3049 if (!pVM)
3050 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
3051 int rc1 = pCmdHlp->pfnExec(pCmdHlp, "dpdg %DV", &paArgs[0]);
3052 int rc2 = pCmdHlp->pfnExec(pCmdHlp, "dpdh %DV", &paArgs[0]);
3053 int rc3 = pCmdHlp->pfnExec(pCmdHlp, "dptg %DV", &paArgs[0]);
3054 int rc4 = pCmdHlp->pfnExec(pCmdHlp, "dpth %DV", &paArgs[0]);
3055 if (VBOX_FAILURE(rc1))
3056 return rc1;
3057 if (VBOX_FAILURE(rc2))
3058 return rc2;
3059 if (VBOX_FAILURE(rc3))
3060 return rc3;
3061 NOREF(pCmd); NOREF(cArgs); NOREF(pResult);
3062 return rc4;
3063}
3064
3065
3066/**
3067 * Converts one or more variables into a byte buffer for a
3068 * given unit size.
3069 *
3070 * @returns VBox status codes:
3071 * @retval VERR_TOO_MUCH_DATA if the buffer is too small, bitched.
3072 * @retval VERR_INTERNAL_ERROR on bad variable type, bitched.
3073 * @retval VINF_SUCCESS on success.
3074 *
3075 * @param pvBuf The buffer to convert into.
3076 * @param pcbBuf The buffer size on input. The size of the result on output.
3077 * @param cbUnit The unit size to apply when converting.
3078 * The high bit is used to indicate unicode string.
3079 * @param paVars The array of variables to convert.
3080 * @param cVars The number of variables.
3081 */
3082int dbgcVarsToBytes(PDBGCCMDHLP pCmdHlp, void *pvBuf, uint32_t *pcbBuf, size_t cbUnit, PCDBGCVAR paVars, unsigned cVars)
3083{
3084 union
3085 {
3086 uint8_t *pu8;
3087 uint16_t *pu16;
3088 uint32_t *pu32;
3089 uint64_t *pu64;
3090 } u, uEnd;
3091 u.pu8 = (uint8_t *)pvBuf;
3092 uEnd.pu8 = u.pu8 + *pcbBuf;
3093
3094 unsigned i;
3095 for (i = 0; i < cVars && u.pu8 < uEnd.pu8; i++)
3096 {
3097 switch (paVars[i].enmType)
3098 {
3099 case DBGCVAR_TYPE_GC_FAR:
3100 case DBGCVAR_TYPE_HC_FAR:
3101 case DBGCVAR_TYPE_GC_FLAT:
3102 case DBGCVAR_TYPE_GC_PHYS:
3103 case DBGCVAR_TYPE_HC_FLAT:
3104 case DBGCVAR_TYPE_HC_PHYS:
3105 case DBGCVAR_TYPE_NUMBER:
3106 {
3107 uint64_t u64 = paVars[i].u.u64Number;
3108 switch (cbUnit & 0x1f)
3109 {
3110 case 1:
3111 do
3112 {
3113 *u.pu8++ = u64;
3114 u64 >>= 8;
3115 } while (u64);
3116 break;
3117 case 2:
3118 do
3119 {
3120 *u.pu16++ = u64;
3121 u64 >>= 16;
3122 } while (u64);
3123 break;
3124 case 4:
3125 *u.pu32++ = u64;
3126 u64 >>= 32;
3127 if (u64)
3128 *u.pu32++ = u64;
3129 break;
3130 case 8:
3131 *u.pu64++ = u64;
3132 break;
3133 }
3134 break;
3135 }
3136
3137 case DBGCVAR_TYPE_STRING:
3138 case DBGCVAR_TYPE_SYMBOL:
3139 {
3140 const char *psz = paVars[i].u.pszString;
3141 size_t cbString = strlen(psz);
3142 if (cbUnit & RT_BIT_32(31))
3143 {
3144 /* Explode char to unit. */
3145 if (cbString > (uintptr_t)(uEnd.pu8 - u.pu8) * (cbUnit & 0x1f))
3146 {
3147 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_TOO_MUCH_DATA, "Max %d bytes.\n", uEnd.pu8 - (uint8_t *)pvBuf);
3148 return VERR_TOO_MUCH_DATA;
3149 }
3150 while (*psz)
3151 {
3152 switch (cbUnit & 0x1f)
3153 {
3154 case 1: *u.pu8++ = *psz; break;
3155 case 2: *u.pu16++ = *psz; break;
3156 case 4: *u.pu32++ = *psz; break;
3157 case 8: *u.pu64++ = *psz; break;
3158 }
3159 psz++;
3160 }
3161 }
3162 else
3163 {
3164 /* Raw copy with zero padding if the size isn't aligned. */
3165 if (cbString > (uintptr_t)(uEnd.pu8 - u.pu8))
3166 {
3167 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_TOO_MUCH_DATA, "Max %d bytes.\n", uEnd.pu8 - (uint8_t *)pvBuf);
3168 return VERR_TOO_MUCH_DATA;
3169 }
3170
3171 size_t cbCopy = cbString & ~(cbUnit - 1);
3172 memcpy(u.pu8, psz, cbCopy);
3173 u.pu8 += cbCopy;
3174 psz += cbCopy;
3175
3176 size_t cbReminder = cbString & (cbUnit - 1);
3177 if (cbReminder)
3178 {
3179 memcpy(u.pu8, psz, cbString & (cbUnit - 1));
3180 memset(u.pu8 + cbReminder, 0, cbUnit - cbReminder);
3181 u.pu8 += cbUnit;
3182 }
3183 }
3184 break;
3185 }
3186
3187 default:
3188 *pcbBuf = u.pu8 - (uint8_t *)pvBuf;
3189 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_INTERNAL_ERROR,
3190 "i=%d enmType=%d\n", i, paVars[i].enmType);
3191 return VERR_INTERNAL_ERROR;
3192 }
3193 }
3194 *pcbBuf = u.pu8 - (uint8_t *)pvBuf;
3195 if (i != cVars)
3196 {
3197 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_TOO_MUCH_DATA, "Max %d bytes.\n", uEnd.pu8 - (uint8_t *)pvBuf);
3198 return VERR_TOO_MUCH_DATA;
3199 }
3200 return VINF_SUCCESS;
3201}
3202
3203
3204/**
3205 * Executes the search.
3206 *
3207 * @returns VBox status code.
3208 * @param pCmdHlp The command helpers.
3209 * @param pVM The VM handle.
3210 * @param pAddress The address to start searching from. (undefined on output)
3211 * @param cbRange The address range to search. Must not wrap.
3212 * @param pabBytes The byte pattern to search for.
3213 * @param cbBytes The size of the pattern.
3214 * @param cbUnit The search unit.
3215 * @param cMaxHits The max number of hits.
3216 * @param pResult Where to store the result if it's a function invocation.
3217 */
3218static int dbgcCmdWorkerSearchMemDoIt(PDBGCCMDHLP pCmdHlp, PVM pVM, PDBGFADDRESS pAddress, RTGCUINTPTR cbRange,
3219 const uint8_t *pabBytes, uint32_t cbBytes,
3220 uint32_t cbUnit, uint64_t cMaxHits, PDBGCVAR pResult)
3221{
3222 /*
3223 * Do the search.
3224 */
3225 uint64_t cHits = 0;
3226 for (;;)
3227 {
3228 /* search */
3229 DBGFADDRESS HitAddress;
3230 int rc = DBGFR3MemScan(pVM, pAddress, cbRange, pabBytes, cbBytes, &HitAddress);
3231 if (RT_FAILURE(rc))
3232 {
3233 if (rc != VERR_DBGF_MEM_NOT_FOUND)
3234 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3MemScan\n");
3235
3236 /* update the current address so we can save it (later). */
3237 pAddress->off += cbRange;
3238 pAddress->FlatPtr += cbRange;
3239 cbRange = 0;
3240 break;
3241 }
3242
3243 /* report result */
3244 DBGCVAR VarCur;
3245 dbgcVarInit(&VarCur);
3246 dbgcVarSetDbgfAddr(&VarCur, &HitAddress);
3247 if (!pResult)
3248 pCmdHlp->pfnExec(pCmdHlp, "db %DV LB 10", &VarCur);
3249 else
3250 dbgcVarSetDbgfAddr(pResult, &HitAddress);
3251
3252 /* advance */
3253 cbRange -= HitAddress.FlatPtr - pAddress->FlatPtr;
3254 *pAddress = HitAddress;
3255 pAddress->FlatPtr += cbBytes;
3256 pAddress->off += cbBytes;
3257 if (cbRange <= cbBytes)
3258 {
3259 cbRange = 0;
3260 break;
3261 }
3262 cbRange -= cbBytes;
3263
3264 if (++cHits >= cMaxHits)
3265 {
3266 /// @todo save the search.
3267 break;
3268 }
3269 }
3270
3271 /*
3272 * Save the search so we can resume it...
3273 */
3274 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3275 if (pDbgc->abSearch != pabBytes)
3276 {
3277 memcpy(pDbgc->abSearch, pabBytes, cbBytes);
3278 pDbgc->cbSearch = cbBytes;
3279 pDbgc->cbSearchUnit = cbUnit;
3280 }
3281 pDbgc->cMaxSearchHits = cMaxHits;
3282 pDbgc->SearchAddr = *pAddress;
3283 pDbgc->cbSearchRange = cbRange;
3284
3285 return cHits ? VINF_SUCCESS : VERR_DBGC_COMMAND_FAILED;
3286}
3287
3288
3289/**
3290 * Resumes the previous search.
3291 *
3292 * @returns VBox status code.
3293 * @param pCmdHlp Pointer to the command helper functions.
3294 * @param pVM Pointer to the current VM (if any).
3295 * @param pResult Where to store the result of a function invocation.
3296 */
3297static int dbgcCmdWorkerSearchMemResume(PDBGCCMDHLP pCmdHlp, PVM pVM, PDBGCVAR pResult)
3298{
3299 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3300
3301 /*
3302 * Make sure there is a previous command.
3303 */
3304 if (!pDbgc->cbSearch)
3305 {
3306 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Error: No previous search\n");
3307 return VERR_DBGC_COMMAND_FAILED;
3308 }
3309
3310 /*
3311 * Make range and address adjustments.
3312 */
3313 DBGFADDRESS Address = pDbgc->SearchAddr;
3314 if (Address.FlatPtr == ~(RTGCUINTPTR)0)
3315 {
3316 Address.FlatPtr -= Address.off;
3317 Address.off = 0;
3318 }
3319
3320 RTGCUINTPTR cbRange = pDbgc->cbSearchRange;
3321 if (!cbRange)
3322 cbRange = ~(RTGCUINTPTR)0;
3323 if (Address.FlatPtr + cbRange < pDbgc->SearchAddr.FlatPtr)
3324 cbRange = ~(RTGCUINTPTR)0 - pDbgc->SearchAddr.FlatPtr + !!pDbgc->SearchAddr.FlatPtr;
3325
3326 return dbgcCmdWorkerSearchMemDoIt(pCmdHlp, pVM, &Address, cbRange, pDbgc->abSearch, pDbgc->cbSearch,
3327 pDbgc->cbSearchUnit, pDbgc->cMaxSearchHits, pResult);
3328}
3329
3330
3331/**
3332 * Search memory, worker for the 's' and 's?' functions.
3333 *
3334 * @returns VBox status.
3335 * @param pCmdHlp Pointer to the command helper functions.
3336 * @param pVM Pointer to the current VM (if any).
3337 * @param pAddress Where to start searching. If no range, search till end of address space.
3338 * @param cMaxHits The maximum number of hits.
3339 * @param chType The search type.
3340 * @param paPatArgs The pattern variable array.
3341 * @param cPatArgs Number of pattern variables.
3342 * @param pResult Where to store the result of a function invocation.
3343 */
3344static int dbgcCmdWorkerSearchMem(PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pAddress, uint64_t cMaxHits, char chType,
3345 PCDBGCVAR paPatArgs, unsigned cPatArgs, PDBGCVAR pResult)
3346{
3347 dbgcVarSetGCFlat(pResult, 0);
3348
3349 /*
3350 * Convert the search pattern into bytes and DBGFR3MemScan can deal with.
3351 */
3352 uint32_t cbUnit;
3353 switch (chType)
3354 {
3355 case 'a':
3356 case 'b': cbUnit = 1; break;
3357 case 'u': cbUnit = 2 | RT_BIT_32(31); break;
3358 case 'w': cbUnit = 2; break;
3359 case 'd': cbUnit = 4; break;
3360 case 'q': cbUnit = 8; break;
3361 default:
3362 return pCmdHlp->pfnVBoxError(pCmdHlp, VERR_INVALID_PARAMETER, "chType=%c\n", chType);
3363 }
3364 uint8_t abBytes[RT_SIZEOFMEMB(DBGC, abSearch)];
3365 uint32_t cbBytes = sizeof(abBytes);
3366 int rc = dbgcVarsToBytes(pCmdHlp, abBytes, &cbBytes, cbUnit, paPatArgs, cPatArgs);
3367 if (RT_FAILURE(rc))
3368 return VERR_DBGC_COMMAND_FAILED;
3369
3370 /*
3371 * Make DBGF address and fix the range.
3372 */
3373 DBGFADDRESS Address;
3374 rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, pAddress, &Address);
3375 if (RT_FAILURE(rc))
3376 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "VarToDbgfAddr(,%Dv,)\n", pAddress);
3377
3378 RTGCUINTPTR cbRange;
3379 switch (pAddress->enmRangeType)
3380 {
3381 case DBGCVAR_RANGE_BYTES:
3382 cbRange = pAddress->u64Range;
3383 if (cbRange != pAddress->u64Range)
3384 cbRange = ~(RTGCUINTPTR)0;
3385 break;
3386
3387 case DBGCVAR_RANGE_ELEMENTS:
3388 cbRange = (RTGCUINTPTR)(pAddress->u64Range * cbUnit);
3389 if ( cbRange != pAddress->u64Range * cbUnit
3390 || cbRange < pAddress->u64Range)
3391 cbRange = ~(RTGCUINTPTR)0;
3392 break;
3393
3394 default:
3395 cbRange = ~(RTGCUINTPTR)0;
3396 break;
3397 }
3398 if (Address.FlatPtr + cbRange < Address.FlatPtr)
3399 cbRange = ~(RTGCUINTPTR)0 - Address.FlatPtr + !!Address.FlatPtr;
3400
3401 /*
3402 * Ok, do it.
3403 */
3404 return dbgcCmdWorkerSearchMemDoIt(pCmdHlp, pVM, &Address, cbRange, abBytes, cbBytes, cbUnit, cMaxHits, pResult);
3405}
3406
3407
3408/**
3409 * The 's' command.
3410 *
3411 * @returns VBox status.
3412 * @param pCmd Pointer to the command descriptor (as registered).
3413 * @param pCmdHlp Pointer to command helper functions.
3414 * @param pVM Pointer to the current VM (if any).
3415 * @param paArgs Pointer to (readonly) array of arguments.
3416 * @param cArgs Number of arguments in the array.
3417 */
3418static DECLCALLBACK(int) dbgcCmdSearchMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
3419{
3420 /* check that the parser did what it's supposed to do. */
3421 //if ( cArgs <= 2
3422 // && paArgs[0].enmType != DBGCVAR_TYPE_STRING)
3423 // return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "parser error\n");
3424
3425 /*
3426 * Repeate previous search?
3427 */
3428 if (cArgs == 0)
3429 return dbgcCmdWorkerSearchMemResume(pCmdHlp, pVM, pResult);
3430
3431 /*
3432 * Parse arguments.
3433 */
3434
3435 return -1;
3436}
3437
3438
3439/**
3440 * The 's?' command.
3441 *
3442 * @returns VBox status.
3443 * @param pCmd Pointer to the command descriptor (as registered).
3444 * @param pCmdHlp Pointer to command helper functions.
3445 * @param pVM Pointer to the current VM (if any).
3446 * @param paArgs Pointer to (readonly) array of arguments.
3447 * @param cArgs Number of arguments in the array.
3448 */
3449static DECLCALLBACK(int) dbgcCmdSearchMemType(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
3450{
3451 /* check that the parser did what it's supposed to do. */
3452 if ( cArgs < 2
3453 || !DBGCVAR_ISGCPOINTER(paArgs[0].enmType))
3454 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "parser error\n");
3455 return dbgcCmdWorkerSearchMem(pCmdHlp, pVM, &paArgs[0], pResult ? 1 : 25, pCmd->pszCmd[1], paArgs + 1, cArgs - 1, pResult);
3456}
3457
3458
3459/**
3460 * List near symbol.
3461 *
3462 * @returns VBox status code.
3463 * @param pCmdHlp Pointer to command helper functions.
3464 * @param pVM Pointer to the current VM (if any).
3465 * @param pArg Pointer to the address or symbol to lookup.
3466 */
3467static int dbgcDoListNear(PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArg, PDBGCVAR pResult)
3468{
3469 dbgcVarSetGCFlat(pResult, 0);
3470
3471 DBGFSYMBOL Symbol;
3472 int rc;
3473 if (pArg->enmType == DBGCVAR_TYPE_SYMBOL)
3474 {
3475 /*
3476 * Lookup the symbol address.
3477 */
3478 rc = DBGFR3SymbolByName(pVM, pArg->u.pszString, &Symbol);
3479 if (VBOX_FAILURE(rc))
3480 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3SymbolByName(, %s,)\n", pArg->u.pszString);
3481
3482 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%VGv %s\n", (RTGCUINTPTR)Symbol.Value, Symbol.szName); /** @todo remove the RTUINGCPTR cast once DBGF got correct interfaces! */
3483 dbgcVarSetGCFlatByteRange(pResult, Symbol.Value, Symbol.cb);
3484 }
3485 else
3486 {
3487 /*
3488 * Convert it to a flat GC address and lookup that address.
3489 */
3490 DBGCVAR AddrVar;
3491 rc = pCmdHlp->pfnEval(pCmdHlp, &AddrVar, "%%(%DV)", pArg);
3492 if (VBOX_FAILURE(rc))
3493 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "%%(%DV)\n", pArg);
3494
3495 dbgcVarSetVar(pResult, &AddrVar);
3496
3497 RTGCINTPTR offDisp = 0;
3498 rc = DBGFR3SymbolByAddr(pVM, AddrVar.u.GCFlat, &offDisp, &Symbol);
3499 if (VBOX_FAILURE(rc))
3500 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3SymbolByAddr(, %VGv,,)\n", AddrVar.u.GCFlat);
3501
3502 if (!offDisp)
3503 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%DV %s", &AddrVar, Symbol.szName);
3504 else if (offDisp > 0)
3505 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%DV %s + %RGv", &AddrVar, Symbol.szName, offDisp);
3506 else
3507 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%DV %s - %RGv", &AddrVar, Symbol.szName, -offDisp);
3508 if ((RTGCINTPTR)Symbol.cb > -offDisp)
3509 {
3510 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " LB %RGv\n", Symbol.cb + offDisp);
3511 dbgcVarSetByteRange(pResult, Symbol.cb + offDisp);
3512 }
3513 else
3514 {
3515 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\n");
3516 dbgcVarSetNoRange(pResult);
3517 }
3518 }
3519
3520 return rc;
3521}
3522
3523
3524/**
3525 * The 'ln' (listnear) command.
3526 *
3527 * @returns VBox status.
3528 * @param pCmd Pointer to the command descriptor (as registered).
3529 * @param pCmdHlp Pointer to command helper functions.
3530 * @param pVM Pointer to the current VM (if any).
3531 * @param paArgs Pointer to (readonly) array of arguments.
3532 * @param cArgs Number of arguments in the array.
3533 */
3534static DECLCALLBACK(int) dbgcCmdListNear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
3535{
3536 dbgcVarSetGCFlat(pResult, 0);
3537 if (!cArgs)
3538 {
3539 /*
3540 * Current cs:eip symbol.
3541 */
3542 DBGCVAR AddrVar;
3543 int rc = pCmdHlp->pfnEval(pCmdHlp, &AddrVar, "%%(cs:eip)");
3544 if (VBOX_FAILURE(rc))
3545 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "%%(cs:eip)\n");
3546 return dbgcDoListNear(pCmdHlp, pVM, &AddrVar, pResult);
3547 }
3548
3549 /*
3550 * Iterate arguments.
3551 */
3552 for (unsigned iArg = 0; iArg < cArgs; iArg++)
3553 {
3554 int rc = dbgcDoListNear(pCmdHlp, pVM, &paArgs[iArg], pResult);
3555 if (VBOX_FAILURE(rc))
3556 return rc;
3557 }
3558
3559 NOREF(pCmd); NOREF(pResult);
3560 return VINF_SUCCESS;
3561}
3562
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