VirtualBox

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

Last change on this file since 89896 was 89896, checked in by vboxsync, 3 years ago

Debugger: Need to account for the segment offset when dumping symbols with 'x' or there will be offset errors ifthe module contains multiple segments because SymInfo.Value holds the offset from the beginning of the segment and not the offset from the beginning of the module as assumed before

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 312.0 KB
Line 
1/* $Id: DBGCEmulateCodeView.cpp 89896 2021-06-24 18:26:06Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, CodeView / WinDbg Emulation.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGC
23#include <VBox/dbg.h>
24#include <VBox/vmm/dbgf.h>
25#include <VBox/vmm/dbgfflowtrace.h>
26#include <VBox/vmm/pgm.h>
27#include <VBox/vmm/cpum.h>
28#include <VBox/dis.h>
29#include <VBox/param.h>
30#include <VBox/err.h>
31#include <VBox/log.h>
32
33#include <iprt/asm.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36#include <iprt/assert.h>
37#include <iprt/ctype.h>
38#include <iprt/time.h>
39
40#include <stdlib.h>
41#include <stdio.h>
42
43#include "DBGCInternal.h"
44
45
46/*********************************************************************************************************************************
47* Internal Functions *
48*********************************************************************************************************************************/
49static FNDBGCCMD dbgcCmdBrkAccess;
50static FNDBGCCMD dbgcCmdBrkClear;
51static FNDBGCCMD dbgcCmdBrkDisable;
52static FNDBGCCMD dbgcCmdBrkEnable;
53static FNDBGCCMD dbgcCmdBrkList;
54static FNDBGCCMD dbgcCmdBrkSet;
55static FNDBGCCMD dbgcCmdBrkREM;
56static FNDBGCCMD dbgcCmdDumpMem;
57static FNDBGCCMD dbgcCmdDumpDT;
58static FNDBGCCMD dbgcCmdDumpIDT;
59static FNDBGCCMD dbgcCmdDumpPageDir;
60static FNDBGCCMD dbgcCmdDumpPageDirBoth;
61static FNDBGCCMD dbgcCmdDumpPageHierarchy;
62static FNDBGCCMD dbgcCmdDumpPageTable;
63static FNDBGCCMD dbgcCmdDumpPageTableBoth;
64static FNDBGCCMD dbgcCmdDumpTSS;
65static FNDBGCCMD dbgcCmdDumpTypeInfo;
66static FNDBGCCMD dbgcCmdDumpTypedVal;
67static FNDBGCCMD dbgcCmdEditMem;
68static FNDBGCCMD dbgcCmdGo;
69static FNDBGCCMD dbgcCmdGoUp;
70static FNDBGCCMD dbgcCmdListModules;
71static FNDBGCCMD dbgcCmdListNear;
72static FNDBGCCMD dbgcCmdListSource;
73static FNDBGCCMD dbgcCmdListSymbols;
74static FNDBGCCMD dbgcCmdMemoryInfo;
75static FNDBGCCMD dbgcCmdReg;
76static FNDBGCCMD dbgcCmdRegGuest;
77static FNDBGCCMD dbgcCmdRegTerse;
78static FNDBGCCMD dbgcCmdSearchMem;
79static FNDBGCCMD dbgcCmdSearchMemType;
80static FNDBGCCMD dbgcCmdStepTrace;
81static FNDBGCCMD dbgcCmdStepTraceTo;
82static FNDBGCCMD dbgcCmdStepTraceToggle;
83static FNDBGCCMD dbgcCmdEventCtrl;
84static FNDBGCCMD dbgcCmdEventCtrlList;
85static FNDBGCCMD dbgcCmdEventCtrlReset;
86static FNDBGCCMD dbgcCmdStack;
87static FNDBGCCMD dbgcCmdUnassemble;
88static FNDBGCCMD dbgcCmdUnassembleCfg;
89static FNDBGCCMD dbgcCmdTraceFlowClear;
90static FNDBGCCMD dbgcCmdTraceFlowDisable;
91static FNDBGCCMD dbgcCmdTraceFlowEnable;
92static FNDBGCCMD dbgcCmdTraceFlowPrint;
93static FNDBGCCMD dbgcCmdTraceFlowReset;
94
95
96/*********************************************************************************************************************************
97* Global Variables *
98*********************************************************************************************************************************/
99/** 'ba' arguments. */
100static const DBGCVARDESC g_aArgBrkAcc[] =
101{
102 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
103 { 1, 1, DBGCVAR_CAT_STRING, 0, "access", "The access type: x=execute, rw=read/write (alias r), w=write, i=not implemented." },
104 { 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." },
105 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
106 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
107 { 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)" },
108 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
109};
110
111
112/** 'bc', 'bd', 'be' arguments. */
113static const DBGCVARDESC g_aArgBrks[] =
114{
115 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
116 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "#bp", "Breakpoint number." },
117 { 0, 1, DBGCVAR_CAT_STRING, 0, "all", "All breakpoints." },
118};
119
120
121/** 'bp' arguments. */
122static const DBGCVARDESC g_aArgBrkSet[] =
123{
124 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
125 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
126 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
127 { 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)" },
128 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
129};
130
131
132/** 'br' arguments. */
133static const DBGCVARDESC g_aArgBrkREM[] =
134{
135 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
136 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
137 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
138 { 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)" },
139 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
140};
141
142
143/** 'd?' arguments. */
144static const DBGCVARDESC g_aArgDumpMem[] =
145{
146 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
147 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start dumping memory." },
148};
149
150
151/** 'dg', 'dga', 'dl', 'dla' arguments. */
152static const DBGCVARDESC g_aArgDumpDT[] =
153{
154 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
155 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "sel", "Selector or selector range." },
156 { 0, ~0U, DBGCVAR_CAT_POINTER, 0, "address", "Far address which selector should be dumped." },
157};
158
159
160/** 'di', 'dia' arguments. */
161static const DBGCVARDESC g_aArgDumpIDT[] =
162{
163 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
164 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "int", "The interrupt vector or interrupt vector range." },
165};
166
167
168/** 'dpd*' arguments. */
169static const DBGCVARDESC g_aArgDumpPD[] =
170{
171 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
172 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "index", "Index into the page directory." },
173 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address which page directory entry to start dumping from. Range is applied to the page directory." },
174};
175
176
177/** 'dpda' arguments. */
178static const DBGCVARDESC g_aArgDumpPDAddr[] =
179{
180 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
181 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address of the page directory entry to start dumping from." },
182};
183
184
185/** 'dph*' arguments. */
186static const DBGCVARDESC g_aArgDumpPH[] =
187{
188 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
189 { 0, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "Where in the address space to start dumping and for how long (range). The default address/range will be used if omitted." },
190 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "cr3", "The CR3 value to use. The current CR3 of the context will be used if omitted." },
191 { 0, 1, DBGCVAR_CAT_STRING, DBGCVD_FLAGS_DEP_PREV, "mode", "The paging mode: legacy, pse, pae, long, ept. Append '-np' for nested paging and '-nx' for no-execute. The current mode will be used if omitted." },
192};
193
194
195/** 'dpt?' arguments. */
196static const DBGCVARDESC g_aArgDumpPT[] =
197{
198 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
199 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address which page directory entry to start dumping from." },
200};
201
202
203/** 'dpta' arguments. */
204static const DBGCVARDESC g_aArgDumpPTAddr[] =
205{
206 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
207 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address of the page table entry to start dumping from." },
208};
209
210
211/** 'dt' arguments. */
212static const DBGCVARDESC g_aArgDumpTSS[] =
213{
214 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
215 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "tss", "TSS selector number." },
216 { 0, 1, DBGCVAR_CAT_POINTER, 0, "tss:ign|addr", "TSS address. If the selector is a TSS selector, the offset will be ignored." }
217};
218
219
220/** 'dti' arguments. */
221static const DBGCVARDESC g_aArgDumpTypeInfo[] =
222{
223 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
224 { 1, 1, DBGCVAR_CAT_STRING, 0, "type", "The type to dump" },
225 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "levels", "How many levels to dump the type information" }
226};
227
228
229/** 'dtv' arguments. */
230static const DBGCVARDESC g_aArgDumpTypedVal[] =
231{
232 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
233 { 1, 1, DBGCVAR_CAT_STRING, 0, "type", "The type to use" },
234 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address to start dumping from." },
235 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "levels", "How many levels to dump" }
236};
237
238
239/** 'e?' arguments. */
240static const DBGCVARDESC g_aArgEditMem[] =
241{
242 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
243 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to write." },
244 { 1, ~0U, DBGCVAR_CAT_NUMBER, 0, "value", "Value to write." },
245};
246
247
248/** 'g' arguments. */
249static const DBGCVARDESC g_aArgGo[] =
250{
251 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
252 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "idCpu", "CPU ID." },
253};
254
255
256/** 'lm' arguments. */
257static const DBGCVARDESC g_aArgListMods[] =
258{
259 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
260 { 0, ~0U, DBGCVAR_CAT_STRING, 0, "module", "Module name." },
261};
262
263
264/** 'ln' arguments. */
265static const DBGCVARDESC g_aArgListNear[] =
266{
267 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
268 { 0, ~0U, DBGCVAR_CAT_POINTER, 0, "address", "Address of the symbol to look up." },
269 { 0, ~0U, DBGCVAR_CAT_SYMBOL, 0, "symbol", "Symbol to lookup." },
270};
271
272
273/** 'ls' arguments. */
274static const DBGCVARDESC g_aArgListSource[] =
275{
276 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
277 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start looking for source lines." },
278};
279
280
281/** 'm' argument. */
282static const DBGCVARDESC g_aArgMemoryInfo[] =
283{
284 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
285 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Pointer to obtain info about." },
286};
287
288
289/** 'p', 'pc', 'pt', 't', 'tc' and 'tt' arguments. */
290static const DBGCVARDESC g_aArgStepTrace[] =
291{
292 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
293 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "count", "Number of instructions or source lines to step." },
294 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed afterwards. Quote it!" },
295};
296
297
298/** 'pa' and 'ta' arguments. */
299static const DBGCVARDESC g_aArgStepTraceTo[] =
300{
301 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
302 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Where to stop" },
303 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed afterwards. Quote it!" },
304};
305
306
307/** 'r' arguments. */
308static const DBGCVARDESC g_aArgReg[] =
309{
310 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
311 { 0, 1, DBGCVAR_CAT_SYMBOL, 0, "register", "Register to show or set." },
312 { 0, 1, DBGCVAR_CAT_STRING, DBGCVD_FLAGS_DEP_PREV, "=", "Equal sign." },
313 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "value", "New register value." },
314};
315
316
317/** 's' arguments. */
318static const DBGCVARDESC g_aArgSearchMem[] =
319{
320 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
321 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-b", "Byte string." },
322 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-w", "Word string." },
323 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-d", "DWord string." },
324 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-q", "QWord string." },
325 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-a", "ASCII string." },
326 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-u", "Unicode string." },
327 { 0, 1, DBGCVAR_CAT_OPTION_NUMBER, 0, "-n <Hits>", "Maximum number of hits." },
328 { 0, 1, DBGCVAR_CAT_GC_POINTER, 0, "range", "Register to show or set." },
329 { 0, ~0U, DBGCVAR_CAT_ANY, 0, "pattern", "Pattern to search for." },
330};
331
332
333/** 's?' arguments. */
334static const DBGCVARDESC g_aArgSearchMemType[] =
335{
336 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
337 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "range", "Register to show or set." },
338 { 1, ~0U, DBGCVAR_CAT_ANY, 0, "pattern", "Pattern to search for." },
339};
340
341
342/** 'sxe', 'sxn', 'sxi', 'sx-' arguments. */
343static const DBGCVARDESC g_aArgEventCtrl[] =
344{
345 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
346 { 0, 1, DBGCVAR_CAT_STRING, 0, "-c", "The -c option, requires <cmds>." },
347 { 0, 1, DBGCVAR_CAT_STRING, DBGCVD_FLAGS_DEP_PREV, "cmds", "Command to execute on this event." },
348 { 0 /*weird*/, ~0U, DBGCVAR_CAT_STRING, 0, "event", "One or more events, 'all' refering to all events." },
349};
350
351/** 'sx' and 'sr' arguments. */
352static const DBGCVARDESC g_aArgEventCtrlOpt[] =
353{
354 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
355 { 0, ~0U, DBGCVAR_CAT_STRING, 0, "event", "Zero or more events, 'all' refering to all events and being the default." },
356};
357
358/** 'u' arguments. */
359static const DBGCVARDESC g_aArgUnassemble[] =
360{
361 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
362 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start disassembling." },
363};
364
365/** 'ucfg' arguments. */
366static const DBGCVARDESC g_aArgUnassembleCfg[] =
367{
368 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
369 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start disassembling." },
370};
371
372/** 'x' arguments. */
373static const DBGCVARDESC g_aArgListSyms[] =
374{
375 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
376 { 1, 1, DBGCVAR_CAT_STRING, 0, "symbols", "The symbols to list, format is Module!Symbol with wildcards being supoprted." }
377};
378
379/** 'tflowc' arguments. */
380static const DBGCVARDESC g_aArgTraceFlowClear[] =
381{
382 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
383 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "#tf", "Trace flow module number." },
384 { 0, 1, DBGCVAR_CAT_STRING, 0, "all", "All trace flow modules." },
385};
386
387/** 'tflowd' arguments. */
388static const DBGCVARDESC g_aArgTraceFlowDisable[] =
389{
390 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
391 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "#tf", "Trace flow module number." },
392 { 0, 1, DBGCVAR_CAT_STRING, 0, "all", "All trace flow modules." },
393};
394
395/** 'tflowe' arguments. */
396static const DBGCVARDESC g_aArgTraceFlowEnable[] =
397{
398 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
399 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start tracing." },
400 { 0, 1, DBGCVAR_CAT_OPTION_NUMBER, 0, "<Hits>", "Maximum number of hits before the module is disabled." }
401};
402
403/** 'tflowp', 'tflowr' arguments. */
404static const DBGCVARDESC g_aArgTraceFlowPrintReset[] =
405{
406 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
407 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "#tf", "Trace flow module number." },
408 { 0, 1, DBGCVAR_CAT_STRING, 0, "all", "All trace flow modules." },
409};
410
411/** Command descriptors for the CodeView / WinDbg emulation.
412 * The emulation isn't attempting to be identical, only somewhat similar.
413 */
414const DBGCCMD g_aCmdsCodeView[] =
415{
416 /* pszCmd, cArgsMin, cArgsMax, paArgDescs, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
417 { "ba", 3, 6, &g_aArgBrkAcc[0], RT_ELEMENTS(g_aArgBrkAcc), 0, dbgcCmdBrkAccess, "<access> <size> <address> [passes [max passes]] [cmds]",
418 "Sets a data access breakpoint." },
419 { "bc", 1, ~0U, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), 0, dbgcCmdBrkClear, "all | <bp#> [bp# []]", "Deletes a set of breakpoints." },
420 { "bd", 1, ~0U, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), 0, dbgcCmdBrkDisable, "all | <bp#> [bp# []]", "Disables a set of breakpoints." },
421 { "be", 1, ~0U, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), 0, dbgcCmdBrkEnable, "all | <bp#> [bp# []]", "Enables a set of breakpoints." },
422 { "bl", 0, 0, NULL, 0, 0, dbgcCmdBrkList, "", "Lists all the breakpoints." },
423 { "bp", 1, 4, &g_aArgBrkSet[0], RT_ELEMENTS(g_aArgBrkSet), 0, dbgcCmdBrkSet, "<address> [passes [max passes]] [cmds]",
424 "Sets a breakpoint (int 3)." },
425 { "br", 1, 4, &g_aArgBrkREM[0], RT_ELEMENTS(g_aArgBrkREM), 0, dbgcCmdBrkREM, "<address> [passes [max passes]] [cmds]",
426 "Sets a recompiler specific breakpoint." },
427 { "d", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory using last element size and type." },
428 { "dF", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as far 16:16." },
429 { "dFs", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as far 16:16 with near symbols." },
430 { "da", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as ascii string." },
431 { "db", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in bytes." },
432 { "dd", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in double words." },
433 { "dds", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as double words with near symbols." },
434 { "da", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as ascii string." },
435 { "dg", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the global descriptor table (GDT)." },
436 { "dga", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the global descriptor table (GDT) including not-present entries." },
437 { "di", 0, ~0U, &g_aArgDumpIDT[0], RT_ELEMENTS(g_aArgDumpIDT), 0, dbgcCmdDumpIDT, "[int [..]]", "Dump the interrupt descriptor table (IDT)." },
438 { "dia", 0, ~0U, &g_aArgDumpIDT[0], RT_ELEMENTS(g_aArgDumpIDT), 0, dbgcCmdDumpIDT, "[int [..]]", "Dump the interrupt descriptor table (IDT) including not-present entries." },
439 { "dl", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the local descriptor table (LDT)." },
440 { "dla", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the local descriptor table (LDT) including not-present entries." },
441 { "dpd", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDir, "[addr|index]", "Dumps page directory entries of the default context." },
442 { "dpda", 0, 1, &g_aArgDumpPDAddr[0],RT_ELEMENTS(g_aArgDumpPDAddr), 0, dbgcCmdDumpPageDir, "[addr]", "Dumps memory at given address as a page directory." },
443 { "dpdb", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDirBoth, "[addr|index]", "Dumps page directory entries of the guest and the hypervisor. " },
444 { "dpdg", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDir, "[addr|index]", "Dumps page directory entries of the guest." },
445 { "dpdh", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDir, "[addr|index]", "Dumps page directory entries of the hypervisor. " },
446 { "dph", 0, 3, &g_aArgDumpPH[0], RT_ELEMENTS(g_aArgDumpPH), 0, dbgcCmdDumpPageHierarchy, "[addr [cr3 [mode]]", "Dumps the paging hierarchy at for specfied address range. Default context." },
447 { "dphg", 0, 3, &g_aArgDumpPH[0], RT_ELEMENTS(g_aArgDumpPH), 0, dbgcCmdDumpPageHierarchy, "[addr [cr3 [mode]]", "Dumps the paging hierarchy at for specfied address range. Guest context." },
448 { "dphh", 0, 3, &g_aArgDumpPH[0], RT_ELEMENTS(g_aArgDumpPH), 0, dbgcCmdDumpPageHierarchy, "[addr [cr3 [mode]]", "Dumps the paging hierarchy at for specfied address range. Hypervisor context." },
449 { "dp", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in mode sized words." },
450 { "dps", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in mode sized words with near symbols." },
451 { "dpt", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the default context." },
452 { "dpta", 1, 1, &g_aArgDumpPTAddr[0],RT_ELEMENTS(g_aArgDumpPTAddr), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps memory at given address as a page table." },
453 { "dptb", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTableBoth,"<addr>", "Dumps page table entries of the guest and the hypervisor." },
454 { "dptg", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the guest." },
455 { "dpth", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the hypervisor." },
456 { "dq", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in quad words." },
457 { "dqs", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as quad words with near symbols." },
458 { "dt", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the task state segment (TSS)." },
459 { "dt16", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the 16-bit task state segment (TSS)." },
460 { "dt32", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the 32-bit task state segment (TSS)." },
461 { "dt64", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the 64-bit task state segment (TSS)." },
462 { "dti", 1, 2, &g_aArgDumpTypeInfo[0],RT_ELEMENTS(g_aArgDumpTypeInfo), 0, dbgcCmdDumpTypeInfo,"<type> [levels]", "Dump type information." },
463 { "dtv", 2, 3, &g_aArgDumpTypedVal[0],RT_ELEMENTS(g_aArgDumpTypedVal), 0, dbgcCmdDumpTypedVal,"<type> <addr> [levels]", "Dump a memory buffer using the information in the given type." },
464 { "du", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as unicode string (little endian)." },
465 { "dw", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in words." },
466 /** @todo add 'e', 'ea str', 'eza str', 'eu str' and 'ezu str'. See also
467 * dbgcCmdSearchMem and its dbgcVarsToBytes usage. */
468 { "eb", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 1-byte value to memory." },
469 { "ew", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 2-byte value to memory." },
470 { "ed", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 4-byte value to memory." },
471 { "eq", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 8-byte value to memory." },
472 { "g", 0, 1, &g_aArgGo[0], RT_ELEMENTS(g_aArgGo), 0, dbgcCmdGo, "[idCpu]", "Continue execution of all or the specified CPU. (The latter is not recommended unless you know exactly what you're doing.)" },
473 { "gu", 0, 0, NULL, 0, 0, dbgcCmdGoUp, "", "Go up - continue execution till after return." },
474 { "k", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Callstack." },
475 { "kv", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Verbose callstack." },
476 { "kg", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Callstack - guest." },
477 { "kgv", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Verbose callstack - guest." },
478 { "kh", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Callstack - hypervisor." },
479 { "lm", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules." },
480 { "lmv", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules, verbose." },
481 { "lmo", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules and their segments." },
482 { "lmov", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules and their segments, verbose." },
483 { "ln", 0, ~0U, &g_aArgListNear[0], RT_ELEMENTS(g_aArgListNear), 0, dbgcCmdListNear, "[addr/sym [..]]", "List symbols near to the address. Default address is CS:EIP." },
484 { "ls", 0, 1, &g_aArgListSource[0],RT_ELEMENTS(g_aArgListSource), 0, dbgcCmdListSource, "[addr]", "Source." },
485 { "m", 1, 1, &g_aArgMemoryInfo[0],RT_ELEMENTS(g_aArgMemoryInfo), 0, dbgcCmdMemoryInfo, "<addr>", "Display information about that piece of memory." },
486 { "p", 0, 2, &g_aArgStepTrace[0], RT_ELEMENTS(g_aArgStepTrace), 0, dbgcCmdStepTrace, "[count] [cmds]", "Step over." },
487 { "pr", 0, 0, NULL, 0, 0, dbgcCmdStepTraceToggle, "", "Toggle displaying registers for tracing & stepping (no code executed)." },
488 { "pa", 1, 1, &g_aArgStepTraceTo[0], RT_ELEMENTS(g_aArgStepTraceTo), 0, dbgcCmdStepTraceTo, "<addr> [count] [cmds]","Step to the given address." },
489 { "pc", 0, 0, &g_aArgStepTrace[0], RT_ELEMENTS(g_aArgStepTrace), 0, dbgcCmdStepTrace, "[count] [cmds]", "Step to the next call instruction." },
490 { "pt", 0, 0, &g_aArgStepTrace[0], RT_ELEMENTS(g_aArgStepTrace), 0, dbgcCmdStepTrace, "[count] [cmds]", "Step to the next return instruction." },
491 { "r", 0, 3, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), 0, dbgcCmdReg, "[reg [[=] newval]]", "Show or set register(s) - active reg set." },
492 { "rg", 0, 3, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), 0, dbgcCmdRegGuest, "[reg [[=] newval]]", "Show or set register(s) - guest reg set." },
493 { "rg32", 0, 0, NULL, 0, 0, dbgcCmdRegGuest, "", "Show 32-bit guest registers." },
494 { "rg64", 0, 0, NULL, 0, 0, dbgcCmdRegGuest, "", "Show 64-bit guest registers." },
495 { "rt", 0, 0, NULL, 0, 0, dbgcCmdRegTerse, "", "Toggles terse / verbose register info." },
496 { "s", 0, ~0U, &g_aArgSearchMem[0], RT_ELEMENTS(g_aArgSearchMem), 0, dbgcCmdSearchMem, "[options] <range> <pattern>", "Continue last search." },
497 { "sa", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for an ascii string." },
498 { "sb", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more bytes." },
499 { "sd", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more double words." },
500 { "sq", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more quad words." },
501 { "su", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for an unicode string." },
502 { "sw", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more words." },
503 { "sx", 0, ~0U, &g_aArgEventCtrlOpt[0], RT_ELEMENTS(g_aArgEventCtrlOpt), 0, dbgcCmdEventCtrlList, "[<event> [..]]", "Lists settings for exceptions, exits and other events. All if no filter is specified." },
504 { "sx-", 3, ~0U, &g_aArgEventCtrl[0], RT_ELEMENTS(g_aArgEventCtrl), 0, dbgcCmdEventCtrl, "-c <cmd> <event> [..]", "Modifies the command for one or more exceptions, exits or other event. 'all' addresses all." },
505 { "sxe", 1, ~0U, &g_aArgEventCtrl[0], RT_ELEMENTS(g_aArgEventCtrl), 0, dbgcCmdEventCtrl, "[-c <cmd>] <event> [..]", "Enable: Break into the debugger on the specified exceptions, exits and other events. 'all' addresses all." },
506 { "sxn", 1, ~0U, &g_aArgEventCtrl[0], RT_ELEMENTS(g_aArgEventCtrl), 0, dbgcCmdEventCtrl, "[-c <cmd>] <event> [..]", "Notify: Display info in the debugger and continue on the specified exceptions, exits and other events. 'all' addresses all." },
507 { "sxi", 1, ~0U, &g_aArgEventCtrl[0], RT_ELEMENTS(g_aArgEventCtrl), 0, dbgcCmdEventCtrl, "[-c <cmd>] <event> [..]", "Ignore: Ignore the specified exceptions, exits and other events ('all' = all of them). Without the -c option, the guest runs like normal." },
508 { "sxr", 0, 0, &g_aArgEventCtrlOpt[0], RT_ELEMENTS(g_aArgEventCtrlOpt), 0, dbgcCmdEventCtrlReset, "", "Reset the settings to default for exceptions, exits and other events. All if no filter is specified." },
509 { "t", 0, 2, &g_aArgStepTrace[0], RT_ELEMENTS(g_aArgStepTrace), 0, dbgcCmdStepTrace, "[count] [cmds]", "Trace ." },
510 { "tflowc", 1, ~0U, &g_aArgTraceFlowClear[0], RT_ELEMENTS(g_aArgTraceFlowClear), 0, dbgcCmdTraceFlowClear, "all | <tf#> [tf# []]", "Clears trace execution flow for the given method." },
511 { "tflowd", 0, 1, &g_aArgTraceFlowDisable[0], RT_ELEMENTS(g_aArgTraceFlowDisable), 0, dbgcCmdTraceFlowDisable, "all | <tf#> [tf# []]", "Disables trace execution flow for the given method." },
512 { "tflowe", 0, 2, &g_aArgTraceFlowEnable[0], RT_ELEMENTS(g_aArgTraceFlowEnable), 0, dbgcCmdTraceFlowEnable, "<addr> <hits>", "Enable trace execution flow of the given method." },
513 { "tflowp", 0, 1, &g_aArgTraceFlowPrintReset[0], RT_ELEMENTS(g_aArgTraceFlowPrintReset), 0, dbgcCmdTraceFlowPrint, "all | <tf#> [tf# []]", "Prints the collected trace data of the given method." },
514 { "tflowr", 0, 1, &g_aArgTraceFlowPrintReset[0], RT_ELEMENTS(g_aArgTraceFlowPrintReset), 0, dbgcCmdTraceFlowReset, "all | <tf#> [tf# []]", "Resets the collected trace data of the given trace flow module." },
515 { "tr", 0, 0, NULL, 0, 0, dbgcCmdStepTraceToggle, "", "Toggle displaying registers for tracing & stepping (no code executed)." },
516 { "ta", 1, 1, &g_aArgStepTraceTo[0], RT_ELEMENTS(g_aArgStepTraceTo), 0, dbgcCmdStepTraceTo, "<addr> [count] [cmds]","Trace to the given address." },
517 { "tc", 0, 0, &g_aArgStepTrace[0], RT_ELEMENTS(g_aArgStepTrace), 0, dbgcCmdStepTrace, "[count] [cmds]", "Trace to the next call instruction." },
518 { "tt", 0, 0, &g_aArgStepTrace[0], RT_ELEMENTS(g_aArgStepTrace), 0, dbgcCmdStepTrace, "[count] [cmds]", "Trace to the next return instruction." },
519 { "u", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble." },
520 { "u64", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 64-bit code." },
521 { "u32", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 32-bit code." },
522 { "u16", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 16-bit code." },
523 { "uv86", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 16-bit code with v8086/real mode addressing." },
524 { "ucfg", 0, 1, &g_aArgUnassembleCfg[0], RT_ELEMENTS(g_aArgUnassembleCfg), 0, dbgcCmdUnassembleCfg, "[addr]", "Unassemble creating a control flow graph." },
525 { "ucfgc", 0, 1, &g_aArgUnassembleCfg[0], RT_ELEMENTS(g_aArgUnassembleCfg), 0, dbgcCmdUnassembleCfg, "[addr]", "Unassemble creating a control flow graph with colors." },
526 { "x", 1, 1, &g_aArgListSyms[0], RT_ELEMENTS(g_aArgListSyms), 0, dbgcCmdListSymbols, "* | <Module!Symbol>", "Examine symbols." },
527};
528
529/** The number of commands in the CodeView/WinDbg emulation. */
530const uint32_t g_cCmdsCodeView = RT_ELEMENTS(g_aCmdsCodeView);
531
532
533/**
534 * Selectable debug event descriptors.
535 *
536 * @remarks Sorted by DBGCSXEVT::enmType value.
537 */
538const DBGCSXEVT g_aDbgcSxEvents[] =
539{
540 { DBGFEVENT_INTERRUPT_HARDWARE, "hwint", NULL, kDbgcSxEventKind_Interrupt, kDbgcEvtState_Disabled, 0, "Hardware interrupt" },
541 { DBGFEVENT_INTERRUPT_SOFTWARE, "swint", NULL, kDbgcSxEventKind_Interrupt, kDbgcEvtState_Disabled, 0, "Software interrupt" },
542 { DBGFEVENT_TRIPLE_FAULT, "triplefault", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Enabled, 0, "Triple fault "},
543 { DBGFEVENT_XCPT_DE, "xcpt_de", "de", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#DE (integer divide error)" },
544 { DBGFEVENT_XCPT_DB, "xcpt_db", "db", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#DB (debug)" },
545 { DBGFEVENT_XCPT_02, "xcpt_02", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
546 { DBGFEVENT_XCPT_BP, "xcpt_bp", "bp", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#BP (breakpoint)" },
547 { DBGFEVENT_XCPT_OF, "xcpt_of", "of", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#OF (overflow (INTO))" },
548 { DBGFEVENT_XCPT_BR, "xcpt_br", "br", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#BR (bound range exceeded)" },
549 { DBGFEVENT_XCPT_UD, "xcpt_ud", "ud", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#UD (undefined opcode)" },
550 { DBGFEVENT_XCPT_NM, "xcpt_nm", "nm", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#NM (FPU not available)" },
551 { DBGFEVENT_XCPT_DF, "xcpt_df", "df", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#DF (double fault)" },
552 { DBGFEVENT_XCPT_09, "xcpt_09", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "Coprocessor segment overrun" },
553 { DBGFEVENT_XCPT_TS, "xcpt_ts", "ts", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, "#TS (task switch)" },
554 { DBGFEVENT_XCPT_NP, "xcpt_np", "np", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, "#NP (segment not present)" },
555 { DBGFEVENT_XCPT_SS, "xcpt_ss", "ss", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, "#SS (stack segment fault)" },
556 { DBGFEVENT_XCPT_GP, "xcpt_gp", "gp", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, "#GP (general protection fault)" },
557 { DBGFEVENT_XCPT_PF, "xcpt_pf", "pf", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, "#PF (page fault)" },
558 { DBGFEVENT_XCPT_0f, "xcpt_0f", "xcpt0f", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
559 { DBGFEVENT_XCPT_MF, "xcpt_mf", "mf", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#MF (math fault)" },
560 { DBGFEVENT_XCPT_AC, "xcpt_ac", "ac", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#AC (alignment check)" },
561 { DBGFEVENT_XCPT_MC, "xcpt_mc", "mc", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#MC (machine check)" },
562 { DBGFEVENT_XCPT_XF, "xcpt_xf", "xf", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#XF (SIMD floating-point exception)" },
563 { DBGFEVENT_XCPT_VE, "xcpt_vd", "ve", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, "#VE (virtualization exception)" },
564 { DBGFEVENT_XCPT_15, "xcpt_15", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
565 { DBGFEVENT_XCPT_16, "xcpt_16", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
566 { DBGFEVENT_XCPT_17, "xcpt_17", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
567 { DBGFEVENT_XCPT_18, "xcpt_18", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
568 { DBGFEVENT_XCPT_19, "xcpt_19", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
569 { DBGFEVENT_XCPT_1a, "xcpt_1a", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
570 { DBGFEVENT_XCPT_1b, "xcpt_1b", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
571 { DBGFEVENT_XCPT_1c, "xcpt_1c", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
572 { DBGFEVENT_XCPT_1d, "xcpt_1d", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
573 { DBGFEVENT_XCPT_SX, "xcpt_sx", "sx", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, "#SX (security exception)" },
574 { DBGFEVENT_XCPT_1f, "xcpt_1f", "xcpt1f", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
575 { DBGFEVENT_INSTR_HALT, "instr_halt", "hlt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
576 { DBGFEVENT_INSTR_MWAIT, "instr_mwait", "mwait", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
577 { DBGFEVENT_INSTR_MONITOR, "instr_monitor", "monitor", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
578 { DBGFEVENT_INSTR_CPUID, "instr_cpuid", "cpuid", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
579 { DBGFEVENT_INSTR_INVD, "instr_invd", "invd", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
580 { DBGFEVENT_INSTR_WBINVD, "instr_wbinvd", "wbinvd", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
581 { DBGFEVENT_INSTR_INVLPG, "instr_invlpg", "invlpg", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
582 { DBGFEVENT_INSTR_RDTSC, "instr_rdtsc", "rdtsc", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
583 { DBGFEVENT_INSTR_RDTSCP, "instr_rdtscp", "rdtscp", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
584 { DBGFEVENT_INSTR_RDPMC, "instr_rdpmc", "rdpmc", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
585 { DBGFEVENT_INSTR_RDMSR, "instr_rdmsr", "rdmsr", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
586 { DBGFEVENT_INSTR_WRMSR, "instr_wrmsr", "wrmsr", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
587 { DBGFEVENT_INSTR_CRX_READ, "instr_crx_read", "crx_read", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, NULL },
588 { DBGFEVENT_INSTR_CRX_WRITE, "instr_crx_write", "crx_write",kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, NULL },
589 { DBGFEVENT_INSTR_DRX_READ, "instr_drx_read", "drx_read", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, NULL },
590 { DBGFEVENT_INSTR_DRX_WRITE, "instr_drx_write", "drx_write",kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_TAKE_ARG, NULL },
591 { DBGFEVENT_INSTR_PAUSE, "instr_pause", "pause", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
592 { DBGFEVENT_INSTR_XSETBV, "instr_xsetbv", "xsetbv", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
593 { DBGFEVENT_INSTR_SIDT, "instr_sidt", "sidt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
594 { DBGFEVENT_INSTR_LIDT, "instr_lidt", "lidt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
595 { DBGFEVENT_INSTR_SGDT, "instr_sgdt", "sgdt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
596 { DBGFEVENT_INSTR_LGDT, "instr_lgdt", "lgdt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
597 { DBGFEVENT_INSTR_SLDT, "instr_sldt", "sldt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
598 { DBGFEVENT_INSTR_LLDT, "instr_lldt", "lldt", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
599 { DBGFEVENT_INSTR_STR, "instr_str", "str", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
600 { DBGFEVENT_INSTR_LTR, "instr_ltr", "ltr", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
601 { DBGFEVENT_INSTR_GETSEC, "instr_getsec", "getsec", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
602 { DBGFEVENT_INSTR_RSM, "instr_rsm", "rsm", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
603 { DBGFEVENT_INSTR_RDRAND, "instr_rdrand", "rdrand", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
604 { DBGFEVENT_INSTR_RDSEED, "instr_rdseed", "rdseed", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
605 { DBGFEVENT_INSTR_XSAVES, "instr_xsaves", "xsaves", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
606 { DBGFEVENT_INSTR_XRSTORS, "instr_xrstors", "xrstors", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
607 { DBGFEVENT_INSTR_VMM_CALL, "instr_vmm_call", "vmm_call", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
608 { DBGFEVENT_INSTR_VMX_VMCLEAR, "instr_vmx_vmclear", "vmclear", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
609 { DBGFEVENT_INSTR_VMX_VMLAUNCH, "instr_vmx_vmlaunch", "vmlaunch", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
610 { DBGFEVENT_INSTR_VMX_VMPTRLD, "instr_vmx_vmptrld", "vmptrld", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
611 { DBGFEVENT_INSTR_VMX_VMPTRST, "instr_vmx_vmptrst", "vmptrst", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
612 { DBGFEVENT_INSTR_VMX_VMREAD, "instr_vmx_vmread", "vmread", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
613 { DBGFEVENT_INSTR_VMX_VMRESUME, "instr_vmx_vmresume", "vmresume", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
614 { DBGFEVENT_INSTR_VMX_VMWRITE, "instr_vmx_vmwrite", "vmwrite", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
615 { DBGFEVENT_INSTR_VMX_VMXOFF, "instr_vmx_vmxoff", "vmxoff", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
616 { DBGFEVENT_INSTR_VMX_VMXON, "instr_vmx_vmxon", "vmxon", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
617 { DBGFEVENT_INSTR_VMX_VMFUNC, "instr_vmx_vmfunc", "vmfunc", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
618 { DBGFEVENT_INSTR_VMX_INVEPT, "instr_vmx_invept", "invept", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
619 { DBGFEVENT_INSTR_VMX_INVVPID, "instr_vmx_invvpid", "invvpid", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
620 { DBGFEVENT_INSTR_VMX_INVPCID, "instr_vmx_invpcid", "invpcid", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
621 { DBGFEVENT_INSTR_SVM_VMRUN, "instr_svm_vmrun", "vmrun", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
622 { DBGFEVENT_INSTR_SVM_VMLOAD, "instr_svm_vmload", "vmload", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
623 { DBGFEVENT_INSTR_SVM_VMSAVE, "instr_svm_vmsave", "vmsave", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
624 { DBGFEVENT_INSTR_SVM_STGI, "instr_svm_stgi", "stgi", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
625 { DBGFEVENT_INSTR_SVM_CLGI, "instr_svm_clgi", "clgi", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
626 { DBGFEVENT_EXIT_TASK_SWITCH, "exit_task_switch", "task_switch", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
627 { DBGFEVENT_EXIT_HALT, "exit_halt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
628 { DBGFEVENT_EXIT_MWAIT, "exit_mwait", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
629 { DBGFEVENT_EXIT_MONITOR, "exit_monitor", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
630 { DBGFEVENT_EXIT_CPUID, "exit_cpuid", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
631 { DBGFEVENT_EXIT_INVD, "exit_invd", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
632 { DBGFEVENT_EXIT_WBINVD, "exit_wbinvd", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
633 { DBGFEVENT_EXIT_INVLPG, "exit_invlpg", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
634 { DBGFEVENT_EXIT_RDTSC, "exit_rdtsc", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
635 { DBGFEVENT_EXIT_RDTSCP, "exit_rdtscp", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
636 { DBGFEVENT_EXIT_RDPMC, "exit_rdpmc", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
637 { DBGFEVENT_EXIT_RDMSR, "exit_rdmsr", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
638 { DBGFEVENT_EXIT_WRMSR, "exit_wrmsr", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
639 { DBGFEVENT_EXIT_CRX_READ, "exit_crx_read", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
640 { DBGFEVENT_EXIT_CRX_WRITE, "exit_crx_write", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
641 { DBGFEVENT_EXIT_DRX_READ, "exit_drx_read", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
642 { DBGFEVENT_EXIT_DRX_WRITE, "exit_drx_write", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
643 { DBGFEVENT_EXIT_PAUSE, "exit_pause", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
644 { DBGFEVENT_EXIT_XSETBV, "exit_xsetbv", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
645 { DBGFEVENT_EXIT_SIDT, "exit_sidt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
646 { DBGFEVENT_EXIT_LIDT, "exit_lidt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
647 { DBGFEVENT_EXIT_SGDT, "exit_sgdt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
648 { DBGFEVENT_EXIT_LGDT, "exit_lgdt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
649 { DBGFEVENT_EXIT_SLDT, "exit_sldt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
650 { DBGFEVENT_EXIT_LLDT, "exit_lldt", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
651 { DBGFEVENT_EXIT_STR, "exit_str", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
652 { DBGFEVENT_EXIT_LTR, "exit_ltr", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
653 { DBGFEVENT_EXIT_GETSEC, "exit_getsec", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
654 { DBGFEVENT_EXIT_RSM, "exit_rsm", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
655 { DBGFEVENT_EXIT_RDRAND, "exit_rdrand", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
656 { DBGFEVENT_EXIT_RDSEED, "exit_rdseed", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
657 { DBGFEVENT_EXIT_XSAVES, "exit_xsaves", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
658 { DBGFEVENT_EXIT_XRSTORS, "exit_xrstors", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
659 { DBGFEVENT_EXIT_VMM_CALL, "exit_vmm_call", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
660 { DBGFEVENT_EXIT_VMX_VMCLEAR, "exit_vmx_vmclear", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
661 { DBGFEVENT_EXIT_VMX_VMLAUNCH, "exit_vmx_vmlaunch", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
662 { DBGFEVENT_EXIT_VMX_VMPTRLD, "exit_vmx_vmptrld", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
663 { DBGFEVENT_EXIT_VMX_VMPTRST, "exit_vmx_vmptrst", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
664 { DBGFEVENT_EXIT_VMX_VMREAD, "exit_vmx_vmread", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
665 { DBGFEVENT_EXIT_VMX_VMRESUME, "exit_vmx_vmresume", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
666 { DBGFEVENT_EXIT_VMX_VMWRITE, "exit_vmx_vmwrite", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
667 { DBGFEVENT_EXIT_VMX_VMXOFF, "exit_vmx_vmxoff", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
668 { DBGFEVENT_EXIT_VMX_VMXON, "exit_vmx_vmxon", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
669 { DBGFEVENT_EXIT_VMX_VMFUNC, "exit_vmx_vmfunc", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
670 { DBGFEVENT_EXIT_VMX_INVEPT, "exit_vmx_invept", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
671 { DBGFEVENT_EXIT_VMX_INVVPID, "exit_vmx_invvpid", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
672 { DBGFEVENT_EXIT_VMX_INVPCID, "exit_vmx_invpcid", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
673 { DBGFEVENT_EXIT_VMX_EPT_VIOLATION, "exit_vmx_ept_violation", "eptvio", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
674 { DBGFEVENT_EXIT_VMX_EPT_MISCONFIG, "exit_vmx_ept_misconfig", "eptmis", kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
675 { DBGFEVENT_EXIT_VMX_VAPIC_ACCESS, "exit_vmx_vapic_access", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
676 { DBGFEVENT_EXIT_VMX_VAPIC_WRITE, "exit_vmx_vapic_write", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
677 { DBGFEVENT_EXIT_SVM_VMRUN, "exit_svm_vmrun", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
678 { DBGFEVENT_EXIT_SVM_VMLOAD, "exit_svm_vmload", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
679 { DBGFEVENT_EXIT_SVM_VMSAVE, "exit_svm_vmsave", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
680 { DBGFEVENT_EXIT_SVM_STGI, "exit_svm_stgi", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
681 { DBGFEVENT_EXIT_SVM_CLGI, "exit_svm_clgi", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
682 { DBGFEVENT_IOPORT_UNASSIGNED, "pio_unassigned", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
683 { DBGFEVENT_IOPORT_UNUSED, "pio_unused", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
684 { DBGFEVENT_MEMORY_UNASSIGNED, "mmio_unassigned", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
685 { DBGFEVENT_MEMORY_ROM_WRITE, "rom_write", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, 0, NULL },
686 { DBGFEVENT_BSOD_MSR, "bsod_msr", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_BUGCHECK, NULL },
687 { DBGFEVENT_BSOD_EFI, "bsod_efi", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_BUGCHECK, NULL },
688 { DBGFEVENT_BSOD_VMMDEV, "bsod_vmmdev", NULL, kDbgcSxEventKind_Plain, kDbgcEvtState_Disabled, DBGCSXEVT_F_BUGCHECK, NULL },
689};
690/** Number of entries in g_aDbgcSxEvents. */
691const uint32_t g_cDbgcSxEvents = RT_ELEMENTS(g_aDbgcSxEvents);
692
693
694
695/**
696 * @callback_method_impl{FNDBGCCMD, The 'g' command.}
697 */
698static DECLCALLBACK(int) dbgcCmdGo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
699{
700 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
701
702 /*
703 * Parse arguments.
704 */
705 VMCPUID idCpu = VMCPUID_ALL;
706 if (cArgs == 1)
707 {
708 VMCPUID cCpus = DBGFR3CpuGetCount(pUVM);
709 if (paArgs[0].u.u64Number >= cCpus)
710 return DBGCCmdHlpFail(pCmdHlp, pCmd, "idCpu %RU64 is out of range! Highest valid ID is %u.\n",
711 paArgs[0].u.u64Number, cCpus - 1);
712 idCpu = (VMCPUID)paArgs[0].u.u64Number;
713 }
714 else
715 Assert(cArgs == 0);
716
717 /*
718 * Try resume the VM or CPU.
719 */
720 int rc = DBGFR3Resume(pUVM, idCpu);
721 if (RT_SUCCESS(rc))
722 {
723 Assert(rc == VINF_SUCCESS || rc == VWRN_DBGF_ALREADY_RUNNING);
724 if (rc != VWRN_DBGF_ALREADY_RUNNING)
725 return VINF_SUCCESS;
726 if (idCpu == VMCPUID_ALL)
727 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The VM is already running");
728 return DBGCCmdHlpFail(pCmdHlp, pCmd, "CPU %u is already running", idCpu);
729 }
730 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3Resume");
731}
732
733
734/**
735 * @callback_method_impl{FNDBGCCMD, The 'gu' command.}
736 */
737static DECLCALLBACK(int) dbgcCmdGoUp(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
738{
739 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
740 RT_NOREF(pCmd, paArgs, cArgs);
741
742 /* The simple way out. */
743 PDBGFADDRESS pStackPop = NULL; /** @todo try set up some stack limitations */
744 RTGCPTR cbStackPop = 0;
745 int rc = DBGFR3StepEx(pUVM, pDbgc->idCpu, DBGF_STEP_F_OVER | DBGF_STEP_F_STOP_AFTER_RET, NULL, pStackPop, cbStackPop, _512K);
746 if (RT_SUCCESS(rc))
747 pDbgc->fReady = false;
748 else
749 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3StepEx(,,DBGF_STEP_F_OVER | DBGF_STEP_F_STOP_AFTER_RET,) failed");
750 return rc;
751}
752
753
754/**
755 * @callback_method_impl{FNDBGCCMD, The 'ba' command.}
756 */
757static DECLCALLBACK(int) dbgcCmdBrkAccess(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
758{
759 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
760
761 /*
762 * Interpret access type.
763 */
764 if ( !strchr("xrwi", paArgs[0].u.pszString[0])
765 || paArgs[0].u.pszString[1])
766 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid access type '%s' for '%s'. Valid types are 'e', 'r', 'w' and 'i'",
767 paArgs[0].u.pszString, pCmd->pszCmd);
768 uint8_t fType = 0;
769 switch (paArgs[0].u.pszString[0])
770 {
771 case 'x': fType = X86_DR7_RW_EO; break;
772 case 'r': fType = X86_DR7_RW_RW; break;
773 case 'w': fType = X86_DR7_RW_WO; break;
774 case 'i': fType = X86_DR7_RW_IO; break;
775 }
776
777 /*
778 * Validate size.
779 */
780 if (fType == X86_DR7_RW_EO && paArgs[1].u.u64Number != 1)
781 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid access size %RX64 for '%s'. 'x' access type requires size 1!",
782 paArgs[1].u.u64Number, pCmd->pszCmd);
783 switch (paArgs[1].u.u64Number)
784 {
785 case 1:
786 case 2:
787 case 4:
788 break;
789 /*case 8: - later*/
790 default:
791 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid access size %RX64 for '%s'. 1, 2 or 4!",
792 paArgs[1].u.u64Number, pCmd->pszCmd);
793 }
794 uint8_t cb = (uint8_t)paArgs[1].u.u64Number;
795
796 /*
797 * Convert the pointer to a DBGF address.
798 */
799 DBGFADDRESS Address;
800 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &paArgs[2], &Address);
801 if (RT_FAILURE(rc))
802 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,%DV,)", &paArgs[2]);
803
804 /*
805 * Pick out the optional arguments.
806 */
807 uint64_t iHitTrigger = 0;
808 uint64_t iHitDisable = UINT64_MAX;
809 const char *pszCmds = NULL;
810 unsigned iArg = 3;
811 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
812 {
813 iHitTrigger = paArgs[iArg].u.u64Number;
814 iArg++;
815 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
816 {
817 iHitDisable = paArgs[iArg].u.u64Number;
818 iArg++;
819 }
820 }
821 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
822 {
823 pszCmds = paArgs[iArg].u.pszString;
824 iArg++;
825 }
826
827 /*
828 * Try set the breakpoint.
829 */
830 uint32_t iBp;
831 rc = DBGFR3BpSetReg(pUVM, &Address, iHitTrigger, iHitDisable, fType, cb, &iBp);
832 if (RT_SUCCESS(rc))
833 {
834 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
835 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
836 if (RT_SUCCESS(rc))
837 return DBGCCmdHlpPrintf(pCmdHlp, "Set access breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
838 if (rc == VERR_DBGC_BP_EXISTS)
839 {
840 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
841 if (RT_SUCCESS(rc))
842 return DBGCCmdHlpPrintf(pCmdHlp, "Updated access breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
843 }
844 int rc2 = DBGFR3BpClear(pDbgc->pUVM, iBp);
845 AssertRC(rc2);
846 }
847 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to set access breakpoint at %RGv", Address.FlatPtr);
848}
849
850
851/**
852 * @callback_method_impl{FNDBGCCMD, The 'bc' command.}
853 */
854static DECLCALLBACK(int) dbgcCmdBrkClear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
855{
856 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
857
858 /*
859 * Enumerate the arguments.
860 */
861 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
862 int rc = VINF_SUCCESS;
863 for (unsigned iArg = 0; iArg < cArgs && RT_SUCCESS(rc); iArg++)
864 {
865 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
866 {
867 /* one */
868 uint32_t iBp = (uint32_t)paArgs[iArg].u.u64Number;
869 if (iBp == paArgs[iArg].u.u64Number)
870 {
871 int rc2 = DBGFR3BpClear(pUVM, iBp);
872 if (RT_FAILURE(rc2))
873 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpClear(,%#x)", iBp);
874 if (RT_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
875 dbgcBpDelete(pDbgc, iBp);
876 }
877 else
878 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Breakpoint id %RX64 is too large", paArgs[iArg].u.u64Number);
879 }
880 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
881 {
882 /* all */
883 PDBGCBP pBp = pDbgc->pFirstBp;
884 while (pBp)
885 {
886 uint32_t iBp = pBp->iBp;
887 pBp = pBp->pNext;
888
889 int rc2 = DBGFR3BpClear(pUVM, iBp);
890 if (RT_FAILURE(rc2))
891 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpClear(,%#x)", iBp);
892 if (RT_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
893 dbgcBpDelete(pDbgc, iBp);
894 }
895 }
896 else
897 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid argument '%s'", paArgs[iArg].u.pszString);
898 }
899 return rc;
900}
901
902
903/**
904 * @callback_method_impl{FNDBGCCMD, The 'bd' command.}
905 */
906static DECLCALLBACK(int) dbgcCmdBrkDisable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
907{
908 /*
909 * Enumerate the arguments.
910 */
911 int rc = VINF_SUCCESS;
912 for (unsigned iArg = 0; iArg < cArgs && RT_SUCCESS(rc); iArg++)
913 {
914 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
915 {
916 /* one */
917 uint32_t iBp = (uint32_t)paArgs[iArg].u.u64Number;
918 if (iBp == paArgs[iArg].u.u64Number)
919 {
920 rc = DBGFR3BpDisable(pUVM, iBp);
921 if (RT_FAILURE(rc))
922 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3BpDisable failed for breakpoint %#x", iBp);
923 }
924 else
925 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Breakpoint id %RX64 is too large", paArgs[iArg].u.u64Number);
926 }
927 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
928 {
929 /* all */
930 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
931 for (PDBGCBP pBp = pDbgc->pFirstBp; pBp; pBp = pBp->pNext)
932 {
933 int rc2 = DBGFR3BpDisable(pUVM, pBp->iBp);
934 if (RT_FAILURE(rc2))
935 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpDisable failed for breakpoint %#x", pBp->iBp);
936 }
937 }
938 else
939 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid argument '%s'", paArgs[iArg].u.pszString);
940 }
941 return rc;
942}
943
944
945/**
946 * @callback_method_impl{FNDBGCCMD, The 'be' command.}
947 */
948static DECLCALLBACK(int) dbgcCmdBrkEnable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
949{
950 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
951
952 /*
953 * Enumerate the arguments.
954 */
955 int rc = VINF_SUCCESS;
956 for (unsigned iArg = 0; iArg < cArgs && RT_SUCCESS(rc); iArg++)
957 {
958 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
959 {
960 /* one */
961 uint32_t iBp = (uint32_t)paArgs[iArg].u.u64Number;
962 if (iBp == paArgs[iArg].u.u64Number)
963 {
964 rc = DBGFR3BpEnable(pUVM, iBp);
965 if (RT_FAILURE(rc))
966 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3BpEnable failed for breakpoint %#x", iBp);
967 }
968 else
969 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Breakpoint id %RX64 is too large", paArgs[iArg].u.u64Number);
970 }
971 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
972 {
973 /* all */
974 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
975 for (PDBGCBP pBp = pDbgc->pFirstBp; pBp; pBp = pBp->pNext)
976 {
977 int rc2 = DBGFR3BpEnable(pUVM, pBp->iBp);
978 if (RT_FAILURE(rc2))
979 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpEnable failed for breakpoint %#x", pBp->iBp);
980 }
981 }
982 else
983 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid argument '%s'", paArgs[iArg].u.pszString);
984 }
985 return rc;
986}
987
988
989/**
990 * Breakpoint enumeration callback function.
991 *
992 * @returns VBox status code. Any failure will stop the enumeration.
993 * @param pUVM The user mode VM handle.
994 * @param pvUser The user argument.
995 * @param hBp The DBGF breakpoint handle.
996 * @param pBp Pointer to the breakpoint information. (readonly)
997 */
998static DECLCALLBACK(int) dbgcEnumBreakpointsCallback(PUVM pUVM, void *pvUser, DBGFBP hBp, PCDBGFBPPUB pBp)
999{
1000 PDBGC pDbgc = (PDBGC)pvUser;
1001 PDBGCBP pDbgcBp = dbgcBpGet(pDbgc, hBp);
1002
1003 /*
1004 * BP type and size.
1005 */
1006 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%#4x %c ", hBp, DBGF_BP_PUB_IS_ENABLED(pBp) ? 'e' : 'd');
1007 bool fHasAddress = false;
1008 switch (DBGF_BP_PUB_GET_TYPE(pBp))
1009 {
1010 case DBGFBPTYPE_INT3:
1011 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " p %RGv", pBp->u.Int3.GCPtr);
1012 fHasAddress = true;
1013 break;
1014 case DBGFBPTYPE_REG:
1015 {
1016 char chType;
1017 switch (pBp->u.Reg.fType)
1018 {
1019 case X86_DR7_RW_EO: chType = 'x'; break;
1020 case X86_DR7_RW_WO: chType = 'w'; break;
1021 case X86_DR7_RW_IO: chType = 'i'; break;
1022 case X86_DR7_RW_RW: chType = 'r'; break;
1023 default: chType = '?'; break;
1024
1025 }
1026 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%d %c %RGv", pBp->u.Reg.cb, chType, pBp->u.Reg.GCPtr);
1027 fHasAddress = true;
1028 break;
1029 }
1030
1031/** @todo realign the list when I/O and MMIO breakpoint command have been added and it's possible to test this code. */
1032 case DBGFBPTYPE_PORT_IO:
1033 case DBGFBPTYPE_MMIO:
1034 {
1035 uint32_t fAccess = DBGF_BP_PUB_GET_TYPE(pBp) == DBGFBPTYPE_PORT_IO ? pBp->u.PortIo.fAccess : pBp->u.Mmio.fAccess;
1036 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, DBGF_BP_PUB_GET_TYPE(pBp) == DBGFBPTYPE_PORT_IO ? " i" : " m");
1037 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " %c%c%c%c%c%c",
1038 fAccess & DBGFBPIOACCESS_READ_MASK ? 'r' : '-',
1039 fAccess & DBGFBPIOACCESS_READ_BYTE ? '1' : '-',
1040 fAccess & DBGFBPIOACCESS_READ_WORD ? '2' : '-',
1041 fAccess & DBGFBPIOACCESS_READ_DWORD ? '4' : '-',
1042 fAccess & DBGFBPIOACCESS_READ_QWORD ? '8' : '-',
1043 fAccess & DBGFBPIOACCESS_READ_OTHER ? '+' : '-');
1044 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " %c%c%c%c%c%c",
1045 fAccess & DBGFBPIOACCESS_WRITE_MASK ? 'w' : '-',
1046 fAccess & DBGFBPIOACCESS_WRITE_BYTE ? '1' : '-',
1047 fAccess & DBGFBPIOACCESS_WRITE_WORD ? '2' : '-',
1048 fAccess & DBGFBPIOACCESS_WRITE_DWORD ? '4' : '-',
1049 fAccess & DBGFBPIOACCESS_WRITE_QWORD ? '8' : '-',
1050 fAccess & DBGFBPIOACCESS_WRITE_OTHER ? '+' : '-');
1051 if (DBGF_BP_PUB_GET_TYPE(pBp) == DBGFBPTYPE_PORT_IO)
1052 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " %04x-%04x",
1053 pBp->u.PortIo.uPort, pBp->u.PortIo.uPort + pBp->u.PortIo.cPorts - 1);
1054 else
1055 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%RGp LB %03x", pBp->u.Mmio.PhysAddr, pBp->u.Mmio.cb);
1056 break;
1057 }
1058
1059 default:
1060 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " unknown type %d!!", DBGF_BP_PUB_GET_TYPE(pBp));
1061 AssertFailed();
1062 break;
1063
1064 }
1065 if (pBp->iHitDisable == ~(uint64_t)0)
1066 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " %04RX64 (%04RX64 to ~0) ", pBp->cHits, pBp->iHitTrigger);
1067 else
1068 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " %04RX64 (%04RX64 to %04RX64)", pBp->cHits, pBp->iHitTrigger, pBp->iHitDisable);
1069
1070 /*
1071 * Try resolve the address if it has one.
1072 */
1073 if (fHasAddress)
1074 {
1075 RTDBGSYMBOL Sym;
1076 RTINTPTR off;
1077 DBGFADDRESS Addr;
1078 int rc = DBGFR3AsSymbolByAddr(pUVM, pDbgc->hDbgAs, DBGFR3AddrFromFlat(pDbgc->pUVM, &Addr, pBp->u.GCPtr),
1079 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
1080 &off, &Sym, NULL);
1081 if (RT_SUCCESS(rc))
1082 {
1083 if (!off)
1084 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%s", Sym.szName);
1085 else if (off > 0)
1086 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%s+%RGv", Sym.szName, off);
1087 else
1088 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%s-%RGv", Sym.szName, -off);
1089 }
1090 }
1091
1092 /*
1093 * The commands.
1094 */
1095 if (pDbgcBp)
1096 {
1097 if (pDbgcBp->cchCmd)
1098 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "\n cmds: '%s'\n", pDbgcBp->szCmd);
1099 else
1100 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "\n");
1101 }
1102 else
1103 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " [unknown bp]\n");
1104
1105 return VINF_SUCCESS;
1106}
1107
1108
1109/**
1110 * @callback_method_impl{FNDBGCCMD, The 'bl' command.}
1111 */
1112static DECLCALLBACK(int) dbgcCmdBrkList(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1113{
1114 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1115 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 0);
1116 NOREF(paArgs);
1117
1118 /*
1119 * Enumerate the breakpoints.
1120 */
1121 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1122 int rc = DBGFR3BpEnum(pUVM, dbgcEnumBreakpointsCallback, pDbgc);
1123 if (RT_FAILURE(rc))
1124 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3BpEnum");
1125 return rc;
1126}
1127
1128
1129/**
1130 * @callback_method_impl{FNDBGCCMD, The 'bp' command.}
1131 */
1132static DECLCALLBACK(int) dbgcCmdBrkSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1133{
1134 /*
1135 * Convert the pointer to a DBGF address.
1136 */
1137 DBGFADDRESS Address;
1138 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
1139 if (RT_FAILURE(rc))
1140 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,'%DV',)", &paArgs[0]);
1141
1142 /*
1143 * Pick out the optional arguments.
1144 */
1145 uint64_t iHitTrigger = 0;
1146 uint64_t iHitDisable = UINT64_MAX;
1147 const char *pszCmds = NULL;
1148 unsigned iArg = 1;
1149 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
1150 {
1151 iHitTrigger = paArgs[iArg].u.u64Number;
1152 iArg++;
1153 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
1154 {
1155 iHitDisable = paArgs[iArg].u.u64Number;
1156 iArg++;
1157 }
1158 }
1159 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
1160 {
1161 pszCmds = paArgs[iArg].u.pszString;
1162 iArg++;
1163 }
1164
1165 /*
1166 * Try set the breakpoint.
1167 */
1168 uint32_t iBp;
1169 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1170 rc = DBGFR3BpSetInt3(pUVM, pDbgc->idCpu, &Address, iHitTrigger, iHitDisable, &iBp);
1171 if (RT_SUCCESS(rc))
1172 {
1173 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
1174 if (RT_SUCCESS(rc))
1175 return DBGCCmdHlpPrintf(pCmdHlp, "Set breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
1176 if (rc == VERR_DBGC_BP_EXISTS)
1177 {
1178 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
1179 if (RT_SUCCESS(rc))
1180 return DBGCCmdHlpPrintf(pCmdHlp, "Updated breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
1181 }
1182 int rc2 = DBGFR3BpClear(pDbgc->pUVM, iBp);
1183 AssertRC(rc2);
1184 }
1185 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to set breakpoint at %RGv", Address.FlatPtr);
1186}
1187
1188
1189/**
1190 * @callback_method_impl{FNDBGCCMD, The 'br' command.}
1191 */
1192static DECLCALLBACK(int) dbgcCmdBrkREM(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1193{
1194 /*
1195 * Convert the pointer to a DBGF address.
1196 */
1197 DBGFADDRESS Address;
1198 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
1199 if (RT_FAILURE(rc))
1200 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,'%DV',)", &paArgs[0]);
1201
1202 /*
1203 * Pick out the optional arguments.
1204 */
1205 uint64_t iHitTrigger = 0;
1206 uint64_t iHitDisable = UINT64_MAX;
1207 const char *pszCmds = NULL;
1208 unsigned iArg = 1;
1209 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
1210 {
1211 iHitTrigger = paArgs[iArg].u.u64Number;
1212 iArg++;
1213 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
1214 {
1215 iHitDisable = paArgs[iArg].u.u64Number;
1216 iArg++;
1217 }
1218 }
1219 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
1220 {
1221 pszCmds = paArgs[iArg].u.pszString;
1222 iArg++;
1223 }
1224
1225 /*
1226 * Try set the breakpoint.
1227 */
1228 uint32_t iBp;
1229 rc = DBGFR3BpSetREM(pUVM, &Address, iHitTrigger, iHitDisable, &iBp);
1230 if (RT_SUCCESS(rc))
1231 {
1232 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1233 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
1234 if (RT_SUCCESS(rc))
1235 return DBGCCmdHlpPrintf(pCmdHlp, "Set REM breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
1236 if (rc == VERR_DBGC_BP_EXISTS)
1237 {
1238 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
1239 if (RT_SUCCESS(rc))
1240 return DBGCCmdHlpPrintf(pCmdHlp, "Updated REM breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
1241 }
1242 int rc2 = DBGFR3BpClear(pDbgc->pUVM, iBp);
1243 AssertRC(rc2);
1244 }
1245 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to set REM breakpoint at %RGv", Address.FlatPtr);
1246}
1247
1248
1249/**
1250 * Helps the unassmble ('u') command display symbols it starts at and passes.
1251 *
1252 * @param pUVM The user mode VM handle.
1253 * @param pCmdHlp The command helpers for printing via.
1254 * @param hDbgAs The address space to look up addresses in.
1255 * @param pAddress The current address.
1256 * @param pcbCallAgain Where to return the distance to the next check (in
1257 * instruction bytes).
1258 */
1259static void dbgcCmdUnassambleHelpListNear(PUVM pUVM, PDBGCCMDHLP pCmdHlp, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress,
1260 PRTUINTPTR pcbCallAgain)
1261{
1262 RTDBGSYMBOL Symbol;
1263 RTGCINTPTR offDispSym;
1264 int rc = DBGFR3AsSymbolByAddr(pUVM, hDbgAs, pAddress,
1265 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
1266 &offDispSym, &Symbol, NULL);
1267 if (RT_FAILURE(rc) || offDispSym > _1G)
1268 rc = DBGFR3AsSymbolByAddr(pUVM, hDbgAs, pAddress,
1269 RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
1270 &offDispSym, &Symbol, NULL);
1271 if (RT_SUCCESS(rc) && offDispSym < _1G)
1272 {
1273 if (!offDispSym)
1274 {
1275 DBGCCmdHlpPrintf(pCmdHlp, "%s:\n", Symbol.szName);
1276 *pcbCallAgain = !Symbol.cb ? 64 : Symbol.cb;
1277 }
1278 else if (offDispSym > 0)
1279 {
1280 DBGCCmdHlpPrintf(pCmdHlp, "%s+%#llx:\n", Symbol.szName, (uint64_t)offDispSym);
1281 *pcbCallAgain = !Symbol.cb ? 64 : Symbol.cb > (RTGCUINTPTR)offDispSym ? Symbol.cb - (RTGCUINTPTR)offDispSym : 1;
1282 }
1283 else
1284 {
1285 DBGCCmdHlpPrintf(pCmdHlp, "%s-%#llx:\n", Symbol.szName, (uint64_t)-offDispSym);
1286 *pcbCallAgain = !Symbol.cb ? 64 : (RTGCUINTPTR)-offDispSym + Symbol.cb;
1287 }
1288 }
1289 else
1290 *pcbCallAgain = UINT32_MAX;
1291}
1292
1293
1294/**
1295 * @callback_method_impl{FNDBGCCMD, The 'u' command.}
1296 */
1297static DECLCALLBACK(int) dbgcCmdUnassemble(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1298{
1299 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1300
1301 /*
1302 * Validate input.
1303 */
1304 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1305 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs <= 1);
1306 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 0 || DBGCVAR_ISPOINTER(paArgs[0].enmType));
1307
1308 if (!cArgs && !DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
1309 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Don't know where to start disassembling");
1310
1311 /*
1312 * Check the desired mode.
1313 */
1314 unsigned fFlags = DBGF_DISAS_FLAGS_NO_ADDRESS | DBGF_DISAS_FLAGS_UNPATCHED_BYTES | DBGF_DISAS_FLAGS_ANNOTATE_PATCHED;
1315 switch (pCmd->pszCmd[1])
1316 {
1317 default: AssertFailed(); RT_FALL_THRU();
1318 case '\0': fFlags |= DBGF_DISAS_FLAGS_DEFAULT_MODE; break;
1319 case '6': fFlags |= DBGF_DISAS_FLAGS_64BIT_MODE; break;
1320 case '3': fFlags |= DBGF_DISAS_FLAGS_32BIT_MODE; break;
1321 case '1': fFlags |= DBGF_DISAS_FLAGS_16BIT_MODE; break;
1322 case 'v': fFlags |= DBGF_DISAS_FLAGS_16BIT_REAL_MODE; break;
1323 }
1324
1325 /** @todo should use DBGFADDRESS for everything */
1326
1327 /*
1328 * Find address.
1329 */
1330 if (!cArgs)
1331 {
1332 if (!DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
1333 {
1334 /** @todo Batch query CS, RIP, CPU mode and flags. */
1335 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
1336 if (CPUMIsGuestIn64BitCode(pVCpu))
1337 {
1338 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FLAT;
1339 pDbgc->SourcePos.u.GCFlat = CPUMGetGuestRIP(pVCpu);
1340 }
1341 else
1342 {
1343 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FAR;
1344 pDbgc->SourcePos.u.GCFar.off = CPUMGetGuestEIP(pVCpu);
1345 pDbgc->SourcePos.u.GCFar.sel = CPUMGetGuestCS(pVCpu);
1346 if ( (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_DEFAULT_MODE
1347 && (CPUMGetGuestEFlags(pVCpu) & X86_EFL_VM))
1348 {
1349 fFlags &= ~DBGF_DISAS_FLAGS_MODE_MASK;
1350 fFlags |= DBGF_DISAS_FLAGS_16BIT_REAL_MODE;
1351 }
1352 }
1353
1354 fFlags |= DBGF_DISAS_FLAGS_CURRENT_GUEST;
1355 }
1356 else if ((fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_DEFAULT_MODE && pDbgc->fDisasm)
1357 {
1358 fFlags &= ~DBGF_DISAS_FLAGS_MODE_MASK;
1359 fFlags |= pDbgc->fDisasm & DBGF_DISAS_FLAGS_MODE_MASK;
1360 }
1361 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_NONE;
1362 }
1363 else
1364 pDbgc->DisasmPos = paArgs[0];
1365 pDbgc->pLastPos = &pDbgc->DisasmPos;
1366
1367 /*
1368 * Range.
1369 */
1370 switch (pDbgc->DisasmPos.enmRangeType)
1371 {
1372 case DBGCVAR_RANGE_NONE:
1373 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
1374 pDbgc->DisasmPos.u64Range = 10;
1375 break;
1376
1377 case DBGCVAR_RANGE_ELEMENTS:
1378 if (pDbgc->DisasmPos.u64Range > 2048)
1379 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Too many lines requested. Max is 2048 lines");
1380 break;
1381
1382 case DBGCVAR_RANGE_BYTES:
1383 if (pDbgc->DisasmPos.u64Range > 65536)
1384 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The requested range is too big. Max is 64KB");
1385 break;
1386
1387 default:
1388 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Unknown range type %d", pDbgc->DisasmPos.enmRangeType);
1389 }
1390
1391 /*
1392 * Convert physical and host addresses to guest addresses.
1393 */
1394 RTDBGAS hDbgAs = pDbgc->hDbgAs;
1395 int rc;
1396 switch (pDbgc->DisasmPos.enmType)
1397 {
1398 case DBGCVAR_TYPE_GC_FLAT:
1399 case DBGCVAR_TYPE_GC_FAR:
1400 break;
1401 case DBGCVAR_TYPE_GC_PHYS:
1402 hDbgAs = DBGF_AS_PHYS;
1403 RT_FALL_THRU();
1404 case DBGCVAR_TYPE_HC_FLAT:
1405 case DBGCVAR_TYPE_HC_PHYS:
1406 {
1407 DBGCVAR VarTmp;
1408 rc = DBGCCmdHlpEval(pCmdHlp, &VarTmp, "%%(%Dv)", &pDbgc->DisasmPos);
1409 if (RT_FAILURE(rc))
1410 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "failed to evaluate '%%(%Dv)'", &pDbgc->DisasmPos);
1411 pDbgc->DisasmPos = VarTmp;
1412 break;
1413 }
1414 default: AssertFailed(); break;
1415 }
1416
1417 DBGFADDRESS CurAddr;
1418 if ( (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_16BIT_REAL_MODE
1419 && pDbgc->DisasmPos.enmType == DBGCVAR_TYPE_GC_FAR)
1420 DBGFR3AddrFromFlat(pUVM, &CurAddr, ((uint32_t)pDbgc->DisasmPos.u.GCFar.sel << 4) + pDbgc->DisasmPos.u.GCFar.off);
1421 else
1422 {
1423 rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &pDbgc->DisasmPos, &CurAddr);
1424 if (RT_FAILURE(rc))
1425 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr failed on '%Dv'", &pDbgc->DisasmPos);
1426 }
1427
1428 pDbgc->fDisasm = fFlags;
1429
1430 /*
1431 * Figure out where we are and display it. Also calculate when we need to
1432 * check for a new symbol if possible.
1433 */
1434 RTGCUINTPTR cbCheckSymbol;
1435 dbgcCmdUnassambleHelpListNear(pUVM, pCmdHlp, hDbgAs, &CurAddr, &cbCheckSymbol);
1436
1437 /*
1438 * Do the disassembling.
1439 */
1440 unsigned cTries = 32;
1441 int iRangeLeft = (int)pDbgc->DisasmPos.u64Range;
1442 if (iRangeLeft == 0) /* kludge for 'r'. */
1443 iRangeLeft = -1;
1444 for (;;)
1445 {
1446 /*
1447 * Disassemble the instruction.
1448 */
1449 char szDis[256];
1450 uint32_t cbInstr = 1;
1451 if (pDbgc->DisasmPos.enmType == DBGCVAR_TYPE_GC_FLAT)
1452 rc = DBGFR3DisasInstrEx(pUVM, pDbgc->idCpu, DBGF_SEL_FLAT, pDbgc->DisasmPos.u.GCFlat, fFlags,
1453 &szDis[0], sizeof(szDis), &cbInstr);
1454 else
1455 rc = DBGFR3DisasInstrEx(pUVM, pDbgc->idCpu, pDbgc->DisasmPos.u.GCFar.sel, pDbgc->DisasmPos.u.GCFar.off, fFlags,
1456 &szDis[0], sizeof(szDis), &cbInstr);
1457 if (RT_SUCCESS(rc))
1458 {
1459 /* print it */
1460 rc = DBGCCmdHlpPrintf(pCmdHlp, "%-16DV %s\n", &pDbgc->DisasmPos, &szDis[0]);
1461 if (RT_FAILURE(rc))
1462 return rc;
1463 }
1464 else
1465 {
1466 /* bitch. */
1467 int rc2 = DBGCCmdHlpPrintf(pCmdHlp, "Failed to disassemble instruction, skipping one byte.\n");
1468 if (RT_FAILURE(rc2))
1469 return rc2;
1470 if (cTries-- > 0)
1471 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Too many disassembly failures. Giving up");
1472 cbInstr = 1;
1473 }
1474
1475 /* advance */
1476 if (iRangeLeft < 0) /* 'r' */
1477 break;
1478 if (pDbgc->DisasmPos.enmRangeType == DBGCVAR_RANGE_ELEMENTS)
1479 iRangeLeft--;
1480 else
1481 iRangeLeft -= cbInstr;
1482 rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->DisasmPos, "(%Dv) + %x", &pDbgc->DisasmPos, cbInstr);
1483 if (RT_FAILURE(rc))
1484 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpEval(,,'(%Dv) + %x')", &pDbgc->DisasmPos, cbInstr);
1485 if (iRangeLeft <= 0)
1486 break;
1487 fFlags &= ~DBGF_DISAS_FLAGS_CURRENT_GUEST;
1488
1489 /* Print next symbol? */
1490 if (cbCheckSymbol <= cbInstr)
1491 {
1492 if ( (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_16BIT_REAL_MODE
1493 && pDbgc->DisasmPos.enmType == DBGCVAR_TYPE_GC_FAR)
1494 DBGFR3AddrFromFlat(pUVM, &CurAddr, ((uint32_t)pDbgc->DisasmPos.u.GCFar.sel << 4) + pDbgc->DisasmPos.u.GCFar.off);
1495 else
1496 rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &pDbgc->DisasmPos, &CurAddr);
1497 if (RT_SUCCESS(rc))
1498 dbgcCmdUnassambleHelpListNear(pUVM, pCmdHlp, hDbgAs, &CurAddr, &cbCheckSymbol);
1499 else
1500 cbCheckSymbol = UINT32_MAX;
1501 }
1502 else
1503 cbCheckSymbol -= cbInstr;
1504 }
1505
1506 NOREF(pCmd);
1507 return VINF_SUCCESS;
1508}
1509
1510
1511/**
1512 * @callback_method_impl{FNDGCSCREENBLIT}
1513 */
1514static DECLCALLBACK(int) dbgcCmdUnassembleCfgBlit(const char *psz, void *pvUser)
1515{
1516 PDBGCCMDHLP pCmdHlp = (PDBGCCMDHLP)pvUser;
1517 return DBGCCmdHlpPrintf(pCmdHlp, "%s", psz);
1518}
1519
1520
1521/**
1522 * Checks whether both addresses are equal.
1523 *
1524 * @returns true if both addresses point to the same location, false otherwise.
1525 * @param pAddr1 First address.
1526 * @param pAddr2 Second address.
1527 */
1528static bool dbgcCmdUnassembleCfgAddrEqual(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2)
1529{
1530 return pAddr1->Sel == pAddr2->Sel
1531 && pAddr1->off == pAddr2->off;
1532}
1533
1534
1535/**
1536 * Checks whether the first given address is lower than the second one.
1537 *
1538 * @returns true if both addresses point to the same location, false otherwise.
1539 * @param pAddr1 First address.
1540 * @param pAddr2 Second address.
1541 */
1542static bool dbgcCmdUnassembleCfgAddrLower(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2)
1543{
1544 return pAddr1->Sel == pAddr2->Sel
1545 && pAddr1->off < pAddr2->off;
1546}
1547
1548
1549/**
1550 * Calculates the size required for the given basic block including the
1551 * border and spacing on the edges.
1552 *
1553 * @returns nothing.
1554 * @param hFlowBb The basic block handle.
1555 * @param pDumpBb The dumper state to fill in for the basic block.
1556 */
1557static void dbgcCmdUnassembleCfgDumpCalcBbSize(DBGFFLOWBB hFlowBb, PDBGCFLOWBBDUMP pDumpBb)
1558{
1559 uint32_t fFlags = DBGFR3FlowBbGetFlags(hFlowBb);
1560 uint32_t cInstr = DBGFR3FlowBbGetInstrCount(hFlowBb);
1561
1562 pDumpBb->hFlowBb = hFlowBb;
1563 pDumpBb->cchHeight = cInstr + 4; /* Include spacing and border top and bottom. */
1564 pDumpBb->cchWidth = 0;
1565 DBGFR3FlowBbGetStartAddress(hFlowBb, &pDumpBb->AddrStart);
1566
1567 DBGFFLOWBBENDTYPE enmType = DBGFR3FlowBbGetType(hFlowBb);
1568 if ( enmType == DBGFFLOWBBENDTYPE_COND
1569 || enmType == DBGFFLOWBBENDTYPE_UNCOND_JMP
1570 || enmType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP)
1571 DBGFR3FlowBbGetBranchAddress(hFlowBb, &pDumpBb->AddrTarget);
1572
1573 if (fFlags & DBGF_FLOW_BB_F_INCOMPLETE_ERR)
1574 {
1575 const char *pszErr = NULL;
1576 DBGFR3FlowBbQueryError(hFlowBb, &pszErr);
1577 if (pszErr)
1578 {
1579 pDumpBb->cchHeight++;
1580 pDumpBb->cchWidth = RT_MAX(pDumpBb->cchWidth, (uint32_t)strlen(pszErr));
1581 }
1582 }
1583 for (unsigned i = 0; i < cInstr; i++)
1584 {
1585 const char *pszInstr = NULL;
1586 int rc = DBGFR3FlowBbQueryInstr(hFlowBb, i, NULL, NULL, &pszInstr);
1587 AssertRC(rc);
1588 pDumpBb->cchWidth = RT_MAX(pDumpBb->cchWidth, (uint32_t)strlen(pszInstr));
1589 }
1590 pDumpBb->cchWidth += 4; /* Include spacing and border left and right. */
1591}
1592
1593
1594/**
1595 * Dumps a top or bottom boundary line.
1596 *
1597 * @returns nothing.
1598 * @param hScreen The screen to draw to.
1599 * @param uStartX Where to start drawing the boundary.
1600 * @param uStartY Y coordinate.
1601 * @param cchWidth Width of the boundary.
1602 * @param enmColor The color to use for drawing.
1603 */
1604static void dbgcCmdUnassembleCfgDumpBbBoundary(DBGCSCREEN hScreen, uint32_t uStartX, uint32_t uStartY, uint32_t cchWidth,
1605 DBGCSCREENCOLOR enmColor)
1606{
1607 dbgcScreenAsciiDrawCharacter(hScreen, uStartX, uStartY, '+', enmColor);
1608 dbgcScreenAsciiDrawLineHorizontal(hScreen, uStartX + 1, uStartX + 1 + cchWidth - 2,
1609 uStartY, '-', enmColor);
1610 dbgcScreenAsciiDrawCharacter(hScreen, uStartX + cchWidth - 1, uStartY, '+', enmColor);
1611}
1612
1613
1614/**
1615 * Dumps a spacing line between the top or bottom boundary and the actual disassembly.
1616 *
1617 * @returns nothing.
1618 * @param hScreen The screen to draw to.
1619 * @param uStartX Where to start drawing the spacing.
1620 * @param uStartY Y coordinate.
1621 * @param cchWidth Width of the spacing.
1622 * @param enmColor The color to use for drawing.
1623 */
1624static void dbgcCmdUnassembleCfgDumpBbSpacing(DBGCSCREEN hScreen, uint32_t uStartX, uint32_t uStartY, uint32_t cchWidth,
1625 DBGCSCREENCOLOR enmColor)
1626{
1627 dbgcScreenAsciiDrawCharacter(hScreen, uStartX, uStartY, '|', enmColor);
1628 dbgcScreenAsciiDrawLineHorizontal(hScreen, uStartX + 1, uStartX + 1 + cchWidth - 2,
1629 uStartY, ' ', enmColor);
1630 dbgcScreenAsciiDrawCharacter(hScreen, uStartX + cchWidth - 1, uStartY, '|', enmColor);
1631}
1632
1633
1634/**
1635 * Writes a given text to the screen.
1636 *
1637 * @returns nothing.
1638 * @param hScreen The screen to draw to.
1639 * @param uStartX Where to start drawing the line.
1640 * @param uStartY Y coordinate.
1641 * @param cchWidth Maximum width of the text.
1642 * @param pszText The text to write.
1643 * @param enmTextColor The color to use for drawing the text.
1644 * @param enmBorderColor The color to use for drawing the border.
1645 */
1646static void dbgcCmdUnassembleCfgDumpBbText(DBGCSCREEN hScreen, uint32_t uStartX, uint32_t uStartY,
1647 uint32_t cchWidth, const char *pszText,
1648 DBGCSCREENCOLOR enmTextColor, DBGCSCREENCOLOR enmBorderColor)
1649{
1650 dbgcScreenAsciiDrawCharacter(hScreen, uStartX, uStartY, '|', enmBorderColor);
1651 dbgcScreenAsciiDrawCharacter(hScreen, uStartX + 1, uStartY, ' ', enmTextColor);
1652 dbgcScreenAsciiDrawString(hScreen, uStartX + 2, uStartY, pszText, enmTextColor);
1653 dbgcScreenAsciiDrawCharacter(hScreen, uStartX + cchWidth - 1, uStartY, '|', enmBorderColor);
1654}
1655
1656
1657/**
1658 * Dumps one basic block using the dumper callback.
1659 *
1660 * @returns nothing.
1661 * @param pDumpBb The basic block dump state to dump.
1662 * @param hScreen The screen to draw to.
1663 */
1664static void dbgcCmdUnassembleCfgDumpBb(PDBGCFLOWBBDUMP pDumpBb, DBGCSCREEN hScreen)
1665{
1666 uint32_t uStartY = pDumpBb->uStartY;
1667 bool fError = RT_BOOL(DBGFR3FlowBbGetFlags(pDumpBb->hFlowBb) & DBGF_FLOW_BB_F_INCOMPLETE_ERR);
1668 DBGCSCREENCOLOR enmColor = fError ? DBGCSCREENCOLOR_RED_BRIGHT : DBGCSCREENCOLOR_DEFAULT;
1669
1670 dbgcCmdUnassembleCfgDumpBbBoundary(hScreen, pDumpBb->uStartX, uStartY, pDumpBb->cchWidth, enmColor);
1671 uStartY++;
1672 dbgcCmdUnassembleCfgDumpBbSpacing(hScreen, pDumpBb->uStartX, uStartY, pDumpBb->cchWidth, enmColor);
1673 uStartY++;
1674
1675 uint32_t cInstr = DBGFR3FlowBbGetInstrCount(pDumpBb->hFlowBb);
1676 for (unsigned i = 0; i < cInstr; i++)
1677 {
1678 const char *pszInstr = NULL;
1679 DBGFR3FlowBbQueryInstr(pDumpBb->hFlowBb, i, NULL, NULL, &pszInstr);
1680 dbgcCmdUnassembleCfgDumpBbText(hScreen, pDumpBb->uStartX, uStartY + i,
1681 pDumpBb->cchWidth, pszInstr, DBGCSCREENCOLOR_DEFAULT,
1682 enmColor);
1683 }
1684 uStartY += cInstr;
1685
1686 if (fError)
1687 {
1688 const char *pszErr = NULL;
1689 DBGFR3FlowBbQueryError(pDumpBb->hFlowBb, &pszErr);
1690 if (pszErr)
1691 dbgcCmdUnassembleCfgDumpBbText(hScreen, pDumpBb->uStartX, uStartY,
1692 pDumpBb->cchWidth, pszErr, enmColor,
1693 enmColor);
1694 uStartY++;
1695 }
1696
1697 dbgcCmdUnassembleCfgDumpBbSpacing(hScreen, pDumpBb->uStartX, uStartY, pDumpBb->cchWidth, enmColor);
1698 uStartY++;
1699 dbgcCmdUnassembleCfgDumpBbBoundary(hScreen, pDumpBb->uStartX, uStartY, pDumpBb->cchWidth, enmColor);
1700 uStartY++;
1701}
1702
1703
1704/**
1705 * Dumps one branch table using the dumper callback.
1706 *
1707 * @returns nothing.
1708 * @param pDumpBranchTbl The basic block dump state to dump.
1709 * @param hScreen The screen to draw to.
1710 */
1711static void dbgcCmdUnassembleCfgDumpBranchTbl(PDBGCFLOWBRANCHTBLDUMP pDumpBranchTbl, DBGCSCREEN hScreen)
1712{
1713 uint32_t uStartY = pDumpBranchTbl->uStartY;
1714 DBGCSCREENCOLOR enmColor = DBGCSCREENCOLOR_CYAN_BRIGHT;
1715
1716 dbgcCmdUnassembleCfgDumpBbBoundary(hScreen, pDumpBranchTbl->uStartX, uStartY, pDumpBranchTbl->cchWidth, enmColor);
1717 uStartY++;
1718 dbgcCmdUnassembleCfgDumpBbSpacing(hScreen, pDumpBranchTbl->uStartX, uStartY, pDumpBranchTbl->cchWidth, enmColor);
1719 uStartY++;
1720
1721 uint32_t cSlots = DBGFR3FlowBranchTblGetSlots(pDumpBranchTbl->hFlowBranchTbl);
1722 for (unsigned i = 0; i < cSlots; i++)
1723 {
1724 DBGFADDRESS Addr;
1725 char szAddr[128];
1726
1727 RT_ZERO(szAddr);
1728 DBGFR3FlowBranchTblGetAddrAtSlot(pDumpBranchTbl->hFlowBranchTbl, i, &Addr);
1729
1730 if (Addr.Sel == DBGF_SEL_FLAT)
1731 RTStrPrintf(&szAddr[0], sizeof(szAddr), "%RGv", Addr.FlatPtr);
1732 else
1733 RTStrPrintf(&szAddr[0], sizeof(szAddr), "%04x:%RGv", Addr.Sel, Addr.off);
1734
1735 dbgcCmdUnassembleCfgDumpBbText(hScreen, pDumpBranchTbl->uStartX, uStartY + i,
1736 pDumpBranchTbl->cchWidth, &szAddr[0], DBGCSCREENCOLOR_DEFAULT,
1737 enmColor);
1738 }
1739 uStartY += cSlots;
1740
1741 dbgcCmdUnassembleCfgDumpBbSpacing(hScreen, pDumpBranchTbl->uStartX, uStartY, pDumpBranchTbl->cchWidth, enmColor);
1742 uStartY++;
1743 dbgcCmdUnassembleCfgDumpBbBoundary(hScreen, pDumpBranchTbl->uStartX, uStartY, pDumpBranchTbl->cchWidth, enmColor);
1744 uStartY++;
1745}
1746
1747
1748/**
1749 * Fills in the dump states for the basic blocks and branch tables.
1750 *
1751 * @returns VBox status code.
1752 * @param hFlowIt The control flow graph iterator handle.
1753 * @param hFlowBranchTblIt The control flow graph branch table iterator handle.
1754 * @param paDumpBb The array of basic block dump states.
1755 * @param paDumpBranchTbl The array of branch table dump states.
1756 * @param cBbs Number of basic blocks.
1757 * @param cBranchTbls Number of branch tables.
1758 */
1759static int dbgcCmdUnassembleCfgDumpCalcDimensions(DBGFFLOWIT hFlowIt, DBGFFLOWBRANCHTBLIT hFlowBranchTblIt,
1760 PDBGCFLOWBBDUMP paDumpBb, PDBGCFLOWBRANCHTBLDUMP paDumpBranchTbl,
1761 uint32_t cBbs, uint32_t cBranchTbls)
1762{
1763 RT_NOREF2(cBbs, cBranchTbls);
1764
1765 /* Calculate the sizes of each basic block first. */
1766 DBGFFLOWBB hFlowBb = DBGFR3FlowItNext(hFlowIt);
1767 uint32_t idx = 0;
1768 while (hFlowBb)
1769 {
1770 dbgcCmdUnassembleCfgDumpCalcBbSize(hFlowBb, &paDumpBb[idx]);
1771 idx++;
1772 hFlowBb = DBGFR3FlowItNext(hFlowIt);
1773 }
1774
1775 if (paDumpBranchTbl)
1776 {
1777 idx = 0;
1778 DBGFFLOWBRANCHTBL hFlowBranchTbl = DBGFR3FlowBranchTblItNext(hFlowBranchTblIt);
1779 while (hFlowBranchTbl)
1780 {
1781 paDumpBranchTbl[idx].hFlowBranchTbl = hFlowBranchTbl;
1782 paDumpBranchTbl[idx].cchHeight = DBGFR3FlowBranchTblGetSlots(hFlowBranchTbl) + 4; /* Spacing and border. */
1783 paDumpBranchTbl[idx].cchWidth = 25 + 4; /* Spacing and border. */
1784 idx++;
1785 hFlowBranchTbl = DBGFR3FlowBranchTblItNext(hFlowBranchTblIt);
1786 }
1787 }
1788
1789 return VINF_SUCCESS;
1790}
1791
1792/**
1793 * Dumps the given control flow graph to the output.
1794 *
1795 * @returns VBox status code.
1796 * @param hCfg The control flow graph handle.
1797 * @param fUseColor Flag whether the output should be colorized.
1798 * @param pCmdHlp The command helper callback table.
1799 */
1800static int dbgcCmdUnassembleCfgDump(DBGFFLOW hCfg, bool fUseColor, PDBGCCMDHLP pCmdHlp)
1801{
1802 int rc = VINF_SUCCESS;
1803 DBGFFLOWIT hCfgIt = NULL;
1804 DBGFFLOWBRANCHTBLIT hFlowBranchTblIt = NULL;
1805 uint32_t cBbs = DBGFR3FlowGetBbCount(hCfg);
1806 uint32_t cBranchTbls = DBGFR3FlowGetBranchTblCount(hCfg);
1807 PDBGCFLOWBBDUMP paDumpBb = (PDBGCFLOWBBDUMP)RTMemTmpAllocZ(cBbs * sizeof(DBGCFLOWBBDUMP));
1808 PDBGCFLOWBRANCHTBLDUMP paDumpBranchTbl = NULL;
1809
1810 if (cBranchTbls)
1811 paDumpBranchTbl = (PDBGCFLOWBRANCHTBLDUMP)RTMemAllocZ(cBranchTbls * sizeof(DBGCFLOWBRANCHTBLDUMP));
1812
1813 if (RT_UNLIKELY(!paDumpBb || (!paDumpBranchTbl && cBranchTbls > 0)))
1814 rc = VERR_NO_MEMORY;
1815 if (RT_SUCCESS(rc))
1816 rc = DBGFR3FlowItCreate(hCfg, DBGFFLOWITORDER_BY_ADDR_LOWEST_FIRST, &hCfgIt);
1817 if (RT_SUCCESS(rc) && cBranchTbls > 0)
1818 rc = DBGFR3FlowBranchTblItCreate(hCfg, DBGFFLOWITORDER_BY_ADDR_LOWEST_FIRST, &hFlowBranchTblIt);
1819
1820 if (RT_SUCCESS(rc))
1821 {
1822 rc = dbgcCmdUnassembleCfgDumpCalcDimensions(hCfgIt, hFlowBranchTblIt, paDumpBb, paDumpBranchTbl,
1823 cBbs, cBranchTbls);
1824
1825 /* Calculate the ASCII screen dimensions and create one. */
1826 uint32_t cchWidth = 0;
1827 uint32_t cchLeftExtra = 5;
1828 uint32_t cchRightExtra = 5;
1829 uint32_t cchHeight = 0;
1830 for (unsigned i = 0; i < cBbs; i++)
1831 {
1832 PDBGCFLOWBBDUMP pDumpBb = &paDumpBb[i];
1833 cchWidth = RT_MAX(cchWidth, pDumpBb->cchWidth);
1834 cchHeight += pDumpBb->cchHeight;
1835
1836 /* Incomplete blocks don't have a successor. */
1837 if (DBGFR3FlowBbGetFlags(pDumpBb->hFlowBb) & DBGF_FLOW_BB_F_INCOMPLETE_ERR)
1838 continue;
1839
1840 switch (DBGFR3FlowBbGetType(pDumpBb->hFlowBb))
1841 {
1842 case DBGFFLOWBBENDTYPE_EXIT:
1843 case DBGFFLOWBBENDTYPE_LAST_DISASSEMBLED:
1844 break;
1845 case DBGFFLOWBBENDTYPE_UNCOND_JMP:
1846 if ( dbgcCmdUnassembleCfgAddrLower(&pDumpBb->AddrTarget, &pDumpBb->AddrStart)
1847 || dbgcCmdUnassembleCfgAddrEqual(&pDumpBb->AddrTarget, &pDumpBb->AddrStart))
1848 cchLeftExtra++;
1849 else
1850 cchRightExtra++;
1851 break;
1852 case DBGFFLOWBBENDTYPE_UNCOND:
1853 cchHeight += 2; /* For the arrow down to the next basic block. */
1854 break;
1855 case DBGFFLOWBBENDTYPE_COND:
1856 cchHeight += 2; /* For the arrow down to the next basic block. */
1857 if ( dbgcCmdUnassembleCfgAddrLower(&pDumpBb->AddrTarget, &pDumpBb->AddrStart)
1858 || dbgcCmdUnassembleCfgAddrEqual(&pDumpBb->AddrTarget, &pDumpBb->AddrStart))
1859 cchLeftExtra++;
1860 else
1861 cchRightExtra++;
1862 break;
1863 case DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP:
1864 default:
1865 AssertFailed();
1866 }
1867 }
1868
1869 for (unsigned i = 0; i < cBranchTbls; i++)
1870 {
1871 PDBGCFLOWBRANCHTBLDUMP pDumpBranchTbl = &paDumpBranchTbl[i];
1872 cchWidth = RT_MAX(cchWidth, pDumpBranchTbl->cchWidth);
1873 cchHeight += pDumpBranchTbl->cchHeight;
1874 }
1875
1876 cchWidth += 2;
1877
1878 DBGCSCREEN hScreen = NULL;
1879 rc = dbgcScreenAsciiCreate(&hScreen, cchWidth + cchLeftExtra + cchRightExtra, cchHeight);
1880 if (RT_SUCCESS(rc))
1881 {
1882 uint32_t uY = 0;
1883
1884 /* Dump the branch tables first. */
1885 for (unsigned i = 0; i < cBranchTbls; i++)
1886 {
1887 paDumpBranchTbl[i].uStartX = cchLeftExtra + (cchWidth - paDumpBranchTbl[i].cchWidth) / 2;
1888 paDumpBranchTbl[i].uStartY = uY;
1889 dbgcCmdUnassembleCfgDumpBranchTbl(&paDumpBranchTbl[i], hScreen);
1890 uY += paDumpBranchTbl[i].cchHeight;
1891 }
1892
1893 /* Dump the basic blocks and connections to the immediate successor. */
1894 for (unsigned i = 0; i < cBbs; i++)
1895 {
1896 paDumpBb[i].uStartX = cchLeftExtra + (cchWidth - paDumpBb[i].cchWidth) / 2;
1897 paDumpBb[i].uStartY = uY;
1898 dbgcCmdUnassembleCfgDumpBb(&paDumpBb[i], hScreen);
1899 uY += paDumpBb[i].cchHeight;
1900
1901 /* Incomplete blocks don't have a successor. */
1902 if (DBGFR3FlowBbGetFlags(paDumpBb[i].hFlowBb) & DBGF_FLOW_BB_F_INCOMPLETE_ERR)
1903 continue;
1904
1905 switch (DBGFR3FlowBbGetType(paDumpBb[i].hFlowBb))
1906 {
1907 case DBGFFLOWBBENDTYPE_EXIT:
1908 case DBGFFLOWBBENDTYPE_LAST_DISASSEMBLED:
1909 case DBGFFLOWBBENDTYPE_UNCOND_JMP:
1910 case DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP:
1911 break;
1912 case DBGFFLOWBBENDTYPE_UNCOND:
1913 /* Draw the arrow down to the next block. */
1914 dbgcScreenAsciiDrawCharacter(hScreen, cchLeftExtra + cchWidth / 2, uY,
1915 '|', DBGCSCREENCOLOR_BLUE_BRIGHT);
1916 uY++;
1917 dbgcScreenAsciiDrawCharacter(hScreen, cchLeftExtra + cchWidth / 2, uY,
1918 'V', DBGCSCREENCOLOR_BLUE_BRIGHT);
1919 uY++;
1920 break;
1921 case DBGFFLOWBBENDTYPE_COND:
1922 /* Draw the arrow down to the next block. */
1923 dbgcScreenAsciiDrawCharacter(hScreen, cchLeftExtra + cchWidth / 2, uY,
1924 '|', DBGCSCREENCOLOR_RED_BRIGHT);
1925 uY++;
1926 dbgcScreenAsciiDrawCharacter(hScreen, cchLeftExtra + cchWidth / 2, uY,
1927 'V', DBGCSCREENCOLOR_RED_BRIGHT);
1928 uY++;
1929 break;
1930 default:
1931 AssertFailed();
1932 }
1933 }
1934
1935 /* Last pass, connect all remaining branches. */
1936 uint32_t uBackConns = 0;
1937 uint32_t uFwdConns = 0;
1938 for (unsigned i = 0; i < cBbs; i++)
1939 {
1940 PDBGCFLOWBBDUMP pDumpBb = &paDumpBb[i];
1941 DBGFFLOWBBENDTYPE enmEndType = DBGFR3FlowBbGetType(pDumpBb->hFlowBb);
1942
1943 /* Incomplete blocks don't have a successor. */
1944 if (DBGFR3FlowBbGetFlags(pDumpBb->hFlowBb) & DBGF_FLOW_BB_F_INCOMPLETE_ERR)
1945 continue;
1946
1947 switch (enmEndType)
1948 {
1949 case DBGFFLOWBBENDTYPE_EXIT:
1950 case DBGFFLOWBBENDTYPE_LAST_DISASSEMBLED:
1951 case DBGFFLOWBBENDTYPE_UNCOND:
1952 break;
1953 case DBGFFLOWBBENDTYPE_COND:
1954 case DBGFFLOWBBENDTYPE_UNCOND_JMP:
1955 {
1956 /* Find the target first to get the coordinates. */
1957 PDBGCFLOWBBDUMP pDumpBbTgt = NULL;
1958 for (unsigned idxDumpBb = 0; idxDumpBb < cBbs; idxDumpBb++)
1959 {
1960 pDumpBbTgt = &paDumpBb[idxDumpBb];
1961 if (dbgcCmdUnassembleCfgAddrEqual(&pDumpBb->AddrTarget, &pDumpBbTgt->AddrStart))
1962 break;
1963 }
1964
1965 DBGCSCREENCOLOR enmColor = enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP
1966 ? DBGCSCREENCOLOR_YELLOW_BRIGHT
1967 : DBGCSCREENCOLOR_GREEN_BRIGHT;
1968
1969 /*
1970 * Use the right side for targets with higher addresses,
1971 * left when jumping backwards.
1972 */
1973 if ( dbgcCmdUnassembleCfgAddrLower(&pDumpBb->AddrTarget, &pDumpBb->AddrStart)
1974 || dbgcCmdUnassembleCfgAddrEqual(&pDumpBb->AddrTarget, &pDumpBb->AddrStart))
1975 {
1976 /* Going backwards. */
1977 uint32_t uXVerLine = /*cchLeftExtra - 1 -*/ uBackConns + 1;
1978 uint32_t uYHorLine = pDumpBb->uStartY + pDumpBb->cchHeight - 1 - 2;
1979 uBackConns++;
1980
1981 /* Draw the arrow pointing to the target block. */
1982 dbgcScreenAsciiDrawCharacter(hScreen, pDumpBbTgt->uStartX - 1, pDumpBbTgt->uStartY,
1983 '>', enmColor);
1984 /* Draw the horizontal line. */
1985 dbgcScreenAsciiDrawLineHorizontal(hScreen, uXVerLine + 1, pDumpBbTgt->uStartX - 2,
1986 pDumpBbTgt->uStartY, '-', enmColor);
1987 dbgcScreenAsciiDrawCharacter(hScreen, uXVerLine, pDumpBbTgt->uStartY, '+',
1988 enmColor);
1989 /* Draw the vertical line down to the source block. */
1990 dbgcScreenAsciiDrawLineVertical(hScreen, uXVerLine, pDumpBbTgt->uStartY + 1, uYHorLine - 1,
1991 '|', enmColor);
1992 dbgcScreenAsciiDrawCharacter(hScreen, uXVerLine, uYHorLine, '+', enmColor);
1993 /* Draw the horizontal connection between the source block and vertical part. */
1994 dbgcScreenAsciiDrawLineHorizontal(hScreen, uXVerLine + 1, pDumpBb->uStartX - 1,
1995 uYHorLine, '-', enmColor);
1996
1997 }
1998 else
1999 {
2000 /* Going forward. */
2001 uint32_t uXVerLine = cchWidth + cchLeftExtra + (cchRightExtra - uFwdConns) - 1;
2002 uint32_t uYHorLine = pDumpBb->uStartY + pDumpBb->cchHeight - 1 - 2;
2003 uFwdConns++;
2004
2005 /* Draw the horizontal line. */
2006 dbgcScreenAsciiDrawLineHorizontal(hScreen, pDumpBb->uStartX + pDumpBb->cchWidth,
2007 uXVerLine - 1, uYHorLine, '-', enmColor);
2008 dbgcScreenAsciiDrawCharacter(hScreen, uXVerLine, uYHorLine, '+', enmColor);
2009 /* Draw the vertical line down to the target block. */
2010 dbgcScreenAsciiDrawLineVertical(hScreen, uXVerLine, uYHorLine + 1, pDumpBbTgt->uStartY - 1,
2011 '|', enmColor);
2012 /* Draw the horizontal connection between the target block and vertical part. */
2013 dbgcScreenAsciiDrawLineHorizontal(hScreen, pDumpBbTgt->uStartX + pDumpBbTgt->cchWidth,
2014 uXVerLine, pDumpBbTgt->uStartY, '-', enmColor);
2015 dbgcScreenAsciiDrawCharacter(hScreen, uXVerLine, pDumpBbTgt->uStartY, '+',
2016 enmColor);
2017 /* Draw the arrow pointing to the target block. */
2018 dbgcScreenAsciiDrawCharacter(hScreen, pDumpBbTgt->uStartX + pDumpBbTgt->cchWidth,
2019 pDumpBbTgt->uStartY, '<', enmColor);
2020 }
2021 break;
2022 }
2023 case DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP:
2024 default:
2025 AssertFailed();
2026 }
2027 }
2028
2029 rc = dbgcScreenAsciiBlit(hScreen, dbgcCmdUnassembleCfgBlit, pCmdHlp, fUseColor);
2030 dbgcScreenAsciiDestroy(hScreen);
2031 }
2032 }
2033
2034 if (paDumpBb)
2035 {
2036 for (unsigned i = 0; i < cBbs; i++)
2037 DBGFR3FlowBbRelease(paDumpBb[i].hFlowBb);
2038 RTMemTmpFree(paDumpBb);
2039 }
2040
2041 if (paDumpBranchTbl)
2042 {
2043 for (unsigned i = 0; i < cBranchTbls; i++)
2044 DBGFR3FlowBranchTblRelease(paDumpBranchTbl[i].hFlowBranchTbl);
2045 RTMemTmpFree(paDumpBranchTbl);
2046 }
2047
2048 if (hCfgIt)
2049 DBGFR3FlowItDestroy(hCfgIt);
2050 if (hFlowBranchTblIt)
2051 DBGFR3FlowBranchTblItDestroy(hFlowBranchTblIt);
2052
2053 return rc;
2054}
2055
2056
2057/**
2058 * @callback_method_impl{FNDBGCCMD, The 'ucfg' command.}
2059 */
2060static DECLCALLBACK(int) dbgcCmdUnassembleCfg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2061{
2062 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2063
2064 /*
2065 * Validate input.
2066 */
2067 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2068 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs <= 1);
2069 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 0 || DBGCVAR_ISPOINTER(paArgs[0].enmType));
2070
2071 if (!cArgs && !DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
2072 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Don't know where to start disassembling");
2073
2074 /*
2075 * Check the desired mode.
2076 */
2077 unsigned fFlags = DBGF_DISAS_FLAGS_UNPATCHED_BYTES | DBGF_DISAS_FLAGS_ANNOTATE_PATCHED;
2078 bool fUseColor = false;
2079 switch (pCmd->pszCmd[4])
2080 {
2081 default: AssertFailed(); RT_FALL_THRU();
2082 case '\0': fFlags |= DBGF_DISAS_FLAGS_DEFAULT_MODE; break;
2083 case '6': fFlags |= DBGF_DISAS_FLAGS_64BIT_MODE; break;
2084 case '3': fFlags |= DBGF_DISAS_FLAGS_32BIT_MODE; break;
2085 case '1': fFlags |= DBGF_DISAS_FLAGS_16BIT_MODE; break;
2086 case 'v': fFlags |= DBGF_DISAS_FLAGS_16BIT_REAL_MODE; break;
2087 case 'c': fUseColor = true; break;
2088 }
2089
2090 /** @todo should use DBGFADDRESS for everything */
2091
2092 /*
2093 * Find address.
2094 */
2095 if (!cArgs)
2096 {
2097 if (!DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
2098 {
2099 /** @todo Batch query CS, RIP, CPU mode and flags. */
2100 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
2101 if (CPUMIsGuestIn64BitCode(pVCpu))
2102 {
2103 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FLAT;
2104 pDbgc->SourcePos.u.GCFlat = CPUMGetGuestRIP(pVCpu);
2105 }
2106 else
2107 {
2108 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FAR;
2109 pDbgc->SourcePos.u.GCFar.off = CPUMGetGuestEIP(pVCpu);
2110 pDbgc->SourcePos.u.GCFar.sel = CPUMGetGuestCS(pVCpu);
2111 if ( (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_DEFAULT_MODE
2112 && (CPUMGetGuestEFlags(pVCpu) & X86_EFL_VM))
2113 {
2114 fFlags &= ~DBGF_DISAS_FLAGS_MODE_MASK;
2115 fFlags |= DBGF_DISAS_FLAGS_16BIT_REAL_MODE;
2116 }
2117 }
2118
2119 fFlags |= DBGF_DISAS_FLAGS_CURRENT_GUEST;
2120 }
2121 else if ((fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_DEFAULT_MODE && pDbgc->fDisasm)
2122 {
2123 fFlags &= ~DBGF_DISAS_FLAGS_MODE_MASK;
2124 fFlags |= pDbgc->fDisasm & DBGF_DISAS_FLAGS_MODE_MASK;
2125 }
2126 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_NONE;
2127 }
2128 else
2129 pDbgc->DisasmPos = paArgs[0];
2130 pDbgc->pLastPos = &pDbgc->DisasmPos;
2131
2132 /*
2133 * Range.
2134 */
2135 switch (pDbgc->DisasmPos.enmRangeType)
2136 {
2137 case DBGCVAR_RANGE_NONE:
2138 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
2139 pDbgc->DisasmPos.u64Range = 10;
2140 break;
2141
2142 case DBGCVAR_RANGE_ELEMENTS:
2143 if (pDbgc->DisasmPos.u64Range > 2048)
2144 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Too many lines requested. Max is 2048 lines");
2145 break;
2146
2147 case DBGCVAR_RANGE_BYTES:
2148 if (pDbgc->DisasmPos.u64Range > 65536)
2149 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The requested range is too big. Max is 64KB");
2150 break;
2151
2152 default:
2153 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Unknown range type %d", pDbgc->DisasmPos.enmRangeType);
2154 }
2155
2156 /*
2157 * Convert physical and host addresses to guest addresses.
2158 */
2159 RTDBGAS hDbgAs = pDbgc->hDbgAs;
2160 int rc;
2161 switch (pDbgc->DisasmPos.enmType)
2162 {
2163 case DBGCVAR_TYPE_GC_FLAT:
2164 case DBGCVAR_TYPE_GC_FAR:
2165 break;
2166 case DBGCVAR_TYPE_GC_PHYS:
2167 hDbgAs = DBGF_AS_PHYS;
2168 RT_FALL_THRU();
2169 case DBGCVAR_TYPE_HC_FLAT:
2170 case DBGCVAR_TYPE_HC_PHYS:
2171 {
2172 DBGCVAR VarTmp;
2173 rc = DBGCCmdHlpEval(pCmdHlp, &VarTmp, "%%(%Dv)", &pDbgc->DisasmPos);
2174 if (RT_FAILURE(rc))
2175 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "failed to evaluate '%%(%Dv)'", &pDbgc->DisasmPos);
2176 pDbgc->DisasmPos = VarTmp;
2177 break;
2178 }
2179 default: AssertFailed(); break;
2180 }
2181
2182 DBGFADDRESS CurAddr;
2183 if ( (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_16BIT_REAL_MODE
2184 && pDbgc->DisasmPos.enmType == DBGCVAR_TYPE_GC_FAR)
2185 DBGFR3AddrFromFlat(pUVM, &CurAddr, ((uint32_t)pDbgc->DisasmPos.u.GCFar.sel << 4) + pDbgc->DisasmPos.u.GCFar.off);
2186 else
2187 {
2188 rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &pDbgc->DisasmPos, &CurAddr);
2189 if (RT_FAILURE(rc))
2190 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr failed on '%Dv'", &pDbgc->DisasmPos);
2191 }
2192
2193 DBGFFLOW hCfg;
2194 rc = DBGFR3FlowCreate(pUVM, pDbgc->idCpu, &CurAddr, 0 /*cbDisasmMax*/,
2195 DBGF_FLOW_CREATE_F_TRY_RESOLVE_INDIRECT_BRANCHES, fFlags, &hCfg);
2196 if (RT_SUCCESS(rc))
2197 {
2198 /* Dump the graph. */
2199 rc = dbgcCmdUnassembleCfgDump(hCfg, fUseColor, pCmdHlp);
2200 DBGFR3FlowRelease(hCfg);
2201 }
2202 else
2203 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3FlowCreate failed on '%Dv'", &pDbgc->DisasmPos);
2204
2205 NOREF(pCmd);
2206 return rc;
2207}
2208
2209
2210/**
2211 * @callback_method_impl{FNDBGCCMD, The 'ls' command.}
2212 */
2213static DECLCALLBACK(int) dbgcCmdListSource(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2214{
2215 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2216
2217 /*
2218 * Validate input.
2219 */
2220 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
2221 if (cArgs == 1)
2222 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
2223 if (!pUVM && !cArgs && !DBGCVAR_ISPOINTER(pDbgc->SourcePos.enmType))
2224 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Don't know where to start listing...");
2225 if (!pUVM && cArgs && DBGCVAR_ISGCPOINTER(paArgs[0].enmType))
2226 return DBGCCmdHlpFail(pCmdHlp, pCmd, "GC address but no VM");
2227
2228 /*
2229 * Find address.
2230 */
2231 if (!cArgs)
2232 {
2233 if (!DBGCVAR_ISPOINTER(pDbgc->SourcePos.enmType))
2234 {
2235 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
2236 pDbgc->SourcePos.enmType = DBGCVAR_TYPE_GC_FAR;
2237 pDbgc->SourcePos.u.GCFar.off = CPUMGetGuestEIP(pVCpu);
2238 pDbgc->SourcePos.u.GCFar.sel = CPUMGetGuestCS(pVCpu);
2239 }
2240 pDbgc->SourcePos.enmRangeType = DBGCVAR_RANGE_NONE;
2241 }
2242 else
2243 pDbgc->SourcePos = paArgs[0];
2244 pDbgc->pLastPos = &pDbgc->SourcePos;
2245
2246 /*
2247 * Ensure the source address is flat GC.
2248 */
2249 switch (pDbgc->SourcePos.enmType)
2250 {
2251 case DBGCVAR_TYPE_GC_FLAT:
2252 break;
2253 case DBGCVAR_TYPE_GC_PHYS:
2254 case DBGCVAR_TYPE_GC_FAR:
2255 case DBGCVAR_TYPE_HC_FLAT:
2256 case DBGCVAR_TYPE_HC_PHYS:
2257 {
2258 int rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->SourcePos, "%%(%Dv)", &pDbgc->SourcePos);
2259 if (RT_FAILURE(rc))
2260 return DBGCCmdHlpPrintf(pCmdHlp, "error: Invalid address or address type. (rc=%d)\n", rc);
2261 break;
2262 }
2263 default: AssertFailed(); break;
2264 }
2265
2266 /*
2267 * Range.
2268 */
2269 switch (pDbgc->SourcePos.enmRangeType)
2270 {
2271 case DBGCVAR_RANGE_NONE:
2272 pDbgc->SourcePos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
2273 pDbgc->SourcePos.u64Range = 10;
2274 break;
2275
2276 case DBGCVAR_RANGE_ELEMENTS:
2277 if (pDbgc->SourcePos.u64Range > 2048)
2278 return DBGCCmdHlpPrintf(pCmdHlp, "error: Too many lines requested. Max is 2048 lines.\n");
2279 break;
2280
2281 case DBGCVAR_RANGE_BYTES:
2282 if (pDbgc->SourcePos.u64Range > 65536)
2283 return DBGCCmdHlpPrintf(pCmdHlp, "error: The requested range is too big. Max is 64KB.\n");
2284 break;
2285
2286 default:
2287 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: Unknown range type %d.\n", pDbgc->SourcePos.enmRangeType);
2288 }
2289
2290 /*
2291 * Do the disassembling.
2292 */
2293 bool fFirst = 1;
2294 RTDBGLINE LinePrev = { 0, 0, 0, 0, 0, "" };
2295 int iRangeLeft = (int)pDbgc->SourcePos.u64Range;
2296 if (iRangeLeft == 0) /* kludge for 'r'. */
2297 iRangeLeft = -1;
2298 for (;;)
2299 {
2300 /*
2301 * Get line info.
2302 */
2303 RTDBGLINE Line;
2304 RTGCINTPTR off;
2305 DBGFADDRESS SourcePosAddr;
2306 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &pDbgc->SourcePos, &SourcePosAddr);
2307 if (RT_FAILURE(rc))
2308 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,%Dv)", &pDbgc->SourcePos);
2309 rc = DBGFR3AsLineByAddr(pUVM, pDbgc->hDbgAs, &SourcePosAddr, &off, &Line, NULL);
2310 if (RT_FAILURE(rc))
2311 return VINF_SUCCESS;
2312
2313 unsigned cLines = 0;
2314 if (memcmp(&Line, &LinePrev, sizeof(Line)))
2315 {
2316 /*
2317 * Print filenamename
2318 */
2319 if (!fFirst && strcmp(Line.szFilename, LinePrev.szFilename))
2320 fFirst = true;
2321 if (fFirst)
2322 {
2323 rc = DBGCCmdHlpPrintf(pCmdHlp, "[%s @ %d]\n", Line.szFilename, Line.uLineNo);
2324 if (RT_FAILURE(rc))
2325 return rc;
2326 }
2327
2328 /*
2329 * Try open the file and read the line.
2330 */
2331 FILE *phFile = fopen(Line.szFilename, "r");
2332 if (phFile)
2333 {
2334 /* Skip ahead to the desired line. */
2335 char szLine[4096];
2336 unsigned cBefore = fFirst ? RT_MIN(2, Line.uLineNo - 1) : Line.uLineNo - LinePrev.uLineNo - 1;
2337 if (cBefore > 7)
2338 cBefore = 0;
2339 unsigned cLeft = Line.uLineNo - cBefore;
2340 while (cLeft > 0)
2341 {
2342 szLine[0] = '\0';
2343 if (!fgets(szLine, sizeof(szLine), phFile))
2344 break;
2345 cLeft--;
2346 }
2347 if (!cLeft)
2348 {
2349 /* print the before lines */
2350 for (;;)
2351 {
2352 size_t cch = strlen(szLine);
2353 while (cch > 0 && (szLine[cch - 1] == '\r' || szLine[cch - 1] == '\n' || RT_C_IS_SPACE(szLine[cch - 1])) )
2354 szLine[--cch] = '\0';
2355 if (cBefore-- <= 0)
2356 break;
2357
2358 rc = DBGCCmdHlpPrintf(pCmdHlp, " %4d: %s\n", Line.uLineNo - cBefore - 1, szLine);
2359 szLine[0] = '\0';
2360 const char *pszShutUpGcc = fgets(szLine, sizeof(szLine), phFile); NOREF(pszShutUpGcc);
2361 cLines++;
2362 }
2363 /* print the actual line */
2364 rc = DBGCCmdHlpPrintf(pCmdHlp, "%08llx %4d: %s\n", Line.Address, Line.uLineNo, szLine);
2365 }
2366 fclose(phFile);
2367 if (RT_FAILURE(rc))
2368 return rc;
2369 fFirst = false;
2370 }
2371 else
2372 return DBGCCmdHlpPrintf(pCmdHlp, "Warning: couldn't open source file '%s'\n", Line.szFilename);
2373
2374 LinePrev = Line;
2375 }
2376
2377
2378 /*
2379 * Advance
2380 */
2381 if (iRangeLeft < 0) /* 'r' */
2382 break;
2383 if (pDbgc->SourcePos.enmRangeType == DBGCVAR_RANGE_ELEMENTS)
2384 iRangeLeft -= cLines;
2385 else
2386 iRangeLeft -= 1;
2387 rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->SourcePos, "(%Dv) + %x", &pDbgc->SourcePos, 1);
2388 if (RT_FAILURE(rc))
2389 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->SourcePos, 1);
2390 if (iRangeLeft <= 0)
2391 break;
2392 }
2393
2394 NOREF(pCmd);
2395 return 0;
2396}
2397
2398
2399/**
2400 * @callback_method_impl{FNDBGCCMD, The 'r' command.}
2401 */
2402static DECLCALLBACK(int) dbgcCmdReg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2403{
2404 return dbgcCmdRegGuest(pCmd, pCmdHlp, pUVM, paArgs, cArgs);
2405}
2406
2407
2408/**
2409 * @callback_method_impl{FNDBGCCMD, Common worker for the dbgcCmdReg*()
2410 * commands.}
2411 */
2412static DECLCALLBACK(int) dbgcCmdRegCommon(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs,
2413 const char *pszPrefix)
2414{
2415 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2416 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 1 || cArgs == 2 || cArgs == 3);
2417 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[0].enmType == DBGCVAR_TYPE_STRING
2418 || paArgs[0].enmType == DBGCVAR_TYPE_SYMBOL);
2419
2420 /*
2421 * Parse the register name and kind.
2422 */
2423 const char *pszReg = paArgs[0].u.pszString;
2424 if (*pszReg == '@')
2425 pszReg++;
2426 VMCPUID idCpu = pDbgc->idCpu;
2427 if (*pszPrefix)
2428 idCpu |= DBGFREG_HYPER_VMCPUID;
2429 if (*pszReg == '.')
2430 {
2431 pszReg++;
2432 idCpu |= DBGFREG_HYPER_VMCPUID;
2433 }
2434 const char * const pszActualPrefix = idCpu & DBGFREG_HYPER_VMCPUID ? "." : "";
2435
2436 /*
2437 * Query the register type & value (the setter needs the type).
2438 */
2439 DBGFREGVALTYPE enmType;
2440 DBGFREGVAL Value;
2441 int rc = DBGFR3RegNmQuery(pUVM, idCpu, pszReg, &Value, &enmType);
2442 if (RT_FAILURE(rc))
2443 {
2444 if (rc == VERR_DBGF_REGISTER_NOT_FOUND)
2445 return DBGCCmdHlpVBoxError(pCmdHlp, VERR_INVALID_PARAMETER, "Unknown register: '%s%s'.\n",
2446 pszActualPrefix, pszReg);
2447 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegNmQuery failed querying '%s%s': %Rrc.\n",
2448 pszActualPrefix, pszReg, rc);
2449 }
2450 if (cArgs == 1)
2451 {
2452 /*
2453 * Show the register.
2454 */
2455 char szValue[160];
2456 rc = DBGFR3RegFormatValue(szValue, sizeof(szValue), &Value, enmType, true /*fSpecial*/);
2457 if (RT_SUCCESS(rc))
2458 rc = DBGCCmdHlpPrintf(pCmdHlp, "%s%s=%s\n", pszActualPrefix, pszReg, szValue);
2459 else
2460 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegFormatValue failed: %Rrc.\n", rc);
2461 }
2462 else
2463 {
2464 DBGCVAR NewValueTmp;
2465 PCDBGCVAR pNewValue;
2466 if (cArgs == 3)
2467 {
2468 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, paArgs[1].enmType == DBGCVAR_TYPE_STRING);
2469 if (strcmp(paArgs[1].u.pszString, "="))
2470 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Second argument must be '='.");
2471 pNewValue = &paArgs[2];
2472 }
2473 else
2474 {
2475 /* Not possible to convince the parser to support both codeview and
2476 windbg syntax and make the equal sign optional. Try help it. */
2477 /** @todo make DBGCCmdHlpConvert do more with strings. */
2478 rc = DBGCCmdHlpConvert(pCmdHlp, &paArgs[1], DBGCVAR_TYPE_NUMBER, true /*fConvSyms*/, &NewValueTmp);
2479 if (RT_FAILURE(rc))
2480 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "The last argument must be a value or valid symbol.");
2481 pNewValue = &NewValueTmp;
2482 }
2483
2484 /*
2485 * Modify the register.
2486 */
2487 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pNewValue->enmType == DBGCVAR_TYPE_NUMBER);
2488 if (enmType != DBGFREGVALTYPE_DTR)
2489 {
2490 enmType = DBGFREGVALTYPE_U64;
2491 rc = DBGCCmdHlpVarToNumber(pCmdHlp, pNewValue, &Value.u64);
2492 }
2493 else
2494 {
2495 enmType = DBGFREGVALTYPE_DTR;
2496 rc = DBGCCmdHlpVarToNumber(pCmdHlp, pNewValue, &Value.dtr.u64Base);
2497 if (RT_SUCCESS(rc) && pNewValue->enmRangeType != DBGCVAR_RANGE_NONE)
2498 Value.dtr.u32Limit = (uint32_t)pNewValue->u64Range;
2499 }
2500 if (RT_SUCCESS(rc))
2501 {
2502 rc = DBGFR3RegNmSet(pUVM, idCpu, pszReg, &Value, enmType);
2503 if (RT_FAILURE(rc))
2504 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegNmSet failed settings '%s%s': %Rrc\n",
2505 pszActualPrefix, pszReg, rc);
2506 if (rc != VINF_SUCCESS)
2507 DBGCCmdHlpPrintf(pCmdHlp, "%s: warning: %Rrc\n", pCmd->pszCmd, rc);
2508 }
2509 else
2510 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegFormatValue failed: %Rrc.\n", rc);
2511 }
2512 return rc;
2513}
2514
2515
2516/**
2517 * @callback_method_impl{FNDBGCCMD,
2518 * The 'rg'\, 'rg64' and 'rg32' commands\, worker for 'r'.}
2519 */
2520static DECLCALLBACK(int) dbgcCmdRegGuest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2521{
2522 /*
2523 * Show all registers our selves.
2524 */
2525 if (cArgs == 0)
2526 {
2527 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2528 bool const f64BitMode = !strcmp(pCmd->pszCmd, "rg64")
2529 || ( strcmp(pCmd->pszCmd, "rg32") != 0
2530 && DBGFR3CpuIsIn64BitCode(pUVM, pDbgc->idCpu));
2531 return DBGCCmdHlpRegPrintf(pCmdHlp, pDbgc->idCpu, f64BitMode, pDbgc->fRegTerse);
2532 }
2533 return dbgcCmdRegCommon(pCmd, pCmdHlp, pUVM, paArgs, cArgs, "");
2534}
2535
2536
2537/**
2538 * @callback_method_impl{FNDBGCCMD, The 'rt' command.}
2539 */
2540static DECLCALLBACK(int) dbgcCmdRegTerse(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2541{
2542 NOREF(pCmd); NOREF(pUVM); NOREF(paArgs); NOREF(cArgs);
2543
2544 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2545 pDbgc->fRegTerse = !pDbgc->fRegTerse;
2546 return DBGCCmdHlpPrintf(pCmdHlp, pDbgc->fRegTerse ? "info: Terse register info.\n" : "info: Verbose register info.\n");
2547}
2548
2549
2550/**
2551 * @callback_method_impl{FNDBGCCMD, The 'pr' and 'tr' commands.}
2552 */
2553static DECLCALLBACK(int) dbgcCmdStepTraceToggle(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2554{
2555 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2556 Assert(cArgs == 0); NOREF(pCmd); NOREF(pUVM); NOREF(paArgs); NOREF(cArgs);
2557
2558 /* Note! windbg accepts 'r' as a flag to 'p', 'pa', 'pc', 'pt', 't',
2559 'ta', 'tc' and 'tt'. We've simplified it. */
2560 pDbgc->fStepTraceRegs = !pDbgc->fStepTraceRegs;
2561 return VINF_SUCCESS;
2562}
2563
2564
2565/**
2566 * @callback_method_impl{FNDBGCCMD, The 'p'\, 'pc'\, 'pt'\, 't'\, 'tc'\, and 'tt' commands.}
2567 */
2568static DECLCALLBACK(int) dbgcCmdStepTrace(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2569{
2570 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2571 if (cArgs != 0)
2572 return DBGCCmdHlpFail(pCmdHlp, pCmd,
2573 "Sorry, but the '%s' command does not currently implement any arguments.\n", pCmd->pszCmd);
2574
2575 /* The 'count' has to be implemented by DBGC, whereas the
2576 filtering is taken care of by DBGF. */
2577
2578 /*
2579 * Convert the command to DBGF_STEP_F_XXX and other API input.
2580 */
2581 //DBGFADDRESS StackPop;
2582 PDBGFADDRESS pStackPop = NULL;
2583 RTGCPTR cbStackPop = 0;
2584 uint32_t cMaxSteps = pCmd->pszCmd[0] == 'p' ? _512K : _64K;
2585 uint32_t fFlags = pCmd->pszCmd[0] == 'p' ? DBGF_STEP_F_OVER : DBGF_STEP_F_INTO;
2586 if (pCmd->pszCmd[1] == 'c')
2587 fFlags |= DBGF_STEP_F_STOP_ON_CALL;
2588 else if (pCmd->pszCmd[1] == 't')
2589 fFlags |= DBGF_STEP_F_STOP_ON_RET;
2590 else if (pCmd->pszCmd[0] != 'p')
2591 cMaxSteps = 1;
2592 else
2593 {
2594 /** @todo consider passing RSP + 1 in for 'p' and something else sensible for
2595 * the 'pt' command. */
2596 }
2597
2598 int rc = DBGFR3StepEx(pUVM, pDbgc->idCpu, fFlags, NULL, pStackPop, cbStackPop, cMaxSteps);
2599 if (RT_SUCCESS(rc))
2600 pDbgc->fReady = false;
2601 else
2602 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3StepEx(,,%#x,) failed", fFlags);
2603
2604 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
2605 return rc;
2606}
2607
2608
2609/**
2610 * @callback_method_impl{FNDBGCCMD, The 'pa' and 'ta' commands.}
2611 */
2612static DECLCALLBACK(int) dbgcCmdStepTraceTo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2613{
2614 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2615 if (cArgs != 1)
2616 return DBGCCmdHlpFail(pCmdHlp, pCmd,
2617 "Sorry, but the '%s' command only implements a single argument at present.\n", pCmd->pszCmd);
2618 DBGFADDRESS Address;
2619 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
2620 if (RT_FAILURE(rc))
2621 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "VarToDbgfAddr(,%Dv,)\n", &paArgs[0]);
2622
2623 uint32_t cMaxSteps = pCmd->pszCmd[0] == 'p' ? _512K : 1;
2624 uint32_t fFlags = pCmd->pszCmd[0] == 'p' ? DBGF_STEP_F_OVER : DBGF_STEP_F_INTO;
2625 rc = DBGFR3StepEx(pUVM, pDbgc->idCpu, fFlags, &Address, NULL, 0, cMaxSteps);
2626 if (RT_SUCCESS(rc))
2627 pDbgc->fReady = false;
2628 else
2629 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3StepEx(,,%#x,) failed", fFlags);
2630 return rc;
2631}
2632
2633
2634/**
2635 * Helper that tries to resolve a far address to a symbol and formats it.
2636 *
2637 * @returns Pointer to symbol string on success, NULL if not resolved.
2638 * Free using RTStrFree.
2639 * @param pCmdHlp The command helper structure.
2640 * @param hAs The address space to use. NIL_RTDBGAS means no symbol resolving.
2641 * @param sel The selector part of the address.
2642 * @param off The offset part of the address.
2643 * @param pszPrefix How to prefix the symbol string.
2644 * @param pszSuffix How to suffix the symbol string.
2645 */
2646static char *dbgcCmdHlpFarAddrToSymbol(PDBGCCMDHLP pCmdHlp, RTDBGAS hAs, RTSEL sel, uint64_t off,
2647 const char *pszPrefix, const char *pszSuffix)
2648{
2649 char *pszRet = NULL;
2650 if (hAs != NIL_RTDBGAS)
2651 {
2652 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2653 DBGFADDRESS Addr;
2654 int rc = DBGFR3AddrFromSelOff(pDbgc->pUVM, pDbgc->idCpu, &Addr, sel, off);
2655 if (RT_SUCCESS(rc))
2656 {
2657 RTGCINTPTR offDispSym = 0;
2658 PRTDBGSYMBOL pSymbol = DBGFR3AsSymbolByAddrA(pDbgc->pUVM, hAs, &Addr,
2659 RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL
2660 | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
2661 &offDispSym, NULL);
2662 if (pSymbol)
2663 {
2664 if (offDispSym == 0)
2665 pszRet = RTStrAPrintf2("%s%s%s", pszPrefix, pSymbol->szName, pszSuffix);
2666 else if (offDispSym > 0)
2667 pszRet = RTStrAPrintf2("%s%s+%llx%s", pszPrefix, pSymbol->szName, (int64_t)offDispSym, pszSuffix);
2668 else
2669 pszRet = RTStrAPrintf2("%s%s-%llx%s", pszPrefix, pSymbol->szName, -(int64_t)offDispSym, pszSuffix);
2670 RTDbgSymbolFree(pSymbol);
2671 }
2672 }
2673 }
2674 return pszRet;
2675}
2676
2677
2678/**
2679 * @callback_method_impl{FNDBGCCMD, The 'k'\, 'kg' and 'kh' commands.}
2680 */
2681static DECLCALLBACK(int) dbgcCmdStack(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2682{
2683 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2684
2685 /*
2686 * Figure which context we're called for and start walking that stack.
2687 */
2688 int rc;
2689 PCDBGFSTACKFRAME pFirstFrame;
2690 bool const fGuest = true;
2691 bool const fVerbose = pCmd->pszCmd[1] == 'v'
2692 || (pCmd->pszCmd[1] != '\0' && pCmd->pszCmd[2] == 'v');
2693 rc = DBGFR3StackWalkBegin(pUVM, pDbgc->idCpu, fGuest ? DBGFCODETYPE_GUEST : DBGFCODETYPE_HYPER, &pFirstFrame);
2694 if (RT_FAILURE(rc))
2695 return DBGCCmdHlpPrintf(pCmdHlp, "Failed to begin stack walk, rc=%Rrc\n", rc);
2696
2697 /*
2698 * Print the frames.
2699 */
2700 char szTmp[1024];
2701 uint32_t fBitFlags = 0;
2702 for (PCDBGFSTACKFRAME pFrame = pFirstFrame;
2703 pFrame;
2704 pFrame = DBGFR3StackWalkNext(pFrame))
2705 {
2706 uint32_t const fCurBitFlags = pFrame->fFlags & (DBGFSTACKFRAME_FLAGS_16BIT | DBGFSTACKFRAME_FLAGS_32BIT | DBGFSTACKFRAME_FLAGS_64BIT);
2707 if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_16BIT)
2708 {
2709 if (fCurBitFlags != fBitFlags)
2710 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "# SS:BP Ret SS:BP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
2711 rc = DBGCCmdHlpPrintf(pCmdHlp, "%02x %04RX16:%04RX16 %04RX16:%04RX16 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
2712 pFrame->iFrame,
2713 pFrame->AddrFrame.Sel,
2714 (uint16_t)pFrame->AddrFrame.off,
2715 pFrame->AddrReturnFrame.Sel,
2716 (uint16_t)pFrame->AddrReturnFrame.off,
2717 (uint32_t)pFrame->AddrReturnPC.Sel,
2718 (uint32_t)pFrame->AddrReturnPC.off,
2719 pFrame->Args.au32[0],
2720 pFrame->Args.au32[1],
2721 pFrame->Args.au32[2],
2722 pFrame->Args.au32[3]);
2723 }
2724 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT)
2725 {
2726 if (fCurBitFlags != fBitFlags)
2727 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "# EBP Ret EBP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
2728 rc = DBGCCmdHlpPrintf(pCmdHlp, "%02x %08RX32 %08RX32 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
2729 pFrame->iFrame,
2730 (uint32_t)pFrame->AddrFrame.off,
2731 (uint32_t)pFrame->AddrReturnFrame.off,
2732 (uint32_t)pFrame->AddrReturnPC.Sel,
2733 (uint32_t)pFrame->AddrReturnPC.off,
2734 pFrame->Args.au32[0],
2735 pFrame->Args.au32[1],
2736 pFrame->Args.au32[2],
2737 pFrame->Args.au32[3]);
2738 }
2739 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT)
2740 {
2741 if (fCurBitFlags != fBitFlags)
2742 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "# RBP Ret SS:RBP Ret RIP CS:RIP / Symbol [line]\n");
2743 rc = DBGCCmdHlpPrintf(pCmdHlp, "%02x %016RX64 %04RX16:%016RX64 %016RX64",
2744 pFrame->iFrame,
2745 (uint64_t)pFrame->AddrFrame.off,
2746 pFrame->AddrReturnFrame.Sel,
2747 (uint64_t)pFrame->AddrReturnFrame.off,
2748 (uint64_t)pFrame->AddrReturnPC.off);
2749 }
2750 if (RT_FAILURE(rc))
2751 break;
2752 if (!pFrame->pSymPC)
2753 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
2754 fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT
2755 ? " %RTsel:%016RGv"
2756 : fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT
2757 ? " %RTsel:%08RGv"
2758 : " %RTsel:%04RGv"
2759 , pFrame->AddrPC.Sel, pFrame->AddrPC.off);
2760 else
2761 {
2762 RTGCINTPTR offDisp = pFrame->AddrPC.FlatPtr - pFrame->pSymPC->Value; /** @todo this isn't 100% correct for segmented stuff. */
2763 if (offDisp > 0)
2764 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s+%llx", pFrame->pSymPC->szName, (int64_t)offDisp);
2765 else if (offDisp < 0)
2766 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s-%llx", pFrame->pSymPC->szName, -(int64_t)offDisp);
2767 else
2768 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s", pFrame->pSymPC->szName);
2769 }
2770 if (RT_SUCCESS(rc) && pFrame->pLinePC)
2771 rc = DBGCCmdHlpPrintf(pCmdHlp, " [%s @ 0i%d]", pFrame->pLinePC->szFilename, pFrame->pLinePC->uLineNo);
2772 if (RT_SUCCESS(rc))
2773 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
2774
2775 if (fVerbose && RT_SUCCESS(rc))
2776 {
2777 /*
2778 * Display verbose frame info.
2779 */
2780 const char *pszRetType = "invalid";
2781 switch (pFrame->enmReturnType)
2782 {
2783 case RTDBGRETURNTYPE_NEAR16: pszRetType = "retn/16"; break;
2784 case RTDBGRETURNTYPE_NEAR32: pszRetType = "retn/32"; break;
2785 case RTDBGRETURNTYPE_NEAR64: pszRetType = "retn/64"; break;
2786 case RTDBGRETURNTYPE_FAR16: pszRetType = "retf/16"; break;
2787 case RTDBGRETURNTYPE_FAR32: pszRetType = "retf/32"; break;
2788 case RTDBGRETURNTYPE_FAR64: pszRetType = "retf/64"; break;
2789 case RTDBGRETURNTYPE_IRET16: pszRetType = "iret-16"; break;
2790 case RTDBGRETURNTYPE_IRET32: pszRetType = "iret/32s"; break;
2791 case RTDBGRETURNTYPE_IRET32_PRIV: pszRetType = "iret/32p"; break;
2792 case RTDBGRETURNTYPE_IRET32_V86: pszRetType = "iret/v86"; break;
2793 case RTDBGRETURNTYPE_IRET64: pszRetType = "iret/64"; break;
2794
2795 case RTDBGRETURNTYPE_END:
2796 case RTDBGRETURNTYPE_INVALID:
2797 case RTDBGRETURNTYPE_32BIT_HACK:
2798 break;
2799 }
2800 size_t cchLine = DBGCCmdHlpPrintfLen(pCmdHlp, " %s", pszRetType);
2801 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_USED_UNWIND_INFO)
2802 cchLine += DBGCCmdHlpPrintfLen(pCmdHlp, " used-unwind-info");
2803 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_USED_ODD_EVEN)
2804 cchLine += DBGCCmdHlpPrintfLen(pCmdHlp, " used-odd-even");
2805 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_REAL_V86)
2806 cchLine += DBGCCmdHlpPrintfLen(pCmdHlp, " real-v86");
2807 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_MAX_DEPTH)
2808 cchLine += DBGCCmdHlpPrintfLen(pCmdHlp, " max-depth");
2809 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_TRAP_FRAME)
2810 cchLine += DBGCCmdHlpPrintfLen(pCmdHlp, " trap-frame");
2811
2812 if (pFrame->cSureRegs > 0)
2813 {
2814 cchLine = 1024; /* force new line */
2815 for (uint32_t i = 0; i < pFrame->cSureRegs; i++)
2816 {
2817 if (cchLine > 80)
2818 {
2819 DBGCCmdHlpPrintf(pCmdHlp, "\n ");
2820 cchLine = 2;
2821 }
2822
2823 szTmp[0] = '\0';
2824 DBGFR3RegFormatValue(szTmp, sizeof(szTmp), &pFrame->paSureRegs[i].Value,
2825 pFrame->paSureRegs[i].enmType, false);
2826 const char *pszName = pFrame->paSureRegs[i].enmReg != DBGFREG_END
2827 ? DBGFR3RegCpuName(pUVM, pFrame->paSureRegs[i].enmReg, pFrame->paSureRegs[i].enmType)
2828 : pFrame->paSureRegs[i].pszName;
2829 cchLine += DBGCCmdHlpPrintfLen(pCmdHlp, " %s=%s", pszName, szTmp);
2830 }
2831 }
2832
2833 if (RT_SUCCESS(rc))
2834 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
2835 }
2836
2837 if (RT_FAILURE(rc))
2838 break;
2839
2840 fBitFlags = fCurBitFlags;
2841 }
2842
2843 DBGFR3StackWalkEnd(pFirstFrame);
2844
2845 NOREF(paArgs); NOREF(cArgs);
2846 return rc;
2847}
2848
2849
2850/**
2851 * Worker function that displays one descriptor entry (GDT, LDT, IDT).
2852 *
2853 * @returns pfnPrintf status code.
2854 * @param pCmdHlp The DBGC command helpers.
2855 * @param pDesc The descriptor to display.
2856 * @param iEntry The descriptor entry number.
2857 * @param fHyper Whether the selector belongs to the hypervisor or not.
2858 * @param hAs Address space to use when resolving symbols.
2859 * @param pfDblEntry Where to indicate whether the entry is two entries wide.
2860 * Optional.
2861 */
2862static int dbgcCmdDumpDTWorker64(PDBGCCMDHLP pCmdHlp, PCX86DESC64 pDesc, unsigned iEntry, bool fHyper, RTDBGAS hAs,
2863 bool *pfDblEntry)
2864{
2865 /* GUEST64 */
2866 int rc;
2867
2868 const char *pszHyper = fHyper ? " HYPER" : "";
2869 const char *pszPresent = pDesc->Gen.u1Present ? "P " : "NP";
2870 if (pDesc->Gen.u1DescType)
2871 {
2872 static const char * const s_apszTypes[] =
2873 {
2874 "DataRO", /* 0 Read-Only */
2875 "DataRO", /* 1 Read-Only - Accessed */
2876 "DataRW", /* 2 Read/Write */
2877 "DataRW", /* 3 Read/Write - Accessed */
2878 "DownRO", /* 4 Expand-down, Read-Only */
2879 "DownRO", /* 5 Expand-down, Read-Only - Accessed */
2880 "DownRW", /* 6 Expand-down, Read/Write */
2881 "DownRW", /* 7 Expand-down, Read/Write - Accessed */
2882 "CodeEO", /* 8 Execute-Only */
2883 "CodeEO", /* 9 Execute-Only - Accessed */
2884 "CodeER", /* A Execute/Readable */
2885 "CodeER", /* B Execute/Readable - Accessed */
2886 "ConfE0", /* C Conforming, Execute-Only */
2887 "ConfE0", /* D Conforming, Execute-Only - Accessed */
2888 "ConfER", /* E Conforming, Execute/Readable */
2889 "ConfER" /* F Conforming, Execute/Readable - Accessed */
2890 };
2891 const char *pszAccessed = pDesc->Gen.u4Type & RT_BIT(0) ? "A " : "NA";
2892 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
2893 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
2894 uint32_t u32Base = X86DESC_BASE(pDesc);
2895 uint32_t cbLimit = X86DESC_LIMIT_G(pDesc);
2896
2897 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d L=%d%s\n",
2898 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
2899 pDesc->Gen.u2Dpl, pszPresent, pszAccessed, pszGranularity, pszBig,
2900 pDesc->Gen.u1Available, pDesc->Gen.u1Long, pszHyper);
2901 }
2902 else
2903 {
2904 static const char * const s_apszTypes[] =
2905 {
2906 "Ill-0 ", /* 0 0000 Reserved (Illegal) */
2907 "Ill-1 ", /* 1 0001 Available 16-bit TSS */
2908 "LDT ", /* 2 0010 LDT */
2909 "Ill-3 ", /* 3 0011 Busy 16-bit TSS */
2910 "Ill-4 ", /* 4 0100 16-bit Call Gate */
2911 "Ill-5 ", /* 5 0101 Task Gate */
2912 "Ill-6 ", /* 6 0110 16-bit Interrupt Gate */
2913 "Ill-7 ", /* 7 0111 16-bit Trap Gate */
2914 "Ill-8 ", /* 8 1000 Reserved (Illegal) */
2915 "Tss64A", /* 9 1001 Available 32-bit TSS */
2916 "Ill-A ", /* A 1010 Reserved (Illegal) */
2917 "Tss64B", /* B 1011 Busy 32-bit TSS */
2918 "Call64", /* C 1100 32-bit Call Gate */
2919 "Ill-D ", /* D 1101 Reserved (Illegal) */
2920 "Int64 ", /* E 1110 32-bit Interrupt Gate */
2921 "Trap64" /* F 1111 32-bit Trap Gate */
2922 };
2923 switch (pDesc->Gen.u4Type)
2924 {
2925 /* raw */
2926 case X86_SEL_TYPE_SYS_UNDEFINED:
2927 case X86_SEL_TYPE_SYS_UNDEFINED2:
2928 case X86_SEL_TYPE_SYS_UNDEFINED4:
2929 case X86_SEL_TYPE_SYS_UNDEFINED3:
2930 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
2931 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
2932 case X86_SEL_TYPE_SYS_286_CALL_GATE:
2933 case X86_SEL_TYPE_SYS_286_INT_GATE:
2934 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
2935 case X86_SEL_TYPE_SYS_TASK_GATE:
2936 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s %.8Rhxs DPL=%d %s%s\n",
2937 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc,
2938 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
2939 break;
2940
2941 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
2942 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
2943 case X86_SEL_TYPE_SYS_LDT:
2944 {
2945 const char *pszBusy = pDesc->Gen.u4Type & RT_BIT(1) ? "B " : "NB";
2946 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
2947 const char *pszLong = pDesc->Gen.u1Long ? "LONG" : " ";
2948
2949 uint64_t u64Base = X86DESC64_BASE(pDesc);
2950 uint32_t cbLimit = X86DESC_LIMIT_G(pDesc);
2951
2952 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%016RX64 Lim=%08x DPL=%d %s %s %s %sAVL=%d R=%d%s\n",
2953 iEntry, s_apszTypes[pDesc->Gen.u4Type], u64Base, cbLimit,
2954 pDesc->Gen.u2Dpl, pszPresent, pszBusy, pszLong, pszBig,
2955 pDesc->Gen.u1Available, pDesc->Gen.u1Long | (pDesc->Gen.u1DefBig << 1),
2956 pszHyper);
2957 if (pfDblEntry)
2958 *pfDblEntry = true;
2959 break;
2960 }
2961
2962 case X86_SEL_TYPE_SYS_386_CALL_GATE:
2963 {
2964 unsigned cParams = pDesc->au8[4] & 0x1f;
2965 const char *pszCountOf = pDesc->Gen.u4Type & RT_BIT(3) ? "DC" : "WC";
2966 RTSEL sel = pDesc->au16[1];
2967 uint64_t off = pDesc->au16[0]
2968 | ((uint64_t)pDesc->au16[3] << 16)
2969 | ((uint64_t)pDesc->Gen.u32BaseHigh3 << 32);
2970 char *pszSymbol = dbgcCmdHlpFarAddrToSymbol(pCmdHlp, hAs, sel, off, " (", ")");
2971 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%016RX64 DPL=%d %s %s=%d%s%s\n",
2972 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
2973 pDesc->Gen.u2Dpl, pszPresent, pszCountOf, cParams, pszHyper, pszSymbol ? pszSymbol : "");
2974 RTStrFree(pszSymbol);
2975 if (pfDblEntry)
2976 *pfDblEntry = true;
2977 break;
2978 }
2979
2980 case X86_SEL_TYPE_SYS_386_INT_GATE:
2981 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
2982 {
2983 RTSEL sel = pDesc->Gate.u16Sel;
2984 uint64_t off = pDesc->Gate.u16OffsetLow
2985 | ((uint64_t)pDesc->Gate.u16OffsetHigh << 16)
2986 | ((uint64_t)pDesc->Gate.u32OffsetTop << 32);
2987 char *pszSymbol = dbgcCmdHlpFarAddrToSymbol(pCmdHlp, hAs, sel, off, " (", ")");
2988 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%016RX64 DPL=%u %s IST=%u%s%s\n",
2989 iEntry, s_apszTypes[pDesc->Gate.u4Type], sel, off,
2990 pDesc->Gate.u2Dpl, pszPresent, pDesc->Gate.u3IST, pszHyper, pszSymbol ? pszSymbol : "");
2991 RTStrFree(pszSymbol);
2992 if (pfDblEntry)
2993 *pfDblEntry = true;
2994 break;
2995 }
2996
2997 /* impossible, just it's necessary to keep gcc happy. */
2998 default:
2999 return VINF_SUCCESS;
3000 }
3001 }
3002 return VINF_SUCCESS;
3003}
3004
3005
3006/**
3007 * Worker function that displays one descriptor entry (GDT, LDT, IDT).
3008 *
3009 * @returns pfnPrintf status code.
3010 * @param pCmdHlp The DBGC command helpers.
3011 * @param pDesc The descriptor to display.
3012 * @param iEntry The descriptor entry number.
3013 * @param fHyper Whether the selector belongs to the hypervisor or not.
3014 * @param hAs Address space to use when resolving symbols.
3015 */
3016static int dbgcCmdDumpDTWorker32(PDBGCCMDHLP pCmdHlp, PCX86DESC pDesc, unsigned iEntry, bool fHyper, RTDBGAS hAs)
3017{
3018 int rc;
3019
3020 const char *pszHyper = fHyper ? " HYPER" : "";
3021 const char *pszPresent = pDesc->Gen.u1Present ? "P " : "NP";
3022 if (pDesc->Gen.u1DescType)
3023 {
3024 static const char * const s_apszTypes[] =
3025 {
3026 "DataRO", /* 0 Read-Only */
3027 "DataRO", /* 1 Read-Only - Accessed */
3028 "DataRW", /* 2 Read/Write */
3029 "DataRW", /* 3 Read/Write - Accessed */
3030 "DownRO", /* 4 Expand-down, Read-Only */
3031 "DownRO", /* 5 Expand-down, Read-Only - Accessed */
3032 "DownRW", /* 6 Expand-down, Read/Write */
3033 "DownRW", /* 7 Expand-down, Read/Write - Accessed */
3034 "CodeEO", /* 8 Execute-Only */
3035 "CodeEO", /* 9 Execute-Only - Accessed */
3036 "CodeER", /* A Execute/Readable */
3037 "CodeER", /* B Execute/Readable - Accessed */
3038 "ConfE0", /* C Conforming, Execute-Only */
3039 "ConfE0", /* D Conforming, Execute-Only - Accessed */
3040 "ConfER", /* E Conforming, Execute/Readable */
3041 "ConfER" /* F Conforming, Execute/Readable - Accessed */
3042 };
3043 const char *pszAccessed = pDesc->Gen.u4Type & RT_BIT(0) ? "A " : "NA";
3044 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
3045 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
3046 uint32_t u32Base = pDesc->Gen.u16BaseLow
3047 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
3048 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
3049 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
3050 if (pDesc->Gen.u1Granularity)
3051 cbLimit <<= PAGE_SHIFT;
3052
3053 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d L=%d%s\n",
3054 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
3055 pDesc->Gen.u2Dpl, pszPresent, pszAccessed, pszGranularity, pszBig,
3056 pDesc->Gen.u1Available, pDesc->Gen.u1Long, pszHyper);
3057 }
3058 else
3059 {
3060 static const char * const s_apszTypes[] =
3061 {
3062 "Ill-0 ", /* 0 0000 Reserved (Illegal) */
3063 "Tss16A", /* 1 0001 Available 16-bit TSS */
3064 "LDT ", /* 2 0010 LDT */
3065 "Tss16B", /* 3 0011 Busy 16-bit TSS */
3066 "Call16", /* 4 0100 16-bit Call Gate */
3067 "TaskG ", /* 5 0101 Task Gate */
3068 "Int16 ", /* 6 0110 16-bit Interrupt Gate */
3069 "Trap16", /* 7 0111 16-bit Trap Gate */
3070 "Ill-8 ", /* 8 1000 Reserved (Illegal) */
3071 "Tss32A", /* 9 1001 Available 32-bit TSS */
3072 "Ill-A ", /* A 1010 Reserved (Illegal) */
3073 "Tss32B", /* B 1011 Busy 32-bit TSS */
3074 "Call32", /* C 1100 32-bit Call Gate */
3075 "Ill-D ", /* D 1101 Reserved (Illegal) */
3076 "Int32 ", /* E 1110 32-bit Interrupt Gate */
3077 "Trap32" /* F 1111 32-bit Trap Gate */
3078 };
3079 switch (pDesc->Gen.u4Type)
3080 {
3081 /* raw */
3082 case X86_SEL_TYPE_SYS_UNDEFINED:
3083 case X86_SEL_TYPE_SYS_UNDEFINED2:
3084 case X86_SEL_TYPE_SYS_UNDEFINED4:
3085 case X86_SEL_TYPE_SYS_UNDEFINED3:
3086 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s %.8Rhxs DPL=%d %s%s\n",
3087 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc,
3088 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
3089 break;
3090
3091 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
3092 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
3093 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
3094 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
3095 case X86_SEL_TYPE_SYS_LDT:
3096 {
3097 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
3098 const char *pszBusy = pDesc->Gen.u4Type & RT_BIT(1) ? "B " : "NB";
3099 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
3100 uint32_t u32Base = pDesc->Gen.u16BaseLow
3101 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
3102 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
3103 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
3104 if (pDesc->Gen.u1Granularity)
3105 cbLimit <<= PAGE_SHIFT;
3106
3107 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d R=%d%s\n",
3108 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
3109 pDesc->Gen.u2Dpl, pszPresent, pszBusy, pszGranularity, pszBig,
3110 pDesc->Gen.u1Available, pDesc->Gen.u1Long | (pDesc->Gen.u1DefBig << 1),
3111 pszHyper);
3112 break;
3113 }
3114
3115 case X86_SEL_TYPE_SYS_TASK_GATE:
3116 {
3117 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s TSS=%04x DPL=%d %s%s\n",
3118 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc->au16[1],
3119 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
3120 break;
3121 }
3122
3123 case X86_SEL_TYPE_SYS_286_CALL_GATE:
3124 case X86_SEL_TYPE_SYS_386_CALL_GATE:
3125 {
3126 unsigned cParams = pDesc->au8[4] & 0x1f;
3127 const char *pszCountOf = pDesc->Gen.u4Type & RT_BIT(3) ? "DC" : "WC";
3128 RTSEL sel = pDesc->au16[1];
3129 uint32_t off = pDesc->au16[0] | ((uint32_t)pDesc->au16[3] << 16);
3130 char *pszSymbol = dbgcCmdHlpFarAddrToSymbol(pCmdHlp, hAs, sel, off, " (", ")");
3131 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%08x DPL=%d %s %s=%d%s%s\n",
3132 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
3133 pDesc->Gen.u2Dpl, pszPresent, pszCountOf, cParams, pszHyper, pszSymbol ? pszSymbol : "");
3134 RTStrFree(pszSymbol);
3135 break;
3136 }
3137
3138 case X86_SEL_TYPE_SYS_286_INT_GATE:
3139 case X86_SEL_TYPE_SYS_386_INT_GATE:
3140 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
3141 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
3142 {
3143 RTSEL sel = pDesc->au16[1];
3144 uint32_t off = pDesc->au16[0] | ((uint32_t)pDesc->au16[3] << 16);
3145 char *pszSymbol = dbgcCmdHlpFarAddrToSymbol(pCmdHlp, hAs, sel, off, " (", ")");
3146 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%08x DPL=%d %s%s%s\n",
3147 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
3148 pDesc->Gen.u2Dpl, pszPresent, pszHyper, pszSymbol ? pszSymbol : "");
3149 RTStrFree(pszSymbol);
3150 break;
3151 }
3152
3153 /* impossible, just it's necessary to keep gcc happy. */
3154 default:
3155 return VINF_SUCCESS;
3156 }
3157 }
3158 return rc;
3159}
3160
3161
3162/**
3163 * @callback_method_impl{FNDBGCCMD, The 'dg'\, 'dga'\, 'dl' and 'dla' commands.}
3164 */
3165static DECLCALLBACK(int) dbgcCmdDumpDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3166{
3167 /*
3168 * Validate input.
3169 */
3170 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3171
3172 /*
3173 * Get the CPU mode, check which command variation this is
3174 * and fix a default parameter if needed.
3175 */
3176 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3177 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
3178 CPUMMODE enmMode = CPUMGetGuestMode(pVCpu);
3179 bool fGdt = pCmd->pszCmd[1] == 'g';
3180 bool fAll = pCmd->pszCmd[2] == 'a';
3181 RTSEL SelTable = fGdt ? 0 : X86_SEL_LDT;
3182
3183 DBGCVAR Var;
3184 if (!cArgs)
3185 {
3186 cArgs = 1;
3187 paArgs = &Var;
3188 Var.enmType = DBGCVAR_TYPE_NUMBER;
3189 Var.u.u64Number = fGdt ? 0 : 4;
3190 Var.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
3191 Var.u64Range = 1024;
3192 }
3193
3194 /*
3195 * Process the arguments.
3196 */
3197 for (unsigned i = 0; i < cArgs; i++)
3198 {
3199 /*
3200 * Retrieve the selector value from the argument.
3201 * The parser may confuse pointers and numbers if more than one
3202 * argument is given, that that into account.
3203 */
3204 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, i, paArgs[i].enmType == DBGCVAR_TYPE_NUMBER || DBGCVAR_ISPOINTER(paArgs[i].enmType));
3205 uint64_t u64;
3206 unsigned cSels = 1;
3207 switch (paArgs[i].enmType)
3208 {
3209 case DBGCVAR_TYPE_NUMBER:
3210 u64 = paArgs[i].u.u64Number;
3211 if (paArgs[i].enmRangeType != DBGCVAR_RANGE_NONE)
3212 cSels = RT_MIN(paArgs[i].u64Range, 1024);
3213 break;
3214 case DBGCVAR_TYPE_GC_FAR: u64 = paArgs[i].u.GCFar.sel; break;
3215 case DBGCVAR_TYPE_GC_FLAT: u64 = paArgs[i].u.GCFlat; break;
3216 case DBGCVAR_TYPE_GC_PHYS: u64 = paArgs[i].u.GCPhys; break;
3217 case DBGCVAR_TYPE_HC_FLAT: u64 = (uintptr_t)paArgs[i].u.pvHCFlat; break;
3218 case DBGCVAR_TYPE_HC_PHYS: u64 = paArgs[i].u.HCPhys; break;
3219 default: u64 = _64K; break;
3220 }
3221 if (u64 < _64K)
3222 {
3223 unsigned Sel = (RTSEL)u64;
3224
3225 /*
3226 * Dump the specified range.
3227 */
3228 bool fSingle = cSels == 1;
3229 while ( cSels-- > 0
3230 && Sel < _64K)
3231 {
3232 DBGFSELINFO SelInfo;
3233 int rc = DBGFR3SelQueryInfo(pUVM, pDbgc->idCpu, Sel | SelTable, DBGFSELQI_FLAGS_DT_GUEST, &SelInfo);
3234 if (RT_SUCCESS(rc))
3235 {
3236 if (SelInfo.fFlags & DBGFSELINFO_FLAGS_REAL_MODE)
3237 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x RealM Bas=%04x Lim=%04x\n",
3238 Sel, (unsigned)SelInfo.GCPtrBase, (unsigned)SelInfo.cbLimit);
3239 else if ( fAll
3240 || fSingle
3241 || SelInfo.u.Raw.Gen.u1Present)
3242 {
3243 if (enmMode == CPUMMODE_PROTECTED)
3244 rc = dbgcCmdDumpDTWorker32(pCmdHlp, &SelInfo.u.Raw, Sel,
3245 !!(SelInfo.fFlags & DBGFSELINFO_FLAGS_HYPER), DBGF_AS_GLOBAL);
3246 else
3247 {
3248 bool fDblSkip = false;
3249 rc = dbgcCmdDumpDTWorker64(pCmdHlp, &SelInfo.u.Raw64, Sel,
3250 !!(SelInfo.fFlags & DBGFSELINFO_FLAGS_HYPER), DBGF_AS_GLOBAL, &fDblSkip);
3251 if (fDblSkip)
3252 Sel += 4;
3253 }
3254 }
3255 }
3256 else
3257 {
3258 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %Rrc\n", Sel, rc);
3259 if (!fAll)
3260 return rc;
3261 }
3262 if (RT_FAILURE(rc))
3263 return rc;
3264
3265 /* next */
3266 Sel += 8;
3267 }
3268 }
3269 else
3270 DBGCCmdHlpPrintf(pCmdHlp, "error: %llx is out of bounds\n", u64);
3271 }
3272
3273 return VINF_SUCCESS;
3274}
3275
3276
3277/**
3278 * @callback_method_impl{FNDBGCCMD, The 'di' and 'dia' commands.}
3279 */
3280static DECLCALLBACK(int) dbgcCmdDumpIDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3281{
3282 /*
3283 * Validate input.
3284 */
3285 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3286
3287 /*
3288 * Establish some stuff like the current IDTR and CPU mode,
3289 * and fix a default parameter.
3290 */
3291 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3292 CPUMMODE enmMode = DBGCCmdHlpGetCpuMode(pCmdHlp);
3293 uint16_t cbLimit = 0;
3294 uint64_t GCFlat = 0;
3295 int rc = DBGFR3RegCpuQueryXdtr(pDbgc->pUVM, pDbgc->idCpu, DBGFREG_IDTR, &GCFlat, &cbLimit);
3296 if (RT_FAILURE(rc))
3297 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3RegCpuQueryXdtr/DBGFREG_IDTR");
3298 unsigned cbEntry;
3299 switch (enmMode)
3300 {
3301 case CPUMMODE_REAL: cbEntry = sizeof(RTFAR16); break;
3302 case CPUMMODE_PROTECTED: cbEntry = sizeof(X86DESC); break;
3303 case CPUMMODE_LONG: cbEntry = sizeof(X86DESC64); break;
3304 default:
3305 return DBGCCmdHlpPrintf(pCmdHlp, "error: Invalid CPU mode %d.\n", enmMode);
3306 }
3307
3308 bool fAll = pCmd->pszCmd[2] == 'a';
3309 DBGCVAR Var;
3310 if (!cArgs)
3311 {
3312 cArgs = 1;
3313 paArgs = &Var;
3314 Var.enmType = DBGCVAR_TYPE_NUMBER;
3315 Var.u.u64Number = 0;
3316 Var.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
3317 Var.u64Range = 256;
3318 }
3319
3320 /*
3321 * Process the arguments.
3322 */
3323 for (unsigned i = 0; i < cArgs; i++)
3324 {
3325 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, i, paArgs[i].enmType == DBGCVAR_TYPE_NUMBER);
3326 if (paArgs[i].u.u64Number < 256)
3327 {
3328 RTGCUINTPTR iInt = (RTGCUINTPTR)paArgs[i].u.u64Number;
3329 unsigned cInts = paArgs[i].enmRangeType != DBGCVAR_RANGE_NONE
3330 ? paArgs[i].u64Range
3331 : 1;
3332 bool fSingle = cInts == 1;
3333 while ( cInts-- > 0
3334 && iInt < 256)
3335 {
3336 /*
3337 * Try read it.
3338 */
3339 union
3340 {
3341 RTFAR16 Real;
3342 X86DESC Prot;
3343 X86DESC64 Long;
3344 } u;
3345 if (iInt * cbEntry + (cbEntry - 1) > cbLimit)
3346 {
3347 DBGCCmdHlpPrintf(pCmdHlp, "%04x not within the IDT\n", (unsigned)iInt);
3348 if (!fAll && !fSingle)
3349 return VINF_SUCCESS;
3350 }
3351 DBGCVAR AddrVar;
3352 AddrVar.enmType = DBGCVAR_TYPE_GC_FLAT;
3353 AddrVar.u.GCFlat = GCFlat + iInt * cbEntry;
3354 AddrVar.enmRangeType = DBGCVAR_RANGE_NONE;
3355 rc = pCmdHlp->pfnMemRead(pCmdHlp, &u, cbEntry, &AddrVar, NULL);
3356 if (RT_FAILURE(rc))
3357 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading IDT entry %#04x.\n", (unsigned)iInt);
3358
3359 /*
3360 * Display it.
3361 */
3362 switch (enmMode)
3363 {
3364 case CPUMMODE_REAL:
3365 {
3366 char *pszSymbol = dbgcCmdHlpFarAddrToSymbol(pCmdHlp, DBGF_AS_GLOBAL, u.Real.sel, u.Real.off, " (", ")");
3367 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %RTfp16%s\n", (unsigned)iInt, u.Real, pszSymbol ? pszSymbol : "");
3368 RTStrFree(pszSymbol);
3369 break;
3370 }
3371 case CPUMMODE_PROTECTED:
3372 if (fAll || fSingle || u.Prot.Gen.u1Present)
3373 rc = dbgcCmdDumpDTWorker32(pCmdHlp, &u.Prot, iInt, false, DBGF_AS_GLOBAL);
3374 break;
3375 case CPUMMODE_LONG:
3376 if (fAll || fSingle || u.Long.Gen.u1Present)
3377 rc = dbgcCmdDumpDTWorker64(pCmdHlp, &u.Long, iInt, false, DBGF_AS_GLOBAL, NULL);
3378 break;
3379 default: break; /* to shut up gcc */
3380 }
3381 if (RT_FAILURE(rc))
3382 return rc;
3383
3384 /* next */
3385 iInt++;
3386 }
3387 }
3388 else
3389 DBGCCmdHlpPrintf(pCmdHlp, "error: %llx is out of bounds (max 256)\n", paArgs[i].u.u64Number);
3390 }
3391
3392 return VINF_SUCCESS;
3393}
3394
3395
3396/**
3397 * @callback_method_impl{FNDBGCCMD,
3398 * The 'da'\, 'dq'\, 'dqs'\, 'dd'\, 'dds'\, 'dw'\, 'db'\, 'dp'\, 'dps'\,
3399 * and 'du' commands.}
3400 */
3401static DECLCALLBACK(int) dbgcCmdDumpMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3402{
3403 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3404
3405 /*
3406 * Validate input.
3407 */
3408 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
3409 if (cArgs == 1)
3410 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
3411 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3412
3413#define DBGC_DUMP_MEM_F_ASCII RT_BIT_32(31)
3414#define DBGC_DUMP_MEM_F_UNICODE RT_BIT_32(30)
3415#define DBGC_DUMP_MEM_F_FAR RT_BIT_32(29)
3416#define DBGC_DUMP_MEM_F_SYMBOLS RT_BIT_32(28)
3417#define DBGC_DUMP_MEM_F_SIZE UINT32_C(0x0000ffff)
3418
3419 /*
3420 * Figure out the element size.
3421 */
3422 unsigned cbElement;
3423 bool fAscii = false;
3424 bool fUnicode = false;
3425 bool fFar = false;
3426 bool fSymbols = pCmd->pszCmd[1] && pCmd->pszCmd[2] == 's';
3427 switch (pCmd->pszCmd[1])
3428 {
3429 default:
3430 case 'b': cbElement = 1; break;
3431 case 'w': cbElement = 2; break;
3432 case 'd': cbElement = 4; break;
3433 case 'q': cbElement = 8; break;
3434 case 'a':
3435 cbElement = 1;
3436 fAscii = true;
3437 break;
3438 case 'F':
3439 cbElement = 4;
3440 fFar = true;
3441 break;
3442 case 'p':
3443 cbElement = DBGFR3CpuIsIn64BitCode(pUVM, pDbgc->idCpu) ? 8 : 4;
3444 break;
3445 case 'u':
3446 cbElement = 2;
3447 fUnicode = true;
3448 break;
3449 case '\0':
3450 fAscii = RT_BOOL(pDbgc->cbDumpElement & DBGC_DUMP_MEM_F_ASCII);
3451 fSymbols = RT_BOOL(pDbgc->cbDumpElement & DBGC_DUMP_MEM_F_SYMBOLS);
3452 fUnicode = RT_BOOL(pDbgc->cbDumpElement & DBGC_DUMP_MEM_F_UNICODE);
3453 fFar = RT_BOOL(pDbgc->cbDumpElement & DBGC_DUMP_MEM_F_FAR);
3454 cbElement = pDbgc->cbDumpElement & DBGC_DUMP_MEM_F_SIZE;
3455 if (!cbElement)
3456 cbElement = 1;
3457 break;
3458 }
3459 uint32_t const cbDumpElement = cbElement
3460 | (fSymbols ? DBGC_DUMP_MEM_F_SYMBOLS : 0)
3461 | (fFar ? DBGC_DUMP_MEM_F_FAR : 0)
3462 | (fUnicode ? DBGC_DUMP_MEM_F_UNICODE : 0)
3463 | (fAscii ? DBGC_DUMP_MEM_F_ASCII : 0);
3464 pDbgc->cbDumpElement = cbDumpElement;
3465
3466 /*
3467 * Find address.
3468 */
3469 if (!cArgs)
3470 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_NONE;
3471 else
3472 pDbgc->DumpPos = paArgs[0];
3473
3474 /*
3475 * Range.
3476 */
3477 switch (pDbgc->DumpPos.enmRangeType)
3478 {
3479 case DBGCVAR_RANGE_NONE:
3480 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_BYTES;
3481 pDbgc->DumpPos.u64Range = 0x60;
3482 break;
3483
3484 case DBGCVAR_RANGE_ELEMENTS:
3485 if (pDbgc->DumpPos.u64Range > 2048)
3486 return DBGCCmdHlpPrintf(pCmdHlp, "error: Too many elements requested. Max is 2048 elements.\n");
3487 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_BYTES;
3488 pDbgc->DumpPos.u64Range = (cbElement ? cbElement : 1) * pDbgc->DumpPos.u64Range;
3489 break;
3490
3491 case DBGCVAR_RANGE_BYTES:
3492 if (pDbgc->DumpPos.u64Range > 65536)
3493 return DBGCCmdHlpPrintf(pCmdHlp, "error: The requested range is too big. Max is 64KB.\n");
3494 break;
3495
3496 default:
3497 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: Unknown range type %d.\n", pDbgc->DumpPos.enmRangeType);
3498 }
3499
3500 pDbgc->pLastPos = &pDbgc->DumpPos;
3501
3502 /*
3503 * Do the dumping.
3504 */
3505 int cbLeft = (int)pDbgc->DumpPos.u64Range;
3506 uint8_t u16Prev = '\0';
3507 for (;;)
3508 {
3509 /*
3510 * Read memory.
3511 */
3512 char achBuffer[16];
3513 size_t cbReq = RT_MIN((int)sizeof(achBuffer), cbLeft);
3514 size_t cb = RT_MIN((int)sizeof(achBuffer), cbLeft);
3515 int rc = pCmdHlp->pfnMemRead(pCmdHlp, &achBuffer, cbReq, &pDbgc->DumpPos, &cb);
3516 if (RT_FAILURE(rc))
3517 {
3518 if (u16Prev && u16Prev != '\n')
3519 DBGCCmdHlpPrintf(pCmdHlp, "\n");
3520 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading memory at %DV.\n", &pDbgc->DumpPos);
3521 }
3522
3523 /*
3524 * Display it.
3525 */
3526 memset(&achBuffer[cb], 0, sizeof(achBuffer) - cb);
3527 if (!fAscii && !fUnicode)
3528 {
3529 DBGCCmdHlpPrintf(pCmdHlp, "%DV:", &pDbgc->DumpPos);
3530 unsigned i;
3531 for (i = 0; i < cb; i += cbElement)
3532 {
3533 const char *pszSpace = " ";
3534 if (cbElement <= 2 && i == 8)
3535 pszSpace = "-";
3536 switch (cbElement)
3537 {
3538 case 1:
3539 DBGCCmdHlpPrintf(pCmdHlp, "%s%02x", pszSpace, *(uint8_t *)&achBuffer[i]);
3540 break;
3541 case 2:
3542 DBGCCmdHlpPrintf(pCmdHlp, "%s%04x", pszSpace, *(uint16_t *)&achBuffer[i]);
3543 break;
3544 case 4:
3545 if (!fFar)
3546 DBGCCmdHlpPrintf(pCmdHlp, "%s%08x", pszSpace, *(uint32_t *)&achBuffer[i]);
3547 else
3548 DBGCCmdHlpPrintf(pCmdHlp, "%s%04x:%04x:",
3549 pszSpace, *(uint16_t *)&achBuffer[i + 2], *(uint16_t *)&achBuffer[i]);
3550 break;
3551 case 8:
3552 DBGCCmdHlpPrintf(pCmdHlp, "%s%016llx", pszSpace, *(uint64_t *)&achBuffer[i]);
3553 break;
3554 }
3555
3556 if (fSymbols)
3557 {
3558 /* Try lookup symbol for the above address. */
3559 DBGFADDRESS Addr;
3560 rc = VINF_SUCCESS;
3561 if (cbElement == 8)
3562 DBGFR3AddrFromFlat(pDbgc->pUVM, &Addr, *(uint64_t *)&achBuffer[i]);
3563 else if (!fFar)
3564 DBGFR3AddrFromFlat(pDbgc->pUVM, &Addr, *(uint32_t *)&achBuffer[i]);
3565 else
3566 rc = DBGFR3AddrFromSelOff(pDbgc->pUVM, pDbgc->idCpu, &Addr,
3567 *(uint16_t *)&achBuffer[i + 2], *(uint16_t *)&achBuffer[i]);
3568 if (RT_SUCCESS(rc))
3569 {
3570 RTINTPTR offDisp;
3571 RTDBGSYMBOL Symbol;
3572 rc = DBGFR3AsSymbolByAddr(pUVM, pDbgc->hDbgAs, &Addr,
3573 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
3574 &offDisp, &Symbol, NULL);
3575 if (RT_SUCCESS(rc))
3576 {
3577 if (!offDisp)
3578 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s", Symbol.szName);
3579 else if (offDisp > 0)
3580 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s + %RGv", Symbol.szName, offDisp);
3581 else
3582 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s - %RGv", Symbol.szName, -offDisp);
3583 if (Symbol.cb > 0)
3584 rc = DBGCCmdHlpPrintf(pCmdHlp, " (LB %RGv)", Symbol.cb);
3585 }
3586 }
3587
3588 /* Next line prefix. */
3589 unsigned iNext = i + cbElement;
3590 if (iNext < cb)
3591 {
3592 DBGCVAR TmpPos = pDbgc->DumpPos;
3593 DBGCCmdHlpEval(pCmdHlp, &TmpPos, "(%Dv) + %x", &pDbgc->DumpPos, iNext);
3594 DBGCCmdHlpPrintf(pCmdHlp, "\n%DV:", &pDbgc->DumpPos);
3595 }
3596 }
3597 }
3598
3599 /* Chars column. */
3600 if (cbElement == 1)
3601 {
3602 while (i++ < sizeof(achBuffer))
3603 DBGCCmdHlpPrintf(pCmdHlp, " ");
3604 DBGCCmdHlpPrintf(pCmdHlp, " ");
3605 for (i = 0; i < cb; i += cbElement)
3606 {
3607 uint8_t u8 = *(uint8_t *)&achBuffer[i];
3608 if (RT_C_IS_PRINT(u8) && u8 < 127 && u8 >= 32)
3609 DBGCCmdHlpPrintf(pCmdHlp, "%c", u8);
3610 else
3611 DBGCCmdHlpPrintf(pCmdHlp, ".");
3612 }
3613 }
3614 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
3615 }
3616 else
3617 {
3618 /*
3619 * We print up to the first zero and stop there.
3620 * Only printables + '\t' and '\n' are printed.
3621 */
3622 if (!u16Prev)
3623 DBGCCmdHlpPrintf(pCmdHlp, "%DV:\n", &pDbgc->DumpPos);
3624 uint16_t u16 = '\0';
3625 unsigned i;
3626 for (i = 0; i < cb; i += cbElement)
3627 {
3628 u16Prev = u16;
3629 if (cbElement == 1)
3630 u16 = *(uint8_t *)&achBuffer[i];
3631 else
3632 u16 = *(uint16_t *)&achBuffer[i];
3633 if ( u16 < 127
3634 && ( (RT_C_IS_PRINT(u16) && u16 >= 32)
3635 || u16 == '\t'
3636 || u16 == '\n'))
3637 DBGCCmdHlpPrintf(pCmdHlp, "%c", (int)u16);
3638 else if (!u16)
3639 break;
3640 else
3641 DBGCCmdHlpPrintf(pCmdHlp, "\\x%0*x", cbElement * 2, u16);
3642 }
3643 if (u16 == '\0')
3644 cb = cbLeft = i + 1;
3645 if (cbLeft - cb <= 0 && u16Prev != '\n')
3646 DBGCCmdHlpPrintf(pCmdHlp, "\n");
3647 }
3648
3649 /*
3650 * Advance
3651 */
3652 cbLeft -= (int)cb;
3653 rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->DumpPos, "(%Dv) + %x", &pDbgc->DumpPos, cb);
3654 if (RT_FAILURE(rc))
3655 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->DumpPos, cb);
3656 if (cbLeft <= 0)
3657 break;
3658 }
3659
3660 NOREF(pCmd);
3661 return VINF_SUCCESS;
3662}
3663
3664
3665/**
3666 * Best guess at which paging mode currently applies to the guest
3667 * paging structures.
3668 *
3669 * This have to come up with a decent answer even when the guest
3670 * is in non-paged protected mode or real mode.
3671 *
3672 * @returns cr3.
3673 * @param pDbgc The DBGC instance.
3674 * @param pfPAE Where to store the page address extension indicator.
3675 * @param pfLME Where to store the long mode enabled indicator.
3676 * @param pfPSE Where to store the page size extension indicator.
3677 * @param pfPGE Where to store the page global enabled indicator.
3678 * @param pfNXE Where to store the no-execution enabled indicator.
3679 */
3680static RTGCPHYS dbgcGetGuestPageMode(PDBGC pDbgc, bool *pfPAE, bool *pfLME, bool *pfPSE, bool *pfPGE, bool *pfNXE)
3681{
3682 PVMCPU pVCpu = VMMR3GetCpuByIdU(pDbgc->pUVM, pDbgc->idCpu);
3683 RTGCUINTREG cr4 = CPUMGetGuestCR4(pVCpu);
3684 *pfPSE = !!(cr4 & X86_CR4_PSE);
3685 *pfPGE = !!(cr4 & X86_CR4_PGE);
3686 if (cr4 & X86_CR4_PAE)
3687 {
3688 *pfPSE = true;
3689 *pfPAE = true;
3690 }
3691 else
3692 *pfPAE = false;
3693
3694 *pfLME = CPUMGetGuestMode(pVCpu) == CPUMMODE_LONG;
3695 *pfNXE = false; /* GUEST64 GUESTNX */
3696 return CPUMGetGuestCR3(pVCpu);
3697}
3698
3699
3700/**
3701 * Determine the shadow paging mode.
3702 *
3703 * @returns cr3.
3704 * @param pDbgc The DBGC instance.
3705 * @param pfPAE Where to store the page address extension indicator.
3706 * @param pfLME Where to store the long mode enabled indicator.
3707 * @param pfPSE Where to store the page size extension indicator.
3708 * @param pfPGE Where to store the page global enabled indicator.
3709 * @param pfNXE Where to store the no-execution enabled indicator.
3710 */
3711static RTHCPHYS dbgcGetShadowPageMode(PDBGC pDbgc, bool *pfPAE, bool *pfLME, bool *pfPSE, bool *pfPGE, bool *pfNXE)
3712{
3713 PVMCPU pVCpu = VMMR3GetCpuByIdU(pDbgc->pUVM, pDbgc->idCpu);
3714
3715 *pfPSE = true;
3716 *pfPGE = false;
3717 switch (PGMGetShadowMode(pVCpu))
3718 {
3719 default:
3720 case PGMMODE_32_BIT:
3721 *pfPAE = *pfLME = *pfNXE = false;
3722 break;
3723 case PGMMODE_PAE:
3724 *pfLME = *pfNXE = false;
3725 *pfPAE = true;
3726 break;
3727 case PGMMODE_PAE_NX:
3728 *pfLME = false;
3729 *pfPAE = *pfNXE = true;
3730 break;
3731 case PGMMODE_AMD64:
3732 *pfNXE = false;
3733 *pfPAE = *pfLME = true;
3734 break;
3735 case PGMMODE_AMD64_NX:
3736 *pfPAE = *pfLME = *pfNXE = true;
3737 break;
3738 }
3739 return PGMGetHyperCR3(pVCpu);
3740}
3741
3742
3743/**
3744 * @callback_method_impl{FNDBGCCMD,
3745 * The 'dpd'\, 'dpda'\, 'dpdb'\, 'dpdg' and 'dpdh' commands.}
3746 */
3747static DECLCALLBACK(int) dbgcCmdDumpPageDir(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3748{
3749 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3750
3751 /*
3752 * Validate input.
3753 */
3754 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
3755 if (cArgs == 1 && pCmd->pszCmd[3] == 'a')
3756 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
3757 if (cArgs == 1 && pCmd->pszCmd[3] != 'a')
3758 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
3759 || DBGCVAR_ISPOINTER(paArgs[0].enmType));
3760 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3761
3762 /*
3763 * Guest or shadow page directories? Get the paging parameters.
3764 */
3765 bool fGuest = pCmd->pszCmd[3] != 'h';
3766 if (!pCmd->pszCmd[3] || pCmd->pszCmd[3] == 'a')
3767 fGuest = paArgs[0].enmType == DBGCVAR_TYPE_NUMBER ? true : DBGCVAR_ISGCPOINTER(paArgs[0].enmType);
3768
3769 bool fPAE, fLME, fPSE, fPGE, fNXE;
3770 uint64_t cr3 = fGuest
3771 ? dbgcGetGuestPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE)
3772 : dbgcGetShadowPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE);
3773 const unsigned cbEntry = fPAE ? sizeof(X86PTEPAE) : sizeof(X86PTE);
3774
3775 /*
3776 * Setup default argument if none was specified.
3777 * Fix address / index confusion.
3778 */
3779 DBGCVAR VarDefault;
3780 if (!cArgs)
3781 {
3782 if (pCmd->pszCmd[3] == 'a')
3783 {
3784 if (fLME || fPAE)
3785 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");
3786 if (fGuest)
3787 DBGCVAR_INIT_GC_PHYS(&VarDefault, cr3);
3788 else
3789 DBGCVAR_INIT_HC_PHYS(&VarDefault, cr3);
3790 }
3791 else
3792 DBGCVAR_INIT_GC_FLAT(&VarDefault, 0);
3793 paArgs = &VarDefault;
3794 cArgs = 1;
3795 }
3796 else if (paArgs[0].enmType == DBGCVAR_TYPE_NUMBER)
3797 {
3798 /* If it's a number (not an address), it's an index, so convert it to an address. */
3799 Assert(pCmd->pszCmd[3] != 'a');
3800 VarDefault = paArgs[0];
3801 if (fPAE)
3802 return DBGCCmdHlpPrintf(pCmdHlp, "PDE indexing is only implemented for 32-bit paging.\n");
3803 if (VarDefault.u.u64Number >= PAGE_SIZE / cbEntry)
3804 return DBGCCmdHlpPrintf(pCmdHlp, "PDE index is out of range [0..%d].\n", PAGE_SIZE / cbEntry - 1);
3805 VarDefault.u.u64Number <<= X86_PD_SHIFT;
3806 VarDefault.enmType = DBGCVAR_TYPE_GC_FLAT;
3807 paArgs = &VarDefault;
3808 }
3809
3810 /*
3811 * Locate the PDE to start displaying at.
3812 *
3813 * The 'dpda' command takes the address of a PDE, while the others are guest
3814 * virtual address which PDEs should be displayed. So, 'dpda' is rather simple
3815 * while the others require us to do all the tedious walking thru the paging
3816 * hierarchy to find the intended PDE.
3817 */
3818 unsigned iEntry = ~0U; /* The page directory index. ~0U for 'dpta'. */
3819 DBGCVAR VarGCPtr = { NULL, }; /* The GC address corresponding to the current PDE (iEntry != ~0U). */
3820 DBGCVAR VarPDEAddr; /* The address of the current PDE. */
3821 unsigned cEntries; /* The number of entries to display. */
3822 unsigned cEntriesMax; /* The max number of entries to display. */
3823 int rc;
3824 if (pCmd->pszCmd[3] == 'a')
3825 {
3826 VarPDEAddr = paArgs[0];
3827 switch (VarPDEAddr.enmRangeType)
3828 {
3829 case DBGCVAR_RANGE_BYTES: cEntries = VarPDEAddr.u64Range / cbEntry; break;
3830 case DBGCVAR_RANGE_ELEMENTS: cEntries = VarPDEAddr.u64Range; break;
3831 default: cEntries = 10; break;
3832 }
3833 cEntriesMax = PAGE_SIZE / cbEntry;
3834 }
3835 else
3836 {
3837 /*
3838 * Determine the range.
3839 */
3840 switch (paArgs[0].enmRangeType)
3841 {
3842 case DBGCVAR_RANGE_BYTES: cEntries = paArgs[0].u64Range / PAGE_SIZE; break;
3843 case DBGCVAR_RANGE_ELEMENTS: cEntries = paArgs[0].u64Range; break;
3844 default: cEntries = 10; break;
3845 }
3846
3847 /*
3848 * Normalize the input address, it must be a flat GC address.
3849 */
3850 rc = DBGCCmdHlpEval(pCmdHlp, &VarGCPtr, "%%(%Dv)", &paArgs[0]);
3851 if (RT_FAILURE(rc))
3852 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "%%(%Dv)", &paArgs[0]);
3853 if (VarGCPtr.enmType == DBGCVAR_TYPE_HC_FLAT)
3854 {
3855 VarGCPtr.u.GCFlat = (uintptr_t)VarGCPtr.u.pvHCFlat;
3856 VarGCPtr.enmType = DBGCVAR_TYPE_GC_FLAT;
3857 }
3858 if (fPAE)
3859 VarGCPtr.u.GCFlat &= ~(((RTGCPTR)1 << X86_PD_PAE_SHIFT) - 1);
3860 else
3861 VarGCPtr.u.GCFlat &= ~(((RTGCPTR)1 << X86_PD_SHIFT) - 1);
3862
3863 /*
3864 * Do the paging walk until we get to the page directory.
3865 */
3866 DBGCVAR VarCur;
3867 if (fGuest)
3868 DBGCVAR_INIT_GC_PHYS(&VarCur, cr3);
3869 else
3870 DBGCVAR_INIT_HC_PHYS(&VarCur, cr3);
3871 if (fLME)
3872 {
3873 /* Page Map Level 4 Lookup. */
3874 /* Check if it's a valid address first? */
3875 VarCur.u.u64Number &= X86_PTE_PAE_PG_MASK;
3876 VarCur.u.u64Number += (((uint64_t)VarGCPtr.u.GCFlat >> X86_PML4_SHIFT) & X86_PML4_MASK) * sizeof(X86PML4E);
3877 X86PML4E Pml4e;
3878 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pml4e, sizeof(Pml4e), &VarCur, NULL);
3879 if (RT_FAILURE(rc))
3880 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PML4E memory at %DV.\n", &VarCur);
3881 if (!Pml4e.n.u1Present)
3882 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory pointer table is not present for %Dv.\n", &VarGCPtr);
3883
3884 VarCur.u.u64Number = Pml4e.u & X86_PML4E_PG_MASK;
3885 Assert(fPAE);
3886 }
3887 if (fPAE)
3888 {
3889 /* Page directory pointer table. */
3890 X86PDPE Pdpe;
3891 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE) * sizeof(Pdpe);
3892 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pdpe, sizeof(Pdpe), &VarCur, NULL);
3893 if (RT_FAILURE(rc))
3894 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDPE memory at %DV.\n", &VarCur);
3895 if (!Pdpe.n.u1Present)
3896 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory is not present for %Dv.\n", &VarGCPtr);
3897
3898 iEntry = (VarGCPtr.u.GCFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
3899 VarPDEAddr = VarCur;
3900 VarPDEAddr.u.u64Number = Pdpe.u & X86_PDPE_PG_MASK;
3901 VarPDEAddr.u.u64Number += iEntry * sizeof(X86PDEPAE);
3902 }
3903 else
3904 {
3905 /* 32-bit legacy - CR3 == page directory. */
3906 iEntry = (VarGCPtr.u.GCFlat >> X86_PD_SHIFT) & X86_PD_MASK;
3907 VarPDEAddr = VarCur;
3908 VarPDEAddr.u.u64Number += iEntry * sizeof(X86PDE);
3909 }
3910 cEntriesMax = (PAGE_SIZE - iEntry) / cbEntry;
3911 }
3912
3913 /* adjust cEntries */
3914 cEntries = RT_MAX(1, cEntries);
3915 cEntries = RT_MIN(cEntries, cEntriesMax);
3916
3917 /*
3918 * The display loop.
3919 */
3920 DBGCCmdHlpPrintf(pCmdHlp, iEntry != ~0U ? "%DV (index %#x):\n" : "%DV:\n",
3921 &VarPDEAddr, iEntry);
3922 do
3923 {
3924 /*
3925 * Read.
3926 */
3927 X86PDEPAE Pde;
3928 Pde.u = 0;
3929 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pde, cbEntry, &VarPDEAddr, NULL);
3930 if (RT_FAILURE(rc))
3931 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarPDEAddr);
3932
3933 /*
3934 * Display.
3935 */
3936 if (iEntry != ~0U)
3937 {
3938 DBGCCmdHlpPrintf(pCmdHlp, "%03x %DV: ", iEntry, &VarGCPtr);
3939 iEntry++;
3940 }
3941 if (fPSE && Pde.b.u1Size)
3942 DBGCCmdHlpPrintf(pCmdHlp,
3943 fPAE
3944 ? "%016llx big phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s %s"
3945 : "%08llx big phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s %s",
3946 Pde.u,
3947 Pde.u & X86_PDE_PAE_PG_MASK,
3948 Pde.b.u1Present ? "p " : "np",
3949 Pde.b.u1Write ? "w" : "r",
3950 Pde.b.u1User ? "u" : "s",
3951 Pde.b.u1Accessed ? "a " : "na",
3952 Pde.b.u1Dirty ? "d " : "nd",
3953 Pde.b.u3Available,
3954 Pde.b.u1Global ? (fPGE ? "g" : "G") : " ",
3955 Pde.b.u1WriteThru ? "pwt" : " ",
3956 Pde.b.u1CacheDisable ? "pcd" : " ",
3957 Pde.b.u1PAT ? "pat" : "",
3958 Pde.b.u1NoExecute ? (fNXE ? "nx" : "NX") : " ");
3959 else
3960 DBGCCmdHlpPrintf(pCmdHlp,
3961 fPAE
3962 ? "%016llx 4kb phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s"
3963 : "%08llx 4kb phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s",
3964 Pde.u,
3965 Pde.u & X86_PDE_PAE_PG_MASK,
3966 Pde.n.u1Present ? "p " : "np",
3967 Pde.n.u1Write ? "w" : "r",
3968 Pde.n.u1User ? "u" : "s",
3969 Pde.n.u1Accessed ? "a " : "na",
3970 Pde.u & RT_BIT(6) ? "6 " : " ",
3971 Pde.n.u3Available,
3972 Pde.u & RT_BIT(8) ? "8" : " ",
3973 Pde.n.u1WriteThru ? "pwt" : " ",
3974 Pde.n.u1CacheDisable ? "pcd" : " ",
3975 Pde.u & RT_BIT(7) ? "7" : "",
3976 Pde.n.u1NoExecute ? (fNXE ? "nx" : "NX") : " ");
3977 if (Pde.u & UINT64_C(0x7fff000000000000))
3978 DBGCCmdHlpPrintf(pCmdHlp, " weird=%RX64", (Pde.u & UINT64_C(0x7fff000000000000)));
3979 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
3980 if (RT_FAILURE(rc))
3981 return rc;
3982
3983 /*
3984 * Advance.
3985 */
3986 VarPDEAddr.u.u64Number += cbEntry;
3987 if (iEntry != ~0U)
3988 VarGCPtr.u.GCFlat += fPAE ? RT_BIT_32(X86_PD_PAE_SHIFT) : RT_BIT_32(X86_PD_SHIFT);
3989 } while (cEntries-- > 0);
3990
3991 return VINF_SUCCESS;
3992}
3993
3994
3995/**
3996 * @callback_method_impl{FNDBGCCMD, The 'dpdb' command.}
3997 */
3998static DECLCALLBACK(int) dbgcCmdDumpPageDirBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3999{
4000 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
4001 int rc1 = pCmdHlp->pfnExec(pCmdHlp, "dpdg %DV", &paArgs[0]);
4002 int rc2 = pCmdHlp->pfnExec(pCmdHlp, "dpdh %DV", &paArgs[0]);
4003 if (RT_FAILURE(rc1))
4004 return rc1;
4005 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
4006 return rc2;
4007}
4008
4009
4010/**
4011 * @callback_method_impl{FNDBGCCMD, The 'dph*' commands and main part of 'm'.}
4012 */
4013static DECLCALLBACK(int) dbgcCmdDumpPageHierarchy(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
4014{
4015 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
4016 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
4017
4018 /*
4019 * Figure the context and base flags.
4020 */
4021 uint32_t fFlags = DBGFPGDMP_FLAGS_PAGE_INFO | DBGFPGDMP_FLAGS_PRINT_CR3;
4022 if (pCmd->pszCmd[0] == 'm')
4023 fFlags |= DBGFPGDMP_FLAGS_GUEST | DBGFPGDMP_FLAGS_SHADOW;
4024 else if (pCmd->pszCmd[3] == '\0')
4025 fFlags |= DBGFPGDMP_FLAGS_GUEST;
4026 else if (pCmd->pszCmd[3] == 'g')
4027 fFlags |= DBGFPGDMP_FLAGS_GUEST;
4028 else if (pCmd->pszCmd[3] == 'h')
4029 fFlags |= DBGFPGDMP_FLAGS_SHADOW;
4030 else
4031 AssertFailed();
4032
4033 if (pDbgc->cPagingHierarchyDumps == 0)
4034 fFlags |= DBGFPGDMP_FLAGS_HEADER;
4035 pDbgc->cPagingHierarchyDumps = (pDbgc->cPagingHierarchyDumps + 1) % 42;
4036
4037 /*
4038 * Get the range.
4039 */
4040 PCDBGCVAR pRange = cArgs > 0 ? &paArgs[0] : pDbgc->pLastPos;
4041 RTGCPTR GCPtrFirst = NIL_RTGCPTR;
4042 int rc = DBGCCmdHlpVarToFlatAddr(pCmdHlp, pRange, &GCPtrFirst);
4043 if (RT_FAILURE(rc))
4044 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to convert %DV to a flat address: %Rrc", pRange, rc);
4045
4046 uint64_t cbRange;
4047 rc = DBGCCmdHlpVarGetRange(pCmdHlp, pRange, PAGE_SIZE, PAGE_SIZE * 8, &cbRange);
4048 if (RT_FAILURE(rc))
4049 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to obtain the range of %DV: %Rrc", pRange, rc);
4050
4051 RTGCPTR GCPtrLast = RTGCPTR_MAX - GCPtrFirst;
4052 if (cbRange >= GCPtrLast)
4053 GCPtrLast = RTGCPTR_MAX;
4054 else if (!cbRange)
4055 GCPtrLast = GCPtrFirst;
4056 else
4057 GCPtrLast = GCPtrFirst + cbRange - 1;
4058
4059 /*
4060 * Do we have a CR3?
4061 */
4062 uint64_t cr3 = 0;
4063 if (cArgs > 1)
4064 {
4065 if ((fFlags & (DBGFPGDMP_FLAGS_GUEST | DBGFPGDMP_FLAGS_SHADOW)) == (DBGFPGDMP_FLAGS_GUEST | DBGFPGDMP_FLAGS_SHADOW))
4066 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No CR3 or mode arguments when dumping both context, please.");
4067 if (paArgs[1].enmType != DBGCVAR_TYPE_NUMBER)
4068 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The CR3 argument is not a number: %DV", &paArgs[1]);
4069 cr3 = paArgs[1].u.u64Number;
4070 }
4071 else
4072 fFlags |= DBGFPGDMP_FLAGS_CURRENT_CR3;
4073
4074 /*
4075 * Do we have a mode?
4076 */
4077 if (cArgs > 2)
4078 {
4079 if (paArgs[2].enmType != DBGCVAR_TYPE_STRING)
4080 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The mode argument is not a string: %DV", &paArgs[2]);
4081 static const struct MODETOFLAGS
4082 {
4083 const char *pszName;
4084 uint32_t fFlags;
4085 } s_aModeToFlags[] =
4086 {
4087 { "ept", DBGFPGDMP_FLAGS_EPT },
4088 { "legacy", 0 },
4089 { "legacy-np", DBGFPGDMP_FLAGS_NP },
4090 { "pse", DBGFPGDMP_FLAGS_PSE },
4091 { "pse-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_NP },
4092 { "pae", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE },
4093 { "pae-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_NP },
4094 { "pae-nx", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_NXE },
4095 { "pae-nx-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_NXE | DBGFPGDMP_FLAGS_NP },
4096 { "long", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME },
4097 { "long-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME | DBGFPGDMP_FLAGS_NP },
4098 { "long-nx", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME | DBGFPGDMP_FLAGS_NXE },
4099 { "long-nx-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME | DBGFPGDMP_FLAGS_NXE | DBGFPGDMP_FLAGS_NP }
4100 };
4101 int i = RT_ELEMENTS(s_aModeToFlags);
4102 while (i-- > 0)
4103 if (!strcmp(s_aModeToFlags[i].pszName, paArgs[</