VirtualBox

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

Last change on this file since 46159 was 46159, checked in by vboxsync, 12 years ago

Patch manager support in the disassembler, making the 'u' command in the debugger always show unpatched instruction and annoate those instructions which have patches associated with them (in any state).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 180.9 KB
Line 
1/* $Id: DBGCEmulateCodeView.cpp 46159 2013-05-18 19:56:08Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, CodeView / WinDbg Emulation.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DBGC
22#include <VBox/dbg.h>
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/pgm.h>
25#include <VBox/vmm/cpum.h>
26#include <VBox/dis.h>
27#include <VBox/param.h>
28#include <VBox/err.h>
29#include <VBox/log.h>
30
31#include <iprt/asm.h>
32#include <iprt/mem.h>
33#include <iprt/string.h>
34#include <iprt/assert.h>
35#include <iprt/ctype.h>
36
37#include <stdlib.h>
38#include <stdio.h>
39
40#include "DBGCInternal.h"
41
42
43/*******************************************************************************
44* Internal Functions *
45*******************************************************************************/
46static FNDBGCCMD dbgcCmdBrkAccess;
47static FNDBGCCMD dbgcCmdBrkClear;
48static FNDBGCCMD dbgcCmdBrkDisable;
49static FNDBGCCMD dbgcCmdBrkEnable;
50static FNDBGCCMD dbgcCmdBrkList;
51static FNDBGCCMD dbgcCmdBrkSet;
52static FNDBGCCMD dbgcCmdBrkREM;
53static FNDBGCCMD dbgcCmdDumpMem;
54static FNDBGCCMD dbgcCmdDumpDT;
55static FNDBGCCMD dbgcCmdDumpIDT;
56static FNDBGCCMD dbgcCmdDumpPageDir;
57static FNDBGCCMD dbgcCmdDumpPageDirBoth;
58static FNDBGCCMD dbgcCmdDumpPageHierarchy;
59static FNDBGCCMD dbgcCmdDumpPageTable;
60static FNDBGCCMD dbgcCmdDumpPageTableBoth;
61static FNDBGCCMD dbgcCmdDumpTSS;
62static FNDBGCCMD dbgcCmdEditMem;
63static FNDBGCCMD dbgcCmdGo;
64static FNDBGCCMD dbgcCmdListModules;
65static FNDBGCCMD dbgcCmdListNear;
66static FNDBGCCMD dbgcCmdListSource;
67static FNDBGCCMD dbgcCmdMemoryInfo;
68static FNDBGCCMD dbgcCmdReg;
69static FNDBGCCMD dbgcCmdRegGuest;
70static FNDBGCCMD dbgcCmdRegHyper;
71static FNDBGCCMD dbgcCmdRegTerse;
72static FNDBGCCMD dbgcCmdSearchMem;
73static FNDBGCCMD dbgcCmdSearchMemType;
74static FNDBGCCMD dbgcCmdStack;
75static FNDBGCCMD dbgcCmdTrace;
76static FNDBGCCMD dbgcCmdUnassemble;
77
78
79/*******************************************************************************
80* Global Variables *
81*******************************************************************************/
82/** 'ba' arguments. */
83static const DBGCVARDESC g_aArgBrkAcc[] =
84{
85 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
86 { 1, 1, DBGCVAR_CAT_STRING, 0, "access", "The access type: x=execute, rw=read/write (alias r), w=write, i=not implemented." },
87 { 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." },
88 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
89 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
90 { 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)" },
91 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
92};
93
94
95/** 'bc', 'bd', 'be' arguments. */
96static const DBGCVARDESC g_aArgBrks[] =
97{
98 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
99 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "#bp", "Breakpoint number." },
100 { 0, 1, DBGCVAR_CAT_STRING, 0, "all", "All breakpoints." },
101};
102
103
104/** 'bp' arguments. */
105static const DBGCVARDESC g_aArgBrkSet[] =
106{
107 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
108 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
109 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
110 { 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)" },
111 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
112};
113
114
115/** 'br' arguments. */
116static const DBGCVARDESC g_aArgBrkREM[] =
117{
118 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
119 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "address", "The address." },
120 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "passes", "The number of passes before we trigger the breakpoint. (0 is default)" },
121 { 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)" },
122 { 0, 1, DBGCVAR_CAT_STRING, 0, "cmds", "String of commands to be executed when the breakpoint is hit. Quote it!" },
123};
124
125
126/** 'd?' arguments. */
127static const DBGCVARDESC g_aArgDumpMem[] =
128{
129 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
130 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start dumping memory." },
131};
132
133
134/** 'dg', 'dga', 'dl', 'dla' arguments. */
135static const DBGCVARDESC g_aArgDumpDT[] =
136{
137 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
138 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "sel", "Selector or selector range." },
139 { 0, ~0U, DBGCVAR_CAT_POINTER, 0, "address", "Far address which selector should be dumped." },
140};
141
142
143/** 'di', 'dia' arguments. */
144static const DBGCVARDESC g_aArgDumpIDT[] =
145{
146 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
147 { 0, ~0U, DBGCVAR_CAT_NUMBER, 0, "int", "The interrupt vector or interrupt vector range." },
148};
149
150
151/** 'dpd*' arguments. */
152static const DBGCVARDESC g_aArgDumpPD[] =
153{
154 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
155 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "index", "Index into the page directory." },
156 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address which page directory entry to start dumping from. Range is applied to the page directory." },
157};
158
159
160/** 'dpda' arguments. */
161static const DBGCVARDESC g_aArgDumpPDAddr[] =
162{
163 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
164 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address of the page directory entry to start dumping from." },
165};
166
167
168/** 'dph*' arguments. */
169static const DBGCVARDESC g_aArgDumpPH[] =
170{
171 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
172 { 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." },
173 { 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." },
174 { 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." },
175};
176
177
178/** 'dpt?' arguments. */
179static const DBGCVARDESC g_aArgDumpPT[] =
180{
181 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
182 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address which page directory entry to start dumping from." },
183};
184
185
186/** 'dpta' arguments. */
187static const DBGCVARDESC g_aArgDumpPTAddr[] =
188{
189 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
190 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address of the page table entry to start dumping from." },
191};
192
193
194/** 'dt' arguments. */
195static const DBGCVARDESC g_aArgDumpTSS[] =
196{
197 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
198 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "tss", "TSS selector number." },
199 { 0, 1, DBGCVAR_CAT_POINTER, 0, "tss:ign|addr", "TSS address. If the selector is a TSS selector, the offset will be ignored." }
200};
201
202
203/** 'e?' arguments. */
204static const DBGCVARDESC g_aArgEditMem[] =
205{
206 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
207 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to write." },
208 { 1, ~0U, DBGCVAR_CAT_NUMBER, 0, "value", "Value to write." },
209};
210
211
212/** 'lm' arguments. */
213static const DBGCVARDESC g_aArgListMods[] =
214{
215 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
216 { 0, ~0U, DBGCVAR_CAT_STRING, 0, "module", "Module name." },
217};
218
219
220/** 'ln' arguments. */
221static const DBGCVARDESC g_aArgListNear[] =
222{
223 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
224 { 0, ~0U, DBGCVAR_CAT_POINTER, 0, "address", "Address of the symbol to look up." },
225 { 0, ~0U, DBGCVAR_CAT_SYMBOL, 0, "symbol", "Symbol to lookup." },
226};
227
228
229/** 'ls' arguments. */
230static const DBGCVARDESC g_aArgListSource[] =
231{
232 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
233 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start looking for source lines." },
234};
235
236
237/** 'm' argument. */
238static const DBGCVARDESC g_aArgMemoryInfo[] =
239{
240 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
241 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "Pointer to obtain info about." },
242};
243
244
245/** 'r' arguments. */
246static const DBGCVARDESC g_aArgReg[] =
247{
248 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
249 { 0, 1, DBGCVAR_CAT_SYMBOL, 0, "register", "Register to show or set." },
250 { 0, 1, DBGCVAR_CAT_NUMBER_NO_RANGE, DBGCVD_FLAGS_DEP_PREV, "value", "New register value." },
251};
252
253
254/** 's' arguments. */
255static const DBGCVARDESC g_aArgSearchMem[] =
256{
257 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
258 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-b", "Byte string." },
259 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-w", "Word string." },
260 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-d", "DWord string." },
261 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-q", "QWord string." },
262 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-a", "ASCII string." },
263 { 0, 1, DBGCVAR_CAT_OPTION, 0, "-u", "Unicode string." },
264 { 0, 1, DBGCVAR_CAT_OPTION_NUMBER, 0, "-n <Hits>", "Maximum number of hits." },
265 { 0, 1, DBGCVAR_CAT_GC_POINTER, 0, "range", "Register to show or set." },
266 { 0, ~0U, DBGCVAR_CAT_ANY, 0, "pattern", "Pattern to search for." },
267};
268
269
270/** 's?' arguments. */
271static const DBGCVARDESC g_aArgSearchMemType[] =
272{
273 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
274 { 1, 1, DBGCVAR_CAT_GC_POINTER, 0, "range", "Register to show or set." },
275 { 1, ~0U, DBGCVAR_CAT_ANY, 0, "pattern", "Pattern to search for." },
276};
277
278
279/** 'u' arguments. */
280static const DBGCVARDESC g_aArgUnassemble[] =
281{
282 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
283 { 0, 1, DBGCVAR_CAT_POINTER, 0, "address", "Address where to start disassembling." },
284};
285
286
287/** Command descriptors for the CodeView / WinDbg emulation.
288 * The emulation isn't attempting to be identical, only somewhat similar.
289 */
290const DBGCCMD g_aCmdsCodeView[] =
291{
292 /* pszCmd, cArgsMin, cArgsMax, paArgDescs, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
293 { "ba", 3, 6, &g_aArgBrkAcc[0], RT_ELEMENTS(g_aArgBrkAcc), 0, dbgcCmdBrkAccess, "<access> <size> <address> [passes [max passes]] [cmds]",
294 "Sets a data access breakpoint." },
295 { "bc", 1, ~0U, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), 0, dbgcCmdBrkClear, "all | <bp#> [bp# []]", "Deletes a set of breakpoints." },
296 { "bd", 1, ~0U, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), 0, dbgcCmdBrkDisable, "all | <bp#> [bp# []]", "Disables a set of breakpoints." },
297 { "be", 1, ~0U, &g_aArgBrks[0], RT_ELEMENTS(g_aArgBrks), 0, dbgcCmdBrkEnable, "all | <bp#> [bp# []]", "Enables a set of breakpoints." },
298 { "bl", 0, 0, NULL, 0, 0, dbgcCmdBrkList, "", "Lists all the breakpoints." },
299 { "bp", 1, 4, &g_aArgBrkSet[0], RT_ELEMENTS(g_aArgBrkSet), 0, dbgcCmdBrkSet, "<address> [passes [max passes]] [cmds]",
300 "Sets a breakpoint (int 3)." },
301 { "br", 1, 4, &g_aArgBrkREM[0], RT_ELEMENTS(g_aArgBrkREM), 0, dbgcCmdBrkREM, "<address> [passes [max passes]] [cmds]",
302 "Sets a recompiler specific breakpoint." },
303 { "d", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory using last element size." },
304 { "da", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as ascii string." },
305 { "db", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in bytes." },
306 { "dd", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in double words." },
307 { "da", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory as ascii string." },
308 { "dg", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the global descriptor table (GDT)." },
309 { "dga", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the global descriptor table (GDT) including not-present entries." },
310 { "di", 0, ~0U, &g_aArgDumpIDT[0], RT_ELEMENTS(g_aArgDumpIDT), 0, dbgcCmdDumpIDT, "[int [..]]", "Dump the interrupt descriptor table (IDT)." },
311 { "dia", 0, ~0U, &g_aArgDumpIDT[0], RT_ELEMENTS(g_aArgDumpIDT), 0, dbgcCmdDumpIDT, "[int [..]]", "Dump the interrupt descriptor table (IDT) including not-present entries." },
312 { "dl", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the local descriptor table (LDT)." },
313 { "dla", 0, ~0U, &g_aArgDumpDT[0], RT_ELEMENTS(g_aArgDumpDT), 0, dbgcCmdDumpDT, "[sel [..]]", "Dump the local descriptor table (LDT) including not-present entries." },
314 { "dpd", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDir, "[addr|index]", "Dumps page directory entries of the default context." },
315 { "dpda", 0, 1, &g_aArgDumpPDAddr[0],RT_ELEMENTS(g_aArgDumpPDAddr), 0, dbgcCmdDumpPageDir, "[addr]", "Dumps memory at given address as a page directory." },
316 { "dpdb", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDirBoth, "[addr|index]", "Dumps page directory entries of the guest and the hypervisor. " },
317 { "dpdg", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDir, "[addr|index]", "Dumps page directory entries of the guest." },
318 { "dpdh", 0, 1, &g_aArgDumpPD[0], RT_ELEMENTS(g_aArgDumpPD), 0, dbgcCmdDumpPageDir, "[addr|index]", "Dumps page directory entries of the hypervisor. " },
319 { "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." },
320 { "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." },
321 { "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." },
322 { "dpt", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the default context." },
323 { "dpta", 1, 1, &g_aArgDumpPTAddr[0],RT_ELEMENTS(g_aArgDumpPTAddr), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps memory at given address as a page table." },
324 { "dptb", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTableBoth,"<addr>", "Dumps page table entries of the guest and the hypervisor." },
325 { "dptg", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the guest." },
326 { "dpth", 1, 1, &g_aArgDumpPT[0], RT_ELEMENTS(g_aArgDumpPT), 0, dbgcCmdDumpPageTable,"<addr>", "Dumps page table entries of the hypervisor." },
327 { "dq", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in quad words." },
328 { "dt", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the task state segment (TSS)." },
329 { "dt16", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the 16-bit task state segment (TSS)." },
330 { "dt32", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the 32-bit task state segment (TSS)." },
331 { "dt64", 0, 1, &g_aArgDumpTSS[0], RT_ELEMENTS(g_aArgDumpTSS), 0, dbgcCmdDumpTSS, "[tss|tss:ign|addr]", "Dump the 64-bit task state segment (TSS)." },
332 { "dw", 0, 1, &g_aArgDumpMem[0], RT_ELEMENTS(g_aArgDumpMem), 0, dbgcCmdDumpMem, "[addr]", "Dump memory in words." },
333 /** @todo add 'e', 'ea str', 'eza str', 'eu str' and 'ezu str'. See also
334 * dbgcCmdSearchMem and its dbgcVarsToBytes usage. */
335 { "eb", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 1-byte value to memory." },
336 { "ew", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 2-byte value to memory." },
337 { "ed", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 4-byte value to memory." },
338 { "eq", 2, 2, &g_aArgEditMem[0], RT_ELEMENTS(g_aArgEditMem), 0, dbgcCmdEditMem, "<addr> <value>", "Write a 8-byte value to memory." },
339 { "g", 0, 0, NULL, 0, 0, dbgcCmdGo, "", "Continue execution." },
340 { "k", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Callstack." },
341 { "kg", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Callstack - guest." },
342 { "kh", 0, 0, NULL, 0, 0, dbgcCmdStack, "", "Callstack - hypervisor." },
343 { "lm", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules." },
344 { "lmv", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules, verbose." },
345 { "lmo", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules and their segments." },
346 { "lmov", 0, ~0U, &g_aArgListMods[0], RT_ELEMENTS(g_aArgListMods), 0, dbgcCmdListModules, "[module [..]]", "List modules and their segments, verbose." },
347 { "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." },
348 { "ls", 0, 1, &g_aArgListSource[0],RT_ELEMENTS(g_aArgListSource), 0, dbgcCmdListSource, "[addr]", "Source." },
349 { "m", 1, 1, &g_aArgMemoryInfo[0],RT_ELEMENTS(g_aArgMemoryInfo), 0, dbgcCmdMemoryInfo, "<addr>", "Display information about that piece of memory." },
350 { "r", 0, 2, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), 0, dbgcCmdReg, "[reg [newval]]", "Show or set register(s) - active reg set." },
351 { "rg", 0, 2, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), 0, dbgcCmdRegGuest, "[reg [newval]]", "Show or set register(s) - guest reg set." },
352 { "rg32", 0, 0, NULL, 0, 0, dbgcCmdRegGuest, "", "Show 32-bit guest registers." },
353 { "rg64", 0, 0, NULL, 0, 0, dbgcCmdRegGuest, "", "Show 64-bit guest registers." },
354 { "rh", 0, 2, &g_aArgReg[0], RT_ELEMENTS(g_aArgReg), 0, dbgcCmdRegHyper, "[reg [newval]]", "Show or set register(s) - hypervisor reg set." },
355 { "rt", 0, 0, NULL, 0, 0, dbgcCmdRegTerse, "", "Toggles terse / verbose register info." },
356 { "s", 0, ~0U, &g_aArgSearchMem[0], RT_ELEMENTS(g_aArgSearchMem), 0, dbgcCmdSearchMem, "[options] <range> <pattern>", "Continue last search." },
357 { "sa", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for an ascii string." },
358 { "sb", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more bytes." },
359 { "sd", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more double words." },
360 { "sq", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more quad words." },
361 { "su", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for an unicode string." },
362 { "sw", 2, ~0U, &g_aArgSearchMemType[0], RT_ELEMENTS(g_aArgSearchMemType),0, dbgcCmdSearchMemType, "<range> <pattern>", "Search memory for one or more words." },
363 { "t", 0, 0, NULL, 0, 0, dbgcCmdTrace, "", "Instruction trace (step into)." },
364 { "u", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble." },
365 { "u64", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 64-bit code." },
366 { "u32", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 32-bit code." },
367 { "u16", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 16-bit code." },
368 { "uv86", 0, 1, &g_aArgUnassemble[0],RT_ELEMENTS(g_aArgUnassemble), 0, dbgcCmdUnassemble, "[addr]", "Unassemble 16-bit code with v8086/real mode addressing." },
369};
370
371/** The number of commands in the CodeView/WinDbg emulation. */
372const uint32_t g_cCmdsCodeView = RT_ELEMENTS(g_aCmdsCodeView);
373
374
375
376
377/**
378 * @interface_method_impl{FNDBCCMD, The 'go' command.}
379 */
380static DECLCALLBACK(int) dbgcCmdGo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
381{
382 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
383
384 /*
385 * Check if the VM is halted or not before trying to resume it.
386 */
387 if (!DBGFR3IsHalted(pUVM))
388 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The VM is already running");
389
390 int rc = DBGFR3Resume(pUVM);
391 if (RT_FAILURE(rc))
392 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3Resume");
393
394 NOREF(paArgs); NOREF(cArgs);
395 return VINF_SUCCESS;
396}
397
398
399/**
400 * @interface_method_impl{FNDBCCMD, The 'ba' command.}
401 */
402static DECLCALLBACK(int) dbgcCmdBrkAccess(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
403{
404 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
405
406 /*
407 * Interpret access type.
408 */
409 if ( !strchr("xrwi", paArgs[0].u.pszString[0])
410 || paArgs[0].u.pszString[1])
411 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid access type '%s' for '%s'. Valid types are 'e', 'r', 'w' and 'i'",
412 paArgs[0].u.pszString, pCmd->pszCmd);
413 uint8_t fType = 0;
414 switch (paArgs[0].u.pszString[0])
415 {
416 case 'x': fType = X86_DR7_RW_EO; break;
417 case 'r': fType = X86_DR7_RW_RW; break;
418 case 'w': fType = X86_DR7_RW_WO; break;
419 case 'i': fType = X86_DR7_RW_IO; break;
420 }
421
422 /*
423 * Validate size.
424 */
425 if (fType == X86_DR7_RW_EO && paArgs[1].u.u64Number != 1)
426 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid access size %RX64 for '%s'. 'x' access type requires size 1!",
427 paArgs[1].u.u64Number, pCmd->pszCmd);
428 switch (paArgs[1].u.u64Number)
429 {
430 case 1:
431 case 2:
432 case 4:
433 break;
434 /*case 8: - later*/
435 default:
436 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid access size %RX64 for '%s'. 1, 2 or 4!",
437 paArgs[1].u.u64Number, pCmd->pszCmd);
438 }
439 uint8_t cb = (uint8_t)paArgs[1].u.u64Number;
440
441 /*
442 * Convert the pointer to a DBGF address.
443 */
444 DBGFADDRESS Address;
445 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &paArgs[2], &Address);
446 if (RT_FAILURE(rc))
447 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,%DV,)", &paArgs[2]);
448
449 /*
450 * Pick out the optional arguments.
451 */
452 uint64_t iHitTrigger = 0;
453 uint64_t iHitDisable = ~0;
454 const char *pszCmds = NULL;
455 unsigned iArg = 3;
456 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
457 {
458 iHitTrigger = paArgs[iArg].u.u64Number;
459 iArg++;
460 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
461 {
462 iHitDisable = paArgs[iArg].u.u64Number;
463 iArg++;
464 }
465 }
466 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
467 {
468 pszCmds = paArgs[iArg].u.pszString;
469 iArg++;
470 }
471
472 /*
473 * Try set the breakpoint.
474 */
475 uint32_t iBp;
476 rc = DBGFR3BpSetReg(pUVM, &Address, iHitTrigger, iHitDisable, fType, cb, &iBp);
477 if (RT_SUCCESS(rc))
478 {
479 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
480 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
481 if (RT_SUCCESS(rc))
482 return DBGCCmdHlpPrintf(pCmdHlp, "Set access breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
483 if (rc == VERR_DBGC_BP_EXISTS)
484 {
485 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
486 if (RT_SUCCESS(rc))
487 return DBGCCmdHlpPrintf(pCmdHlp, "Updated access breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
488 }
489 int rc2 = DBGFR3BpClear(pDbgc->pUVM, iBp);
490 AssertRC(rc2);
491 }
492 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to set access breakpoint at %RGv", Address.FlatPtr);
493}
494
495
496/**
497 * @interface_method_impl{FNDBCCMD, The 'bc' command.}
498 */
499static DECLCALLBACK(int) dbgcCmdBrkClear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
500{
501 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
502
503 /*
504 * Enumerate the arguments.
505 */
506 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
507 int rc = VINF_SUCCESS;
508 for (unsigned iArg = 0; iArg < cArgs && RT_SUCCESS(rc); iArg++)
509 {
510 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
511 {
512 /* one */
513 uint32_t iBp = (uint32_t)paArgs[iArg].u.u64Number;
514 if (iBp == paArgs[iArg].u.u64Number)
515 {
516 int rc2 = DBGFR3BpClear(pUVM, iBp);
517 if (RT_FAILURE(rc2))
518 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpClear(,%#x)", iBp);
519 if (RT_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
520 dbgcBpDelete(pDbgc, iBp);
521 }
522 else
523 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Breakpoint id %RX64 is too large", paArgs[iArg].u.u64Number);
524 }
525 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
526 {
527 /* all */
528 PDBGCBP pBp = pDbgc->pFirstBp;
529 while (pBp)
530 {
531 uint32_t iBp = pBp->iBp;
532 pBp = pBp->pNext;
533
534 int rc2 = DBGFR3BpClear(pUVM, iBp);
535 if (RT_FAILURE(rc2))
536 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpClear(,%#x)", iBp);
537 if (RT_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
538 dbgcBpDelete(pDbgc, iBp);
539 }
540 }
541 else
542 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid argument '%s'", paArgs[iArg].u.pszString);
543 }
544 return rc;
545}
546
547
548/**
549 * @interface_method_impl{FNDBCCMD, The 'bd' command.}
550 */
551static DECLCALLBACK(int) dbgcCmdBrkDisable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
552{
553 /*
554 * Enumerate the arguments.
555 */
556 int rc = VINF_SUCCESS;
557 for (unsigned iArg = 0; iArg < cArgs && RT_SUCCESS(rc); iArg++)
558 {
559 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
560 {
561 /* one */
562 uint32_t iBp = (uint32_t)paArgs[iArg].u.u64Number;
563 if (iBp == paArgs[iArg].u.u64Number)
564 {
565 rc = DBGFR3BpDisable(pUVM, iBp);
566 if (RT_FAILURE(rc))
567 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3BpDisable failed for breakpoint %#x", iBp);
568 }
569 else
570 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Breakpoint id %RX64 is too large", paArgs[iArg].u.u64Number);
571 }
572 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
573 {
574 /* all */
575 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
576 for (PDBGCBP pBp = pDbgc->pFirstBp; pBp; pBp = pBp->pNext)
577 {
578 int rc2 = DBGFR3BpDisable(pUVM, pBp->iBp);
579 if (RT_FAILURE(rc2))
580 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpDisable failed for breakpoint %#x", pBp->iBp);
581 }
582 }
583 else
584 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid argument '%s'", paArgs[iArg].u.pszString);
585 }
586 return rc;
587}
588
589
590/**
591 * @interface_method_impl{FNDBCCMD, The 'be' command.}
592 */
593static DECLCALLBACK(int) dbgcCmdBrkEnable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
594{
595 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
596
597 /*
598 * Enumerate the arguments.
599 */
600 int rc = VINF_SUCCESS;
601 for (unsigned iArg = 0; iArg < cArgs && RT_SUCCESS(rc); iArg++)
602 {
603 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
604 {
605 /* one */
606 uint32_t iBp = (uint32_t)paArgs[iArg].u.u64Number;
607 if (iBp == paArgs[iArg].u.u64Number)
608 {
609 rc = DBGFR3BpEnable(pUVM, iBp);
610 if (RT_FAILURE(rc))
611 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3BpEnable failed for breakpoint %#x", iBp);
612 }
613 else
614 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Breakpoint id %RX64 is too large", paArgs[iArg].u.u64Number);
615 }
616 else if (!strcmp(paArgs[iArg].u.pszString, "all"))
617 {
618 /* all */
619 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
620 for (PDBGCBP pBp = pDbgc->pFirstBp; pBp; pBp = pBp->pNext)
621 {
622 int rc2 = DBGFR3BpEnable(pUVM, pBp->iBp);
623 if (RT_FAILURE(rc2))
624 rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc2, "DBGFR3BpEnable failed for breakpoint %#x", pBp->iBp);
625 }
626 }
627 else
628 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Invalid argument '%s'", paArgs[iArg].u.pszString);
629 }
630 return rc;
631}
632
633
634/**
635 * Breakpoint enumeration callback function.
636 *
637 * @returns VBox status code. Any failure will stop the enumeration.
638 * @param pUVM The user mode VM handle.
639 * @param pvUser The user argument.
640 * @param pBp Pointer to the breakpoint information. (readonly)
641 */
642static DECLCALLBACK(int) dbgcEnumBreakpointsCallback(PUVM pUVM, void *pvUser, PCDBGFBP pBp)
643{
644 PDBGC pDbgc = (PDBGC)pvUser;
645 PDBGCBP pDbgcBp = dbgcBpGet(pDbgc, pBp->iBp);
646
647 /*
648 * BP type and size.
649 */
650 char chType;
651 char cb = 1;
652 switch (pBp->enmType)
653 {
654 case DBGFBPTYPE_INT3:
655 chType = 'p';
656 break;
657 case DBGFBPTYPE_REG:
658 switch (pBp->u.Reg.fType)
659 {
660 case X86_DR7_RW_EO: chType = 'x'; break;
661 case X86_DR7_RW_WO: chType = 'w'; break;
662 case X86_DR7_RW_IO: chType = 'i'; break;
663 case X86_DR7_RW_RW: chType = 'r'; break;
664 default: chType = '?'; break;
665
666 }
667 cb = pBp->u.Reg.cb;
668 break;
669 case DBGFBPTYPE_REM:
670 chType = 'r';
671 break;
672 default:
673 chType = '?';
674 break;
675 }
676
677 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%#4x %c %d %c %RGv %04RX64 (%04RX64 to ",
678 pBp->iBp, pBp->fEnabled ? 'e' : 'd', (int)cb, chType,
679 pBp->GCPtr, pBp->cHits, pBp->iHitTrigger);
680 if (pBp->iHitDisable == ~(uint64_t)0)
681 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "~0) ");
682 else
683 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%04RX64)", pBp->iHitDisable);
684
685 /*
686 * Try resolve the address.
687 */
688 RTDBGSYMBOL Sym;
689 RTINTPTR off;
690 DBGFADDRESS Addr;
691 int rc = DBGFR3AsSymbolByAddr(pUVM, pDbgc->hDbgAs, DBGFR3AddrFromFlat(pDbgc->pUVM, &Addr, pBp->GCPtr),
692 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &off, &Sym, NULL);
693 if (RT_SUCCESS(rc))
694 {
695 if (!off)
696 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%s", Sym.szName);
697 else if (off > 0)
698 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%s+%RGv", Sym.szName, off);
699 else
700 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "%s-%RGv", Sym.szName, -off);
701 }
702
703 /*
704 * The commands.
705 */
706 if (pDbgcBp)
707 {
708 if (pDbgcBp->cchCmd)
709 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "\n cmds: '%s'\n", pDbgcBp->szCmd);
710 else
711 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "\n");
712 }
713 else
714 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, " [unknown bp]\n");
715
716 return VINF_SUCCESS;
717}
718
719
720/**
721 * @interface_method_impl{FNDBCCMD, The 'bl' command.}
722 */
723static DECLCALLBACK(int) dbgcCmdBrkList(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
724{
725 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
726 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 0);
727 NOREF(paArgs);
728
729 /*
730 * Enumerate the breakpoints.
731 */
732 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
733 int rc = DBGFR3BpEnum(pUVM, dbgcEnumBreakpointsCallback, pDbgc);
734 if (RT_FAILURE(rc))
735 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGFR3BpEnum");
736 return rc;
737}
738
739
740/**
741 * @interface_method_impl{FNDBCCMD, The 'bp' command.}
742 */
743static DECLCALLBACK(int) dbgcCmdBrkSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
744{
745 /*
746 * Convert the pointer to a DBGF address.
747 */
748 DBGFADDRESS Address;
749 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
750 if (RT_FAILURE(rc))
751 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,'%DV',)", &paArgs[0]);
752
753 /*
754 * Pick out the optional arguments.
755 */
756 uint64_t iHitTrigger = 0;
757 uint64_t iHitDisable = ~0;
758 const char *pszCmds = NULL;
759 unsigned iArg = 1;
760 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
761 {
762 iHitTrigger = paArgs[iArg].u.u64Number;
763 iArg++;
764 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
765 {
766 iHitDisable = paArgs[iArg].u.u64Number;
767 iArg++;
768 }
769 }
770 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
771 {
772 pszCmds = paArgs[iArg].u.pszString;
773 iArg++;
774 }
775
776 /*
777 * Try set the breakpoint.
778 */
779 uint32_t iBp;
780 rc = DBGFR3BpSet(pUVM, &Address, iHitTrigger, iHitDisable, &iBp);
781 if (RT_SUCCESS(rc))
782 {
783 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
784 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
785 if (RT_SUCCESS(rc))
786 return DBGCCmdHlpPrintf(pCmdHlp, "Set breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
787 if (rc == VERR_DBGC_BP_EXISTS)
788 {
789 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
790 if (RT_SUCCESS(rc))
791 return DBGCCmdHlpPrintf(pCmdHlp, "Updated breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
792 }
793 int rc2 = DBGFR3BpClear(pDbgc->pUVM, iBp);
794 AssertRC(rc2);
795 }
796 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to set breakpoint at %RGv", Address.FlatPtr);
797}
798
799
800/**
801 * @interface_method_impl{FNDBCCMD, The 'br' command.}
802 */
803static DECLCALLBACK(int) dbgcCmdBrkREM(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
804{
805 /*
806 * Convert the pointer to a DBGF address.
807 */
808 DBGFADDRESS Address;
809 int rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &paArgs[0], &Address);
810 if (RT_FAILURE(rc))
811 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr(,'%DV',)", &paArgs[0]);
812
813 /*
814 * Pick out the optional arguments.
815 */
816 uint64_t iHitTrigger = 0;
817 uint64_t iHitDisable = ~0;
818 const char *pszCmds = NULL;
819 unsigned iArg = 1;
820 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
821 {
822 iHitTrigger = paArgs[iArg].u.u64Number;
823 iArg++;
824 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
825 {
826 iHitDisable = paArgs[iArg].u.u64Number;
827 iArg++;
828 }
829 }
830 if (iArg < cArgs && paArgs[iArg].enmType == DBGCVAR_TYPE_STRING)
831 {
832 pszCmds = paArgs[iArg].u.pszString;
833 iArg++;
834 }
835
836 /*
837 * Try set the breakpoint.
838 */
839 uint32_t iBp;
840 rc = DBGFR3BpSetREM(pUVM, &Address, iHitTrigger, iHitDisable, &iBp);
841 if (RT_SUCCESS(rc))
842 {
843 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
844 rc = dbgcBpAdd(pDbgc, iBp, pszCmds);
845 if (RT_SUCCESS(rc))
846 return DBGCCmdHlpPrintf(pCmdHlp, "Set REM breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
847 if (rc == VERR_DBGC_BP_EXISTS)
848 {
849 rc = dbgcBpUpdate(pDbgc, iBp, pszCmds);
850 if (RT_SUCCESS(rc))
851 return DBGCCmdHlpPrintf(pCmdHlp, "Updated REM breakpoint %u at %RGv\n", iBp, Address.FlatPtr);
852 }
853 int rc2 = DBGFR3BpClear(pDbgc->pUVM, iBp);
854 AssertRC(rc2);
855 }
856 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to set REM breakpoint at %RGv", Address.FlatPtr);
857}
858
859
860/**
861 * Helps the unassmble ('u') command display symbols it starts at and passes.
862 *
863 * @param pUVM The user mode VM handle.
864 * @param pCmdHlp The command helpers for printing via.
865 * @param hDbgAs The address space to look up addresses in.
866 * @param pAddress The current address.
867 * @param pcbCallAgain Where to return the distance to the next check (in
868 * instruction bytes).
869 */
870static void dbgcCmdUnassambleHelpListNear(PUVM pUVM, PDBGCCMDHLP pCmdHlp, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress,
871 PRTUINTPTR pcbCallAgain)
872{
873 RTDBGSYMBOL Symbol;
874 RTGCINTPTR offDispSym;
875 int rc = DBGFR3AsSymbolByAddr(pUVM, hDbgAs, pAddress, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offDispSym, &Symbol, NULL);
876 if (RT_FAILURE(rc) || offDispSym > _1G)
877 rc = DBGFR3AsSymbolByAddr(pUVM, hDbgAs, pAddress, RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL, &offDispSym, &Symbol, NULL);
878 if (RT_SUCCESS(rc) && offDispSym < _1G)
879 {
880 if (!offDispSym)
881 {
882 DBGCCmdHlpPrintf(pCmdHlp, "%s:\n", Symbol.szName);
883 *pcbCallAgain = Symbol.cb;
884 }
885 else if (offDispSym > 0)
886 {
887 DBGCCmdHlpPrintf(pCmdHlp, "%s+%#llx:\n", Symbol.szName, (uint64_t)offDispSym);
888 *pcbCallAgain = Symbol.cb > (RTGCUINTPTR)offDispSym ? Symbol.cb - (RTGCUINTPTR)offDispSym : 1;
889 }
890 else
891 {
892 DBGCCmdHlpPrintf(pCmdHlp, "%s-%#llx:\n", Symbol.szName, (uint64_t)-offDispSym);
893 *pcbCallAgain = (RTGCUINTPTR)-offDispSym + Symbol.cb;
894 }
895 }
896 else
897 *pcbCallAgain = UINT32_MAX;
898}
899
900
901/**
902 * @interface_method_impl{FNDBCCMD, The 'u' command.}
903 */
904static DECLCALLBACK(int) dbgcCmdUnassemble(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
905{
906 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
907
908 /*
909 * Validate input.
910 */
911 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
912 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs <= 1);
913 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 0 || DBGCVAR_ISPOINTER(paArgs[0].enmType));
914
915 if (!cArgs && !DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
916 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Don't know where to start disassembling");
917
918 /*
919 * Check the desired mode.
920 */
921 unsigned fFlags = DBGF_DISAS_FLAGS_NO_ADDRESS | DBGF_DISAS_FLAGS_UNPATCHED_BYTES | DBGF_DISAS_FLAGS_ANNOTATE_PATCHED;
922 switch (pCmd->pszCmd[1])
923 {
924 default: AssertFailed();
925 case '\0': fFlags |= DBGF_DISAS_FLAGS_DEFAULT_MODE; break;
926 case '6': fFlags |= DBGF_DISAS_FLAGS_64BIT_MODE; break;
927 case '3': fFlags |= DBGF_DISAS_FLAGS_32BIT_MODE; break;
928 case '1': fFlags |= DBGF_DISAS_FLAGS_16BIT_MODE; break;
929 case 'v': fFlags |= DBGF_DISAS_FLAGS_16BIT_REAL_MODE; break;
930 }
931
932 /** @todo should use DBGFADDRESS for everything */
933
934 /*
935 * Find address.
936 */
937 if (!cArgs)
938 {
939 if (!DBGCVAR_ISPOINTER(pDbgc->DisasmPos.enmType))
940 {
941 /** @todo Batch query CS, RIP & CPU mode. */
942 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
943 if ( pDbgc->fRegCtxGuest
944 && CPUMIsGuestIn64BitCode(pVCpu))
945 {
946 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FLAT;
947 pDbgc->SourcePos.u.GCFlat = CPUMGetGuestRIP(pVCpu);
948 }
949 else
950 {
951 pDbgc->DisasmPos.enmType = DBGCVAR_TYPE_GC_FAR;
952 pDbgc->SourcePos.u.GCFar.off = pDbgc->fRegCtxGuest ? CPUMGetGuestEIP(pVCpu) : CPUMGetHyperEIP(pVCpu);
953 pDbgc->SourcePos.u.GCFar.sel = pDbgc->fRegCtxGuest ? CPUMGetGuestCS(pVCpu) : CPUMGetHyperCS(pVCpu);
954 }
955
956 if (pDbgc->fRegCtxGuest)
957 fFlags |= DBGF_DISAS_FLAGS_CURRENT_GUEST;
958 else
959 fFlags |= DBGF_DISAS_FLAGS_CURRENT_HYPER | DBGF_DISAS_FLAGS_HYPER;
960 }
961 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_NONE;
962 }
963 else
964 pDbgc->DisasmPos = paArgs[0];
965 pDbgc->pLastPos = &pDbgc->DisasmPos;
966
967 /*
968 * Range.
969 */
970 switch (pDbgc->DisasmPos.enmRangeType)
971 {
972 case DBGCVAR_RANGE_NONE:
973 pDbgc->DisasmPos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
974 pDbgc->DisasmPos.u64Range = 10;
975 break;
976
977 case DBGCVAR_RANGE_ELEMENTS:
978 if (pDbgc->DisasmPos.u64Range > 2048)
979 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Too many lines requested. Max is 2048 lines");
980 break;
981
982 case DBGCVAR_RANGE_BYTES:
983 if (pDbgc->DisasmPos.u64Range > 65536)
984 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The requested range is too big. Max is 64KB");
985 break;
986
987 default:
988 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Unknown range type %d", pDbgc->DisasmPos.enmRangeType);
989 }
990
991 /*
992 * Convert physical and host addresses to guest addresses.
993 */
994 RTDBGAS hDbgAs = pDbgc->hDbgAs;
995 int rc;
996 switch (pDbgc->DisasmPos.enmType)
997 {
998 case DBGCVAR_TYPE_GC_FLAT:
999 case DBGCVAR_TYPE_GC_FAR:
1000 break;
1001 case DBGCVAR_TYPE_GC_PHYS:
1002 hDbgAs = DBGF_AS_PHYS;
1003 case DBGCVAR_TYPE_HC_FLAT:
1004 case DBGCVAR_TYPE_HC_PHYS:
1005 {
1006 DBGCVAR VarTmp;
1007 rc = DBGCCmdHlpEval(pCmdHlp, &VarTmp, "%%(%Dv)", &pDbgc->DisasmPos);
1008 if (RT_FAILURE(rc))
1009 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "failed to evaluate '%%(%Dv)'", &pDbgc->DisasmPos);
1010 pDbgc->DisasmPos = VarTmp;
1011 break;
1012 }
1013 default: AssertFailed(); break;
1014 }
1015
1016 DBGFADDRESS CurAddr;
1017 rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &pDbgc->DisasmPos, &CurAddr);
1018 if (RT_FAILURE(rc))
1019 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToDbgfAddr failed on '%Dv'", &pDbgc->DisasmPos);
1020
1021 if (CurAddr.fFlags & DBGFADDRESS_FLAGS_HMA)
1022 fFlags |= DBGF_DISAS_FLAGS_HYPER; /* This crap is due to not using DBGFADDRESS as DBGFR3Disas* input. */
1023
1024 /*
1025 * Figure out where we are and display it. Also calculate when we need to
1026 * check for a new symbol if possible.
1027 */
1028 RTGCUINTPTR cbCheckSymbol;
1029 dbgcCmdUnassambleHelpListNear(pUVM, pCmdHlp, hDbgAs, &CurAddr, &cbCheckSymbol);
1030
1031 /*
1032 * Do the disassembling.
1033 */
1034 unsigned cTries = 32;
1035 int iRangeLeft = (int)pDbgc->DisasmPos.u64Range;
1036 if (iRangeLeft == 0) /* kludge for 'r'. */
1037 iRangeLeft = -1;
1038 for (;;)
1039 {
1040 /*
1041 * Disassemble the instruction.
1042 */
1043 char szDis[256];
1044 uint32_t cbInstr = 1;
1045 if (pDbgc->DisasmPos.enmType == DBGCVAR_TYPE_GC_FLAT)
1046 rc = DBGFR3DisasInstrEx(pUVM, pDbgc->idCpu, DBGF_SEL_FLAT, pDbgc->DisasmPos.u.GCFlat, fFlags,
1047 &szDis[0], sizeof(szDis), &cbInstr);
1048 else
1049 rc = DBGFR3DisasInstrEx(pUVM, pDbgc->idCpu, pDbgc->DisasmPos.u.GCFar.sel, pDbgc->DisasmPos.u.GCFar.off, fFlags,
1050 &szDis[0], sizeof(szDis), &cbInstr);
1051 if (RT_SUCCESS(rc))
1052 {
1053 /* print it */
1054 rc = DBGCCmdHlpPrintf(pCmdHlp, "%-16DV %s\n", &pDbgc->DisasmPos, &szDis[0]);
1055 if (RT_FAILURE(rc))
1056 return rc;
1057 }
1058 else
1059 {
1060 /* bitch. */
1061 int rc2 = DBGCCmdHlpPrintf(pCmdHlp, "Failed to disassemble instruction, skipping one byte.\n");
1062 if (RT_FAILURE(rc2))
1063 return rc2;
1064 if (cTries-- > 0)
1065 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Too many disassembly failures. Giving up");
1066 cbInstr = 1;
1067 }
1068
1069 /* advance */
1070 if (iRangeLeft < 0) /* 'r' */
1071 break;
1072 if (pDbgc->DisasmPos.enmRangeType == DBGCVAR_RANGE_ELEMENTS)
1073 iRangeLeft--;
1074 else
1075 iRangeLeft -= cbInstr;
1076 rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->DisasmPos, "(%Dv) + %x", &pDbgc->DisasmPos, cbInstr);
1077 if (RT_FAILURE(rc))
1078 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpEval(,,'(%Dv) + %x')", &pDbgc->DisasmPos, cbInstr);
1079 if (iRangeLeft <= 0)
1080 break;
1081 fFlags &= ~(DBGF_DISAS_FLAGS_CURRENT_GUEST | DBGF_DISAS_FLAGS_CURRENT_HYPER);
1082
1083 /* Print next symbol? */
1084 if (cbCheckSymbol <= cbInstr)
1085 {
1086 rc = DBGCCmdHlpVarToDbgfAddr(pCmdHlp, &pDbgc->DisasmPos, &CurAddr);
1087 if (RT_SUCCESS(rc))
1088 dbgcCmdUnassambleHelpListNear(pUVM, pCmdHlp, hDbgAs, &CurAddr, &cbCheckSymbol);
1089 else
1090 cbCheckSymbol = UINT32_MAX;
1091 }
1092 else
1093 cbCheckSymbol -= cbInstr;
1094 }
1095
1096 NOREF(pCmd);
1097 return VINF_SUCCESS;
1098}
1099
1100
1101/**
1102 * @interface_method_impl{FNDBCCMD, The 'ls' command.}
1103 */
1104static DECLCALLBACK(int) dbgcCmdListSource(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1105{
1106 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1107
1108 /*
1109 * Validate input.
1110 */
1111 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
1112 if (cArgs == 1)
1113 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
1114 if (!pUVM && !cArgs && !DBGCVAR_ISPOINTER(pDbgc->SourcePos.enmType))
1115 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Don't know where to start listing...");
1116 if (!pUVM && cArgs && DBGCVAR_ISGCPOINTER(paArgs[0].enmType))
1117 return DBGCCmdHlpFail(pCmdHlp, pCmd, "GC address but no VM");
1118
1119 /*
1120 * Find address.
1121 */
1122 if (!cArgs)
1123 {
1124 if (!DBGCVAR_ISPOINTER(pDbgc->SourcePos.enmType))
1125 {
1126 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
1127 pDbgc->SourcePos.enmType = DBGCVAR_TYPE_GC_FAR;
1128 pDbgc->SourcePos.u.GCFar.off = pDbgc->fRegCtxGuest ? CPUMGetGuestEIP(pVCpu) : CPUMGetHyperEIP(pVCpu);
1129 pDbgc->SourcePos.u.GCFar.sel = pDbgc->fRegCtxGuest ? CPUMGetGuestCS(pVCpu) : CPUMGetHyperCS(pVCpu);
1130 }
1131 pDbgc->SourcePos.enmRangeType = DBGCVAR_RANGE_NONE;
1132 }
1133 else
1134 pDbgc->SourcePos = paArgs[0];
1135 pDbgc->pLastPos = &pDbgc->SourcePos;
1136
1137 /*
1138 * Ensure the source address is flat GC.
1139 */
1140 switch (pDbgc->SourcePos.enmType)
1141 {
1142 case DBGCVAR_TYPE_GC_FLAT:
1143 break;
1144 case DBGCVAR_TYPE_GC_PHYS:
1145 case DBGCVAR_TYPE_GC_FAR:
1146 case DBGCVAR_TYPE_HC_FLAT:
1147 case DBGCVAR_TYPE_HC_PHYS:
1148 {
1149 int rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->SourcePos, "%%(%Dv)", &pDbgc->SourcePos);
1150 if (RT_FAILURE(rc))
1151 return DBGCCmdHlpPrintf(pCmdHlp, "error: Invalid address or address type. (rc=%d)\n", rc);
1152 break;
1153 }
1154 default: AssertFailed(); break;
1155 }
1156
1157 /*
1158 * Range.
1159 */
1160 switch (pDbgc->SourcePos.enmRangeType)
1161 {
1162 case DBGCVAR_RANGE_NONE:
1163 pDbgc->SourcePos.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
1164 pDbgc->SourcePos.u64Range = 10;
1165 break;
1166
1167 case DBGCVAR_RANGE_ELEMENTS:
1168 if (pDbgc->SourcePos.u64Range > 2048)
1169 return DBGCCmdHlpPrintf(pCmdHlp, "error: Too many lines requested. Max is 2048 lines.\n");
1170 break;
1171
1172 case DBGCVAR_RANGE_BYTES:
1173 if (pDbgc->SourcePos.u64Range > 65536)
1174 return DBGCCmdHlpPrintf(pCmdHlp, "error: The requested range is too big. Max is 64KB.\n");
1175 break;
1176
1177 default:
1178 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: Unknown range type %d.\n", pDbgc->SourcePos.enmRangeType);
1179 }
1180
1181 /*
1182 * Do the disassembling.
1183 */
1184 bool fFirst = 1;
1185 DBGFLINE LinePrev = { 0, 0, "" };
1186 int iRangeLeft = (int)pDbgc->SourcePos.u64Range;
1187 if (iRangeLeft == 0) /* kludge for 'r'. */
1188 iRangeLeft = -1;
1189 for (;;)
1190 {
1191 /*
1192 * Get line info.
1193 */
1194 DBGFLINE Line;
1195 RTGCINTPTR off;
1196 int rc = DBGFR3LineByAddr(pUVM, pDbgc->SourcePos.u.GCFlat, &off, &Line);
1197 if (RT_FAILURE(rc))
1198 return VINF_SUCCESS;
1199
1200 unsigned cLines = 0;
1201 if (memcmp(&Line, &LinePrev, sizeof(Line)))
1202 {
1203 /*
1204 * Print filenamename
1205 */
1206 if (!fFirst && strcmp(Line.szFilename, LinePrev.szFilename))
1207 fFirst = true;
1208 if (fFirst)
1209 {
1210 rc = DBGCCmdHlpPrintf(pCmdHlp, "[%s @ %d]\n", Line.szFilename, Line.uLineNo);
1211 if (RT_FAILURE(rc))
1212 return rc;
1213 }
1214
1215 /*
1216 * Try open the file and read the line.
1217 */
1218 FILE *phFile = fopen(Line.szFilename, "r");
1219 if (phFile)
1220 {
1221 /* Skip ahead to the desired line. */
1222 char szLine[4096];
1223 unsigned cBefore = fFirst ? RT_MIN(2, Line.uLineNo - 1) : Line.uLineNo - LinePrev.uLineNo - 1;
1224 if (cBefore > 7)
1225 cBefore = 0;
1226 unsigned cLeft = Line.uLineNo - cBefore;
1227 while (cLeft > 0)
1228 {
1229 szLine[0] = '\0';
1230 if (!fgets(szLine, sizeof(szLine), phFile))
1231 break;
1232 cLeft--;
1233 }
1234 if (!cLeft)
1235 {
1236 /* print the before lines */
1237 for (;;)
1238 {
1239 size_t cch = strlen(szLine);
1240 while (cch > 0 && (szLine[cch - 1] == '\r' || szLine[cch - 1] == '\n' || RT_C_IS_SPACE(szLine[cch - 1])) )
1241 szLine[--cch] = '\0';
1242 if (cBefore-- <= 0)
1243 break;
1244
1245 rc = DBGCCmdHlpPrintf(pCmdHlp, " %4d: %s\n", Line.uLineNo - cBefore - 1, szLine);
1246 szLine[0] = '\0';
1247 fgets(szLine, sizeof(szLine), phFile);
1248 cLines++;
1249 }
1250 /* print the actual line */
1251 rc = DBGCCmdHlpPrintf(pCmdHlp, "%08llx %4d: %s\n", Line.Address, Line.uLineNo, szLine);
1252 }
1253 fclose(phFile);
1254 if (RT_FAILURE(rc))
1255 return rc;
1256 fFirst = false;
1257 }
1258 else
1259 return DBGCCmdHlpPrintf(pCmdHlp, "Warning: couldn't open source file '%s'\n", Line.szFilename);
1260
1261 LinePrev = Line;
1262 }
1263
1264
1265 /*
1266 * Advance
1267 */
1268 if (iRangeLeft < 0) /* 'r' */
1269 break;
1270 if (pDbgc->SourcePos.enmRangeType == DBGCVAR_RANGE_ELEMENTS)
1271 iRangeLeft -= cLines;
1272 else
1273 iRangeLeft -= 1;
1274 rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->SourcePos, "(%Dv) + %x", &pDbgc->SourcePos, 1);
1275 if (RT_FAILURE(rc))
1276 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->SourcePos, 1);
1277 if (iRangeLeft <= 0)
1278 break;
1279 }
1280
1281 NOREF(pCmd);
1282 return 0;
1283}
1284
1285
1286/**
1287 * @interface_method_impl{FNDBCCMD, The 'r' command.}
1288 */
1289static DECLCALLBACK(int) dbgcCmdReg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1290{
1291 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1292 if (!pDbgc->fRegCtxGuest)
1293 return dbgcCmdRegHyper(pCmd, pCmdHlp, pUVM, paArgs, cArgs);
1294 return dbgcCmdRegGuest(pCmd, pCmdHlp, pUVM, paArgs, cArgs);
1295}
1296
1297
1298/**
1299 * @interface_method_impl{FNDBCCMD, Common worker for the dbgcCmdReg*()
1300 * commands.}
1301 */
1302static DECLCALLBACK(int) dbgcCmdRegCommon(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs,
1303 const char *pszPrefix)
1304{
1305 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1306 Assert(cArgs == 1 || cArgs == 2); /* cArgs == 0 is handled by the caller */
1307 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 1 || cArgs == 2);
1308 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[0].enmType == DBGCVAR_TYPE_STRING
1309 || paArgs[0].enmType == DBGCVAR_TYPE_SYMBOL);
1310
1311 /*
1312 * Parse the register name and kind.
1313 */
1314 const char *pszReg = paArgs[0].u.pszString;
1315 if (*pszReg == '@')
1316 pszReg++;
1317 VMCPUID idCpu = pDbgc->idCpu;
1318 if (*pszPrefix)
1319 idCpu |= DBGFREG_HYPER_VMCPUID;
1320 if (*pszReg == '.')
1321 {
1322 pszReg++;
1323 idCpu |= DBGFREG_HYPER_VMCPUID;
1324 }
1325 const char * const pszActualPrefix = idCpu & DBGFREG_HYPER_VMCPUID ? "." : "";
1326
1327 /*
1328 * Query the register type & value (the setter needs the type).
1329 */
1330 DBGFREGVALTYPE enmType;
1331 DBGFREGVAL Value;
1332 int rc = DBGFR3RegNmQuery(pUVM, idCpu, pszReg, &Value, &enmType);
1333 if (RT_FAILURE(rc))
1334 {
1335 if (rc == VERR_DBGF_REGISTER_NOT_FOUND)
1336 return DBGCCmdHlpVBoxError(pCmdHlp, VERR_INVALID_PARAMETER, "Unknown register: '%s%s'.\n",
1337 pszActualPrefix, pszReg);
1338 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegNmQuery failed querying '%s%s': %Rrc.\n",
1339 pszActualPrefix, pszReg, rc);
1340 }
1341 if (cArgs == 1)
1342 {
1343 /*
1344 * Show the register.
1345 */
1346 char szValue[160];
1347 rc = DBGFR3RegFormatValue(szValue, sizeof(szValue), &Value, enmType, true /*fSpecial*/);
1348 if (RT_SUCCESS(rc))
1349 rc = DBGCCmdHlpPrintf(pCmdHlp, "%s%s=%s\n", pszActualPrefix, pszReg, szValue);
1350 else
1351 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegFormatValue failed: %Rrc.\n", rc);
1352 }
1353 else if (cArgs == 2)
1354 {
1355 /*
1356 * Modify the register.
1357 */
1358 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, paArgs[1].enmType == DBGCVAR_TYPE_STRING
1359 || paArgs[1].enmType == DBGCVAR_TYPE_SYMBOL);
1360 if (enmType != DBGFREGVALTYPE_DTR)
1361 {
1362 enmType = DBGFREGVALTYPE_U64;
1363 rc = DBGCCmdHlpVarToNumber(pCmdHlp, &paArgs[1], &Value.u64);
1364 }
1365 else
1366 {
1367 enmType = DBGFREGVALTYPE_DTR;
1368 rc = DBGCCmdHlpVarToNumber(pCmdHlp, &paArgs[1], &Value.dtr.u64Base);
1369 if (RT_SUCCESS(rc) && paArgs[1].enmRangeType != DBGCVAR_RANGE_NONE)
1370 Value.dtr.u32Limit = (uint32_t)paArgs[1].u64Range;
1371 }
1372 if (RT_SUCCESS(rc))
1373 {
1374 rc = DBGFR3RegNmSet(pUVM, idCpu, pszReg, &Value, enmType);
1375 if (RT_FAILURE(rc))
1376 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegNmSet failed settings '%s%s': %Rrc\n",
1377 pszActualPrefix, pszReg, rc);
1378 }
1379 else
1380 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegFormatValue failed: %Rrc.\n", rc);
1381 }
1382 else
1383 {
1384 NOREF(pCmd); NOREF(paArgs);
1385 rc = DBGCCmdHlpPrintf(pCmdHlp, "Huh? cArgs=%d Expected 0, 1 or 2!\n", cArgs);
1386 }
1387 return rc;
1388}
1389
1390
1391/**
1392 * @interface_method_impl{FNDBCCMD, The 'rg', 'rg64' and 'rg32' commands.}
1393 */
1394static DECLCALLBACK(int) dbgcCmdRegGuest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1395{
1396 /*
1397 * Show all registers our selves.
1398 */
1399 if (cArgs == 0)
1400 {
1401 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1402 bool const f64BitMode = !strcmp(pCmd->pszCmd, "rg64")
1403 || ( !strcmp(pCmd->pszCmd, "rg32")
1404 && CPUMIsGuestIn64BitCode(VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu)));
1405 char szDisAndRegs[8192];
1406 int rc;
1407
1408 if (pDbgc->fRegTerse)
1409 {
1410 if (f64BitMode)
1411 rc = DBGFR3RegPrintf(pUVM, pDbgc->idCpu, &szDisAndRegs[0], sizeof(szDisAndRegs),
1412 "u %016VR{rip} L 0\n"
1413 "rax=%016VR{rax} rbx=%016VR{rbx} rcx=%016VR{rcx} rdx=%016VR{rdx}\n"
1414 "rsi=%016VR{rsi} rdi=%016VR{rdi} r8 =%016VR{r8} r9 =%016VR{r9}\n"
1415 "r10=%016VR{r10} r11=%016VR{r11} r12=%016VR{r12} r13=%016VR{r13}\n"
1416 "r14=%016VR{r14} r15=%016VR{r15} %VRF{rflags}\n"
1417 "rip=%016VR{rip} rsp=%016VR{rsp} rbp=%016VR{rbp}\n"
1418 "cs=%04VR{cs} ds=%04VR{ds} es=%04VR{es} fs=%04VR{fs} gs=%04VR{gs} ss=%04VR{ss} rflags=%08VR{rflags}\n");
1419 else
1420 rc = DBGFR3RegPrintf(pUVM, pDbgc->idCpu, szDisAndRegs, sizeof(szDisAndRegs),
1421 "u %04VR{cs}:%08VR{eip} L 0\n"
1422 "eax=%08VR{eax} ebx=%08VR{ebx} ecx=%08VR{ecx} edx=%08VR{edx} esi=%08VR{esi} edi=%08VR{edi}\n"
1423 "eip=%08VR{eip} esp=%08VR{esp} ebp=%08VR{ebp} %VRF{eflags}\n"
1424 "cs=%04VR{cs} ds=%04VR{ds} es=%04VR{es} fs=%04VR{fs} gs=%04VR{gs} ss=%04VR{ss} eflags=%08VR{eflags}\n");
1425 }
1426 else
1427 {
1428 if (f64BitMode)
1429 rc = DBGFR3RegPrintf(pUVM, pDbgc->idCpu, &szDisAndRegs[0], sizeof(szDisAndRegs),
1430 "u %016VR{rip} L 0\n"
1431 "rax=%016VR{rax} rbx=%016VR{rbx} rcx=%016VR{rcx} rdx=%016VR{rdx}\n"
1432 "rsi=%016VR{rsi} rdi=%016VR{rdi} r8 =%016VR{r8} r9 =%016VR{r9}\n"
1433 "r10=%016VR{r10} r11=%016VR{r11} r12=%016VR{r12} r13=%016VR{r13}\n"
1434 "r14=%016VR{r14} r15=%016VR{r15} %VRF{rflags}\n"
1435 "rip=%016VR{rip} rsp=%016VR{rsp} rbp=%016VR{rbp}\n"
1436 "cs={%04VR{cs} base=%016VR{cs_base} limit=%08VR{cs_lim} flags=%04VR{cs_attr}} cr0=%016VR{cr0}\n"
1437 "ds={%04VR{ds} base=%016VR{ds_base} limit=%08VR{ds_lim} flags=%04VR{ds_attr}} cr2=%016VR{cr2}\n"
1438 "es={%04VR{es} base=%016VR{es_base} limit=%08VR{es_lim} flags=%04VR{es_attr}} cr3=%016VR{cr3}\n"
1439 "fs={%04VR{fs} base=%016VR{fs_base} limit=%08VR{fs_lim} flags=%04VR{fs_attr}} cr4=%016VR{cr4}\n"
1440 "gs={%04VR{gs} base=%016VR{gs_base} limit=%08VR{gs_lim} flags=%04VR{gs_attr}} cr8=%016VR{cr8}\n"
1441 "ss={%04VR{ss} base=%016VR{ss_base} limit=%08VR{ss_lim} flags=%04VR{ss_attr}}\n"
1442 "dr0=%016VR{dr0} dr1=%016VR{dr1} dr2=%016VR{dr2} dr3=%016VR{dr3}\n"
1443 "dr6=%016VR{dr6} dr7=%016VR{dr7}\n"
1444 "gdtr=%016VR{gdtr_base}:%04VR{gdtr_lim} idtr=%016VR{idtr_base}:%04VR{idtr_lim} rflags=%08VR{rflags}\n"
1445 "ldtr={%04VR{ldtr} base=%016VR{ldtr_base} limit=%08VR{ldtr_lim} flags=%08VR{ldtr_attr}}\n"
1446 "tr ={%04VR{tr} base=%016VR{tr_base} limit=%08VR{tr_lim} flags=%08VR{tr_attr}}\n"
1447 " sysenter={cs=%04VR{sysenter_cs} eip=%08VR{sysenter_eip} esp=%08VR{sysenter_esp}}\n"
1448 " efer=%016VR{efer}\n"
1449 " pat=%016VR{pat}\n"
1450 " sf_mask=%016VR{sf_mask}\n"
1451 "krnl_gs_base=%016VR{krnl_gs_base}\n"
1452 " lstar=%016VR{lstar}\n"
1453 " star=%016VR{star} cstar=%016VR{cstar}\n"
1454 "fcw=%04VR{fcw} fsw=%04VR{fsw} ftw=%04VR{ftw} mxcsr=%04VR{mxcsr} mxcsr_mask=%04VR{mxcsr_mask}\n"
1455 );
1456 else
1457 rc = DBGFR3RegPrintf(pUVM, pDbgc->idCpu, szDisAndRegs, sizeof(szDisAndRegs),
1458 "u %04VR{cs}:%08VR{eip} L 0\n"
1459 "eax=%08VR{eax} ebx=%08VR{ebx} ecx=%08VR{ecx} edx=%08VR{edx} esi=%08VR{esi} edi=%08VR{edi}\n"
1460 "eip=%08VR{eip} esp=%08VR{esp} ebp=%08VR{ebp} %VRF{eflags}\n"
1461 "cs={%04VR{cs} base=%08VR{cs_base} limit=%08VR{cs_lim} flags=%04VR{cs_attr}} dr0=%08VR{dr0} dr1=%08VR{dr1}\n"
1462 "ds={%04VR{ds} base=%08VR{ds_base} limit=%08VR{ds_lim} flags=%04VR{ds_attr}} dr2=%08VR{dr2} dr3=%08VR{dr3}\n"
1463 "es={%04VR{es} base=%08VR{es_base} limit=%08VR{es_lim} flags=%04VR{es_attr}} dr6=%08VR{dr6} dr7=%08VR{dr7}\n"
1464 "fs={%04VR{fs} base=%08VR{fs_base} limit=%08VR{fs_lim} flags=%04VR{fs_attr}} cr0=%08VR{cr0} cr2=%08VR{cr2}\n"
1465 "gs={%04VR{gs} base=%08VR{gs_base} limit=%08VR{gs_lim} flags=%04VR{gs_attr}} cr3=%08VR{cr3} cr4=%08VR{cr4}\n"
1466 "ss={%04VR{ss} base=%08VR{ss_base} limit=%08VR{ss_lim} flags=%04VR{ss_attr}} cr8=%08VR{cr8}\n"
1467 "gdtr=%08VR{gdtr_base}:%04VR{gdtr_lim} idtr=%08VR{idtr_base}:%04VR{idtr_lim} eflags=%08VR{eflags}\n"
1468 "ldtr={%04VR{ldtr} base=%08VR{ldtr_base} limit=%08VR{ldtr_lim} flags=%04VR{ldtr_attr}}\n"
1469 "tr ={%04VR{tr} base=%08VR{tr_base} limit=%08VR{tr_lim} flags=%04VR{tr_attr}}\n"
1470 "sysenter={cs=%04VR{sysenter_cs} eip=%08VR{sysenter_eip} esp=%08VR{sysenter_esp}}\n"
1471 "fcw=%04VR{fcw} fsw=%04VR{fsw} ftw=%04VR{ftw} mxcsr=%04VR{mxcsr} mxcsr_mask=%04VR{mxcsr_mask}\n"
1472 );
1473 }
1474 if (RT_FAILURE(rc))
1475 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegPrintf failed");
1476 char *pszRegs = strchr(szDisAndRegs, '\n');
1477 *pszRegs++ = '\0';
1478 rc = DBGCCmdHlpPrintf(pCmdHlp, "%s", pszRegs);
1479
1480 /*
1481 * Disassemble one instruction at cs:[r|e]ip.
1482 */
1483 return pCmdHlp->pfnExec(pCmdHlp, "%s", szDisAndRegs);
1484 }
1485 return dbgcCmdRegCommon(pCmd, pCmdHlp, pUVM, paArgs, cArgs, "");
1486}
1487
1488
1489/**
1490 * @interface_method_impl{FNDBCCMD, The 'rh' command.}
1491 */
1492static DECLCALLBACK(int) dbgcCmdRegHyper(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1493{
1494 /*
1495 * Show all registers our selves.
1496 */
1497 if (cArgs == 0)
1498 {
1499 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1500 char szDisAndRegs[8192];
1501 int rc;
1502
1503 if (pDbgc->fRegTerse)
1504 rc = DBGFR3RegPrintf(pUVM, pDbgc->idCpu | DBGFREG_HYPER_VMCPUID, szDisAndRegs, sizeof(szDisAndRegs),
1505 "u %VR{cs}:%VR{eip} L 0\n"
1506 ".eax=%08VR{eax} .ebx=%08VR{ebx} .ecx=%08VR{ecx} .edx=%08VR{edx} .esi=%08VR{esi} .edi=%08VR{edi}\n"
1507 ".eip=%08VR{eip} .esp=%08VR{esp} .ebp=%08VR{ebp} .%VRF{eflags}\n"
1508 ".cs=%04VR{cs} .ds=%04VR{ds} .es=%04VR{es} .fs=%04VR{fs} .gs=%04VR{gs} .ss=%04VR{ss} .eflags=%08VR{eflags}\n");
1509 else
1510 rc = DBGFR3RegPrintf(pUVM, pDbgc->idCpu | DBGFREG_HYPER_VMCPUID, szDisAndRegs, sizeof(szDisAndRegs),
1511 "u %04VR{cs}:%08VR{eip} L 0\n"
1512 ".eax=%08VR{eax} .ebx=%08VR{ebx} .ecx=%08VR{ecx} .edx=%08VR{edx} .esi=%08VR{esi} .edi=%08VR{edi}\n"
1513 ".eip=%08VR{eip} .esp=%08VR{esp} .ebp=%08VR{ebp} .%VRF{eflags}\n"
1514 ".cs={%04VR{cs} base=%08VR{cs_base} limit=%08VR{cs_lim} flags=%04VR{cs_attr}} .dr0=%08VR{dr0} .dr1=%08VR{dr1}\n"
1515 ".ds={%04VR{ds} base=%08VR{ds_base} limit=%08VR{ds_lim} flags=%04VR{ds_attr}} .dr2=%08VR{dr2} .dr3=%08VR{dr3}\n"
1516 ".es={%04VR{es} base=%08VR{es_base} limit=%08VR{es_lim} flags=%04VR{es_attr}} .dr6=%08VR{dr6} .dr6=%08VR{dr6}\n"
1517 ".fs={%04VR{fs} base=%08VR{fs_base} limit=%08VR{fs_lim} flags=%04VR{fs_attr}} .cr3=%016VR{cr3}\n"
1518 ".gs={%04VR{gs} base=%08VR{gs_base} limit=%08VR{gs_lim} flags=%04VR{gs_attr}}\n"
1519 ".ss={%04VR{ss} base=%08VR{ss_base} limit=%08VR{ss_lim} flags=%04VR{ss_attr}}\n"
1520 ".gdtr=%08VR{gdtr_base}:%04VR{gdtr_lim} .idtr=%08VR{idtr_base}:%04VR{idtr_lim} .eflags=%08VR{eflags}\n"
1521 ".ldtr={%04VR{ldtr} base=%08VR{ldtr_base} limit=%08VR{ldtr_lim} flags=%04VR{ldtr_attr}}\n"
1522 ".tr ={%04VR{tr} base=%08VR{tr_base} limit=%08VR{tr_lim} flags=%04VR{tr_attr}}\n"
1523 );
1524 if (RT_FAILURE(rc))
1525 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3RegPrintf failed");
1526 char *pszRegs = strchr(szDisAndRegs, '\n');
1527 *pszRegs++ = '\0';
1528 rc = DBGCCmdHlpPrintf(pCmdHlp, "%s", pszRegs);
1529
1530 /*
1531 * Disassemble one instruction at cs:[r|e]ip.
1532 */
1533 return pCmdHlp->pfnExec(pCmdHlp, "%s", szDisAndRegs);
1534 }
1535 return dbgcCmdRegCommon(pCmd, pCmdHlp, pUVM, paArgs, cArgs, ".");
1536}
1537
1538
1539/**
1540 * @interface_method_impl{FNDBCCMD, The 'rt' command.}
1541 */
1542static DECLCALLBACK(int) dbgcCmdRegTerse(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1543{
1544 NOREF(pCmd); NOREF(pUVM); NOREF(paArgs); NOREF(cArgs);
1545
1546 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1547 pDbgc->fRegTerse = !pDbgc->fRegTerse;
1548 return DBGCCmdHlpPrintf(pCmdHlp, pDbgc->fRegTerse ? "info: Terse register info.\n" : "info: Verbose register info.\n");
1549}
1550
1551
1552/**
1553 * @interface_method_impl{FNDBCCMD, The 't' command.}
1554 */
1555static DECLCALLBACK(int) dbgcCmdTrace(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1556{
1557 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1558
1559 int rc = DBGFR3Step(pUVM, pDbgc->idCpu);
1560 if (RT_SUCCESS(rc))
1561 pDbgc->fReady = false;
1562 else
1563 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to single step VM %p\n", pDbgc->pVM);
1564
1565 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
1566 return rc;
1567}
1568
1569
1570/**
1571 * @interface_method_impl{FNDBCCMD, The 'k', 'kg' and 'kh' commands.}
1572 */
1573static DECLCALLBACK(int) dbgcCmdStack(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1574{
1575 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1576
1577 /*
1578 * Figure which context we're called for and start walking that stack.
1579 */
1580 int rc;
1581 PCDBGFSTACKFRAME pFirstFrame;
1582 bool const fGuest = pCmd->pszCmd[1] == 'g'
1583 || (!pCmd->pszCmd[1] && pDbgc->fRegCtxGuest);
1584 rc = DBGFR3StackWalkBegin(pUVM, pDbgc->idCpu, fGuest ? DBGFCODETYPE_GUEST : DBGFCODETYPE_HYPER, &pFirstFrame);
1585 if (RT_FAILURE(rc))
1586 return DBGCCmdHlpPrintf(pCmdHlp, "Failed to begin stack walk, rc=%Rrc\n", rc);
1587
1588 /*
1589 * Print header.
1590 * 12345678 12345678 0023:87654321 12345678 87654321 12345678 87654321 symbol
1591 */
1592 uint32_t fBitFlags = 0;
1593 for (PCDBGFSTACKFRAME pFrame = pFirstFrame;
1594 pFrame;
1595 pFrame = DBGFR3StackWalkNext(pFrame))
1596 {
1597 uint32_t const fCurBitFlags = pFrame->fFlags & (DBGFSTACKFRAME_FLAGS_16BIT | DBGFSTACKFRAME_FLAGS_32BIT | DBGFSTACKFRAME_FLAGS_64BIT);
1598 if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_16BIT)
1599 {
1600 if (fCurBitFlags != fBitFlags)
1601 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "SS:BP Ret SS:BP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1602 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04RX16:%04RX16 %04RX16:%04RX16 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1603 pFrame->AddrFrame.Sel,
1604 (uint16_t)pFrame->AddrFrame.off,
1605 pFrame->AddrReturnFrame.Sel,
1606 (uint16_t)pFrame->AddrReturnFrame.off,
1607 (uint32_t)pFrame->AddrReturnPC.Sel,
1608 (uint32_t)pFrame->AddrReturnPC.off,
1609 pFrame->Args.au32[0],
1610 pFrame->Args.au32[1],
1611 pFrame->Args.au32[2],
1612 pFrame->Args.au32[3]);
1613 }
1614 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT)
1615 {
1616 if (fCurBitFlags != fBitFlags)
1617 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "EBP Ret EBP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1618 rc = DBGCCmdHlpPrintf(pCmdHlp, "%08RX32 %08RX32 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1619 (uint32_t)pFrame->AddrFrame.off,
1620 (uint32_t)pFrame->AddrReturnFrame.off,
1621 (uint32_t)pFrame->AddrReturnPC.Sel,
1622 (uint32_t)pFrame->AddrReturnPC.off,
1623 pFrame->Args.au32[0],
1624 pFrame->Args.au32[1],
1625 pFrame->Args.au32[2],
1626 pFrame->Args.au32[3]);
1627 }
1628 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT)
1629 {
1630 if (fCurBitFlags != fBitFlags)
1631 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "RBP Ret SS:RBP Ret RIP CS:RIP / Symbol [line]\n");
1632 rc = DBGCCmdHlpPrintf(pCmdHlp, "%016RX64 %04RX16:%016RX64 %016RX64",
1633 (uint64_t)pFrame->AddrFrame.off,
1634 pFrame->AddrReturnFrame.Sel,
1635 (uint64_t)pFrame->AddrReturnFrame.off,
1636 (uint64_t)pFrame->AddrReturnPC.off);
1637 }
1638 if (RT_FAILURE(rc))
1639 break;
1640 if (!pFrame->pSymPC)
1641 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1642 fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT
1643 ? " %RTsel:%016RGv"
1644 : fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT
1645 ? " %RTsel:%08RGv"
1646 : " %RTsel:%04RGv"
1647 , pFrame->AddrPC.Sel, pFrame->AddrPC.off);
1648 else
1649 {
1650 RTGCINTPTR offDisp = pFrame->AddrPC.FlatPtr - pFrame->pSymPC->Value; /** @todo this isn't 100% correct for segmented stuff. */
1651 if (offDisp > 0)
1652 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s+%llx", pFrame->pSymPC->szName, (int64_t)offDisp);
1653 else if (offDisp < 0)
1654 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s-%llx", pFrame->pSymPC->szName, -(int64_t)offDisp);
1655 else
1656 rc = DBGCCmdHlpPrintf(pCmdHlp, " %s", pFrame->pSymPC->szName);
1657 }
1658 if (RT_SUCCESS(rc) && pFrame->pLinePC)
1659 rc = DBGCCmdHlpPrintf(pCmdHlp, " [%s @ 0i%d]", pFrame->pLinePC->szFilename, pFrame->pLinePC->uLineNo);
1660 if (RT_SUCCESS(rc))
1661 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
1662 if (RT_FAILURE(rc))
1663 break;
1664
1665 fBitFlags = fCurBitFlags;
1666 }
1667
1668 DBGFR3StackWalkEnd(pFirstFrame);
1669
1670 NOREF(paArgs); NOREF(cArgs);
1671 return rc;
1672}
1673
1674
1675static int dbgcCmdDumpDTWorker64(PDBGCCMDHLP pCmdHlp, PCX86DESC64 pDesc, unsigned iEntry, bool fHyper, bool *pfDblEntry)
1676{
1677 /* GUEST64 */
1678 int rc;
1679
1680 const char *pszHyper = fHyper ? " HYPER" : "";
1681 const char *pszPresent = pDesc->Gen.u1Present ? "P " : "NP";
1682 if (pDesc->Gen.u1DescType)
1683 {
1684 static const char * const s_apszTypes[] =
1685 {
1686 "DataRO", /* 0 Read-Only */
1687 "DataRO", /* 1 Read-Only - Accessed */
1688 "DataRW", /* 2 Read/Write */
1689 "DataRW", /* 3 Read/Write - Accessed */
1690 "DownRO", /* 4 Expand-down, Read-Only */
1691 "DownRO", /* 5 Expand-down, Read-Only - Accessed */
1692 "DownRW", /* 6 Expand-down, Read/Write */
1693 "DownRW", /* 7 Expand-down, Read/Write - Accessed */
1694 "CodeEO", /* 8 Execute-Only */
1695 "CodeEO", /* 9 Execute-Only - Accessed */
1696 "CodeER", /* A Execute/Readable */
1697 "CodeER", /* B Execute/Readable - Accessed */
1698 "ConfE0", /* C Conforming, Execute-Only */
1699 "ConfE0", /* D Conforming, Execute-Only - Accessed */
1700 "ConfER", /* E Conforming, Execute/Readable */
1701 "ConfER" /* F Conforming, Execute/Readable - Accessed */
1702 };
1703 const char *pszAccessed = pDesc->Gen.u4Type & RT_BIT(0) ? "A " : "NA";
1704 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
1705 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1706 uint32_t u32Base = X86DESC_BASE(pDesc);
1707 uint32_t cbLimit = X86DESC_LIMIT_G(pDesc);
1708
1709 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d L=%d%s\n",
1710 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1711 pDesc->Gen.u2Dpl, pszPresent, pszAccessed, pszGranularity, pszBig,
1712 pDesc->Gen.u1Available, pDesc->Gen.u1Long, pszHyper);
1713 }
1714 else
1715 {
1716 static const char * const s_apszTypes[] =
1717 {
1718 "Ill-0 ", /* 0 0000 Reserved (Illegal) */
1719 "Ill-1 ", /* 1 0001 Available 16-bit TSS */
1720 "LDT ", /* 2 0010 LDT */
1721 "Ill-3 ", /* 3 0011 Busy 16-bit TSS */
1722 "Ill-4 ", /* 4 0100 16-bit Call Gate */
1723 "Ill-5 ", /* 5 0101 Task Gate */
1724 "Ill-6 ", /* 6 0110 16-bit Interrupt Gate */
1725 "Ill-7 ", /* 7 0111 16-bit Trap Gate */
1726 "Ill-8 ", /* 8 1000 Reserved (Illegal) */
1727 "Tss64A", /* 9 1001 Available 32-bit TSS */
1728 "Ill-A ", /* A 1010 Reserved (Illegal) */
1729 "Tss64B", /* B 1011 Busy 32-bit TSS */
1730 "Call64", /* C 1100 32-bit Call Gate */
1731 "Ill-D ", /* D 1101 Reserved (Illegal) */
1732 "Int64 ", /* E 1110 32-bit Interrupt Gate */
1733 "Trap64" /* F 1111 32-bit Trap Gate */
1734 };
1735 switch (pDesc->Gen.u4Type)
1736 {
1737 /* raw */
1738 case X86_SEL_TYPE_SYS_UNDEFINED:
1739 case X86_SEL_TYPE_SYS_UNDEFINED2:
1740 case X86_SEL_TYPE_SYS_UNDEFINED4:
1741 case X86_SEL_TYPE_SYS_UNDEFINED3:
1742 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
1743 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
1744 case X86_SEL_TYPE_SYS_286_CALL_GATE:
1745 case X86_SEL_TYPE_SYS_286_INT_GATE:
1746 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
1747 case X86_SEL_TYPE_SYS_TASK_GATE:
1748 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s %.8Rhxs DPL=%d %s%s\n",
1749 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc,
1750 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1751 break;
1752
1753 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
1754 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
1755 case X86_SEL_TYPE_SYS_LDT:
1756 {
1757 const char *pszBusy = pDesc->Gen.u4Type & RT_BIT(1) ? "B " : "NB";
1758 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1759 const char *pszLong = pDesc->Gen.u1Long ? "LONG" : " ";
1760
1761 uint64_t u32Base = X86DESC64_BASE(pDesc);
1762 uint32_t cbLimit = X86DESC_LIMIT_G(pDesc);
1763
1764 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%016RX64 Lim=%08x DPL=%d %s %s %s %sAVL=%d R=%d%s\n",
1765 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1766 pDesc->Gen.u2Dpl, pszPresent, pszBusy, pszLong, pszBig,
1767 pDesc->Gen.u1Available, pDesc->Gen.u1Long | (pDesc->Gen.u1DefBig << 1),
1768 pszHyper);
1769 if (pfDblEntry)
1770 *pfDblEntry = true;
1771 break;
1772 }
1773
1774 case X86_SEL_TYPE_SYS_386_CALL_GATE:
1775 {
1776 unsigned cParams = pDesc->au8[4] & 0x1f;
1777 const char *pszCountOf = pDesc->Gen.u4Type & RT_BIT(3) ? "DC" : "WC";
1778 RTSEL sel = pDesc->au16[1];
1779 uint64_t off = pDesc->au16[0]
1780 | ((uint64_t)pDesc->au16[3] << 16)
1781 | ((uint64_t)pDesc->Gen.u32BaseHigh3 << 32);
1782 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%016RX64 DPL=%d %s %s=%d%s\n",
1783 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1784 pDesc->Gen.u2Dpl, pszPresent, pszCountOf, cParams, pszHyper);
1785 if (pfDblEntry)
1786 *pfDblEntry = true;
1787 break;
1788 }
1789
1790 case X86_SEL_TYPE_SYS_386_INT_GATE:
1791 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
1792 {
1793 RTSEL sel = pDesc->au16[1];
1794 uint64_t off = pDesc->au16[0]
1795 | ((uint64_t)pDesc->au16[3] << 16)
1796 | ((uint64_t)pDesc->Gen.u32BaseHigh3 << 32);
1797 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%016RX64 DPL=%d %s%s\n",
1798 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1799 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1800 if (pfDblEntry)
1801 *pfDblEntry = true;
1802 break;
1803 }
1804
1805 /* impossible, just it's necessary to keep gcc happy. */
1806 default:
1807 return VINF_SUCCESS;
1808 }
1809 }
1810 return VINF_SUCCESS;
1811}
1812
1813
1814/**
1815 * Worker function that displays one descriptor entry (GDT, LDT, IDT).
1816 *
1817 * @returns pfnPrintf status code.
1818 * @param pCmdHlp The DBGC command helpers.
1819 * @param pDesc The descriptor to display.
1820 * @param iEntry The descriptor entry number.
1821 * @param fHyper Whether the selector belongs to the hypervisor or not.
1822 */
1823static int dbgcCmdDumpDTWorker32(PDBGCCMDHLP pCmdHlp, PCX86DESC pDesc, unsigned iEntry, bool fHyper)
1824{
1825 int rc;
1826
1827 const char *pszHyper = fHyper ? " HYPER" : "";
1828 const char *pszPresent = pDesc->Gen.u1Present ? "P " : "NP";
1829 if (pDesc->Gen.u1DescType)
1830 {
1831 static const char * const s_apszTypes[] =
1832 {
1833 "DataRO", /* 0 Read-Only */
1834 "DataRO", /* 1 Read-Only - Accessed */
1835 "DataRW", /* 2 Read/Write */
1836 "DataRW", /* 3 Read/Write - Accessed */
1837 "DownRO", /* 4 Expand-down, Read-Only */
1838 "DownRO", /* 5 Expand-down, Read-Only - Accessed */
1839 "DownRW", /* 6 Expand-down, Read/Write */
1840 "DownRW", /* 7 Expand-down, Read/Write - Accessed */
1841 "CodeEO", /* 8 Execute-Only */
1842 "CodeEO", /* 9 Execute-Only - Accessed */
1843 "CodeER", /* A Execute/Readable */
1844 "CodeER", /* B Execute/Readable - Accessed */
1845 "ConfE0", /* C Conforming, Execute-Only */
1846 "ConfE0", /* D Conforming, Execute-Only - Accessed */
1847 "ConfER", /* E Conforming, Execute/Readable */
1848 "ConfER" /* F Conforming, Execute/Readable - Accessed */
1849 };
1850 const char *pszAccessed = pDesc->Gen.u4Type & RT_BIT(0) ? "A " : "NA";
1851 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
1852 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1853 uint32_t u32Base = pDesc->Gen.u16BaseLow
1854 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
1855 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
1856 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1857 if (pDesc->Gen.u1Granularity)
1858 cbLimit <<= PAGE_SHIFT;
1859
1860 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d L=%d%s\n",
1861 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1862 pDesc->Gen.u2Dpl, pszPresent, pszAccessed, pszGranularity, pszBig,
1863 pDesc->Gen.u1Available, pDesc->Gen.u1Long, pszHyper);
1864 }
1865 else
1866 {
1867 static const char * const s_apszTypes[] =
1868 {
1869 "Ill-0 ", /* 0 0000 Reserved (Illegal) */
1870 "Tss16A", /* 1 0001 Available 16-bit TSS */
1871 "LDT ", /* 2 0010 LDT */
1872 "Tss16B", /* 3 0011 Busy 16-bit TSS */
1873 "Call16", /* 4 0100 16-bit Call Gate */
1874 "TaskG ", /* 5 0101 Task Gate */
1875 "Int16 ", /* 6 0110 16-bit Interrupt Gate */
1876 "Trap16", /* 7 0111 16-bit Trap Gate */
1877 "Ill-8 ", /* 8 1000 Reserved (Illegal) */
1878 "Tss32A", /* 9 1001 Available 32-bit TSS */
1879 "Ill-A ", /* A 1010 Reserved (Illegal) */
1880 "Tss32B", /* B 1011 Busy 32-bit TSS */
1881 "Call32", /* C 1100 32-bit Call Gate */
1882 "Ill-D ", /* D 1101 Reserved (Illegal) */
1883 "Int32 ", /* E 1110 32-bit Interrupt Gate */
1884 "Trap32" /* F 1111 32-bit Trap Gate */
1885 };
1886 switch (pDesc->Gen.u4Type)
1887 {
1888 /* raw */
1889 case X86_SEL_TYPE_SYS_UNDEFINED:
1890 case X86_SEL_TYPE_SYS_UNDEFINED2:
1891 case X86_SEL_TYPE_SYS_UNDEFINED4:
1892 case X86_SEL_TYPE_SYS_UNDEFINED3:
1893 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s %.8Rhxs DPL=%d %s%s\n",
1894 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc,
1895 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1896 break;
1897
1898 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
1899 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
1900 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
1901 case X86_SEL_TYPE_SYS_386_TSS_BUSY:
1902 case X86_SEL_TYPE_SYS_LDT:
1903 {
1904 const char *pszGranularity = pDesc->Gen.u1Granularity ? "G" : " ";
1905 const char *pszBusy = pDesc->Gen.u4Type & RT_BIT(1) ? "B " : "NB";
1906 const char *pszBig = pDesc->Gen.u1DefBig ? "BIG" : " ";
1907 uint32_t u32Base = pDesc->Gen.u16BaseLow
1908 | ((uint32_t)pDesc->Gen.u8BaseHigh1 << 16)
1909 | ((uint32_t)pDesc->Gen.u8BaseHigh2 << 24);
1910 uint32_t cbLimit = pDesc->Gen.u16LimitLow | (pDesc->Gen.u4LimitHigh << 16);
1911 if (pDesc->Gen.u1Granularity)
1912 cbLimit <<= PAGE_SHIFT;
1913
1914 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Bas=%08x Lim=%08x DPL=%d %s %s %s %s AVL=%d R=%d%s\n",
1915 iEntry, s_apszTypes[pDesc->Gen.u4Type], u32Base, cbLimit,
1916 pDesc->Gen.u2Dpl, pszPresent, pszBusy, pszGranularity, pszBig,
1917 pDesc->Gen.u1Available, pDesc->Gen.u1Long | (pDesc->Gen.u1DefBig << 1),
1918 pszHyper);
1919 break;
1920 }
1921
1922 case X86_SEL_TYPE_SYS_TASK_GATE:
1923 {
1924 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s TSS=%04x DPL=%d %s%s\n",
1925 iEntry, s_apszTypes[pDesc->Gen.u4Type], pDesc->au16[1],
1926 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1927 break;
1928 }
1929
1930 case X86_SEL_TYPE_SYS_286_CALL_GATE:
1931 case X86_SEL_TYPE_SYS_386_CALL_GATE:
1932 {
1933 unsigned cParams = pDesc->au8[4] & 0x1f;
1934 const char *pszCountOf = pDesc->Gen.u4Type & RT_BIT(3) ? "DC" : "WC";
1935 RTSEL sel = pDesc->au16[1];
1936 uint32_t off = pDesc->au16[0] | ((uint32_t)pDesc->au16[3] << 16);
1937 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%08x DPL=%d %s %s=%d%s\n",
1938 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1939 pDesc->Gen.u2Dpl, pszPresent, pszCountOf, cParams, pszHyper);
1940 break;
1941 }
1942
1943 case X86_SEL_TYPE_SYS_286_INT_GATE:
1944 case X86_SEL_TYPE_SYS_386_INT_GATE:
1945 case X86_SEL_TYPE_SYS_286_TRAP_GATE:
1946 case X86_SEL_TYPE_SYS_386_TRAP_GATE:
1947 {
1948 RTSEL sel = pDesc->au16[1];
1949 uint32_t off = pDesc->au16[0] | ((uint32_t)pDesc->au16[3] << 16);
1950 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %s Sel:Off=%04x:%08x DPL=%d %s%s\n",
1951 iEntry, s_apszTypes[pDesc->Gen.u4Type], sel, off,
1952 pDesc->Gen.u2Dpl, pszPresent, pszHyper);
1953 break;
1954 }
1955
1956 /* impossible, just it's necessary to keep gcc happy. */
1957 default:
1958 return VINF_SUCCESS;
1959 }
1960 }
1961 return rc;
1962}
1963
1964
1965/**
1966 * @interface_method_impl{FNDBCCMD, The 'dg', 'dga', 'dl' and 'dla' commands.}
1967 */
1968static DECLCALLBACK(int) dbgcCmdDumpDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1969{
1970 /*
1971 * Validate input.
1972 */
1973 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1974
1975 /*
1976 * Get the CPU mode, check which command variation this is
1977 * and fix a default parameter if needed.
1978 */
1979 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1980 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
1981 CPUMMODE enmMode = CPUMGetGuestMode(pVCpu);
1982 bool fGdt = pCmd->pszCmd[1] == 'g';
1983 bool fAll = pCmd->pszCmd[2] == 'a';
1984 RTSEL SelTable = fGdt ? 0 : X86_SEL_LDT;
1985
1986 DBGCVAR Var;
1987 if (!cArgs)
1988 {
1989 cArgs = 1;
1990 paArgs = &Var;
1991 Var.enmType = DBGCVAR_TYPE_NUMBER;
1992 Var.u.u64Number = 0;
1993 Var.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
1994 Var.u64Range = 1024;
1995 }
1996
1997 /*
1998 * Process the arguments.
1999 */
2000 for (unsigned i = 0; i < cArgs; i++)
2001 {
2002 /*
2003 * Retrieve the selector value from the argument.
2004 * The parser may confuse pointers and numbers if more than one
2005 * argument is given, that that into account.
2006 */
2007 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, i, paArgs[i].enmType == DBGCVAR_TYPE_NUMBER || DBGCVAR_ISPOINTER(paArgs[i].enmType));
2008 uint64_t u64;
2009 unsigned cSels = 1;
2010 switch (paArgs[i].enmType)
2011 {
2012 case DBGCVAR_TYPE_NUMBER:
2013 u64 = paArgs[i].u.u64Number;
2014 if (paArgs[i].enmRangeType != DBGCVAR_RANGE_NONE)
2015 cSels = RT_MIN(paArgs[i].u64Range, 1024);
2016 break;
2017 case DBGCVAR_TYPE_GC_FAR: u64 = paArgs[i].u.GCFar.sel; break;
2018 case DBGCVAR_TYPE_GC_FLAT: u64 = paArgs[i].u.GCFlat; break;
2019 case DBGCVAR_TYPE_GC_PHYS: u64 = paArgs[i].u.GCPhys; break;
2020 case DBGCVAR_TYPE_HC_FLAT: u64 = (uintptr_t)paArgs[i].u.pvHCFlat; break;
2021 case DBGCVAR_TYPE_HC_PHYS: u64 = paArgs[i].u.HCPhys; break;
2022 default: u64 = _64K; break;
2023 }
2024 if (u64 < _64K)
2025 {
2026 unsigned Sel = (RTSEL)u64;
2027
2028 /*
2029 * Dump the specified range.
2030 */
2031 bool fSingle = cSels == 1;
2032 while ( cSels-- > 0
2033 && Sel < _64K)
2034 {
2035 DBGFSELINFO SelInfo;
2036 int rc = DBGFR3SelQueryInfo(pUVM, pDbgc->idCpu, Sel | SelTable, DBGFSELQI_FLAGS_DT_GUEST, &SelInfo);
2037 if (RT_SUCCESS(rc))
2038 {
2039 if (SelInfo.fFlags & DBGFSELINFO_FLAGS_REAL_MODE)
2040 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x RealM Bas=%04x Lim=%04x\n",
2041 Sel, (unsigned)SelInfo.GCPtrBase, (unsigned)SelInfo.cbLimit);
2042 else if ( fAll
2043 || fSingle
2044 || SelInfo.u.Raw.Gen.u1Present)
2045 {
2046 if (enmMode == CPUMMODE_PROTECTED)
2047 rc = dbgcCmdDumpDTWorker32(pCmdHlp, &SelInfo.u.Raw, Sel, !!(SelInfo.fFlags & DBGFSELINFO_FLAGS_HYPER));
2048 else
2049 {
2050 bool fDblSkip = false;
2051 rc = dbgcCmdDumpDTWorker64(pCmdHlp, &SelInfo.u.Raw64, Sel, !!(SelInfo.fFlags & DBGFSELINFO_FLAGS_HYPER), &fDblSkip);
2052 if (fDblSkip)
2053 Sel += 4;
2054 }
2055 }
2056 }
2057 else
2058 {
2059 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %Rrc\n", Sel, rc);
2060 if (!fAll)
2061 return rc;
2062 }
2063 if (RT_FAILURE(rc))
2064 return rc;
2065
2066 /* next */
2067 Sel += 8;
2068 }
2069 }
2070 else
2071 DBGCCmdHlpPrintf(pCmdHlp, "error: %llx is out of bounds\n", u64);
2072 }
2073
2074 return VINF_SUCCESS;
2075}
2076
2077
2078/**
2079 * @interface_method_impl{FNDBCCMD, The 'di' and 'dia' commands.}
2080 */
2081static DECLCALLBACK(int) dbgcCmdDumpIDT(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2082{
2083 /*
2084 * Validate input.
2085 */
2086 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2087
2088 /*
2089 * Establish some stuff like the current IDTR and CPU mode,
2090 * and fix a default parameter.
2091 */
2092 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2093 PVMCPU pVCpu = VMMR3GetCpuByIdU(pUVM, pDbgc->idCpu);
2094 uint16_t cbLimit;
2095 RTGCUINTPTR GCPtrBase = CPUMGetGuestIDTR(pVCpu, &cbLimit);
2096 CPUMMODE enmMode = CPUMGetGuestMode(pVCpu);
2097 unsigned cbEntry;
2098 switch (enmMode)
2099 {
2100 case CPUMMODE_REAL: cbEntry = sizeof(RTFAR16); break;
2101 case CPUMMODE_PROTECTED: cbEntry = sizeof(X86DESC); break;
2102 case CPUMMODE_LONG: cbEntry = sizeof(X86DESC64); break;
2103 default:
2104 return DBGCCmdHlpPrintf(pCmdHlp, "error: Invalid CPU mode %d.\n", enmMode);
2105 }
2106
2107 bool fAll = pCmd->pszCmd[2] == 'a';
2108 DBGCVAR Var;
2109 if (!cArgs)
2110 {
2111 cArgs = 1;
2112 paArgs = &Var;
2113 Var.enmType = DBGCVAR_TYPE_NUMBER;
2114 Var.u.u64Number = 0;
2115 Var.enmRangeType = DBGCVAR_RANGE_ELEMENTS;
2116 Var.u64Range = 256;
2117 }
2118
2119 /*
2120 * Process the arguments.
2121 */
2122 for (unsigned i = 0; i < cArgs; i++)
2123 {
2124 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, i, paArgs[i].enmType == DBGCVAR_TYPE_NUMBER);
2125 if (paArgs[i].u.u64Number < 256)
2126 {
2127 RTGCUINTPTR iInt = (RTGCUINTPTR)paArgs[i].u.u64Number;
2128 unsigned cInts = paArgs[i].enmRangeType != DBGCVAR_RANGE_NONE
2129 ? paArgs[i].u64Range
2130 : 1;
2131 bool fSingle = cInts == 1;
2132 while ( cInts-- > 0
2133 && iInt < 256)
2134 {
2135 /*
2136 * Try read it.
2137 */
2138 union
2139 {
2140 RTFAR16 Real;
2141 X86DESC Prot;
2142 X86DESC64 Long;
2143 } u;
2144 if (iInt * cbEntry + (cbEntry - 1) > cbLimit)
2145 {
2146 DBGCCmdHlpPrintf(pCmdHlp, "%04x not within the IDT\n", (unsigned)iInt);
2147 if (!fAll && !fSingle)
2148 return VINF_SUCCESS;
2149 }
2150 DBGCVAR AddrVar;
2151 AddrVar.enmType = DBGCVAR_TYPE_GC_FLAT;
2152 AddrVar.u.GCFlat = GCPtrBase + iInt * cbEntry;
2153 AddrVar.enmRangeType = DBGCVAR_RANGE_NONE;
2154 int rc = pCmdHlp->pfnMemRead(pCmdHlp, &u, cbEntry, &AddrVar, NULL);
2155 if (RT_FAILURE(rc))
2156 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading IDT entry %#04x.\n", (unsigned)iInt);
2157
2158 /*
2159 * Display it.
2160 */
2161 switch (enmMode)
2162 {
2163 case CPUMMODE_REAL:
2164 rc = DBGCCmdHlpPrintf(pCmdHlp, "%04x %RTfp16\n", (unsigned)iInt, u.Real);
2165 /** @todo resolve 16:16 IDTE to a symbol */
2166 break;
2167 case CPUMMODE_PROTECTED:
2168 if (fAll || fSingle || u.Prot.Gen.u1Present)
2169 rc = dbgcCmdDumpDTWorker32(pCmdHlp, &u.Prot, iInt, false);
2170 break;
2171 case CPUMMODE_LONG:
2172 if (fAll || fSingle || u.Long.Gen.u1Present)
2173 rc = dbgcCmdDumpDTWorker64(pCmdHlp, &u.Long, iInt, false, NULL);
2174 break;
2175 default: break; /* to shut up gcc */
2176 }
2177 if (RT_FAILURE(rc))
2178 return rc;
2179
2180 /* next */
2181 iInt++;
2182 }
2183 }
2184 else
2185 DBGCCmdHlpPrintf(pCmdHlp, "error: %llx is out of bounds (max 256)\n", paArgs[i].u.u64Number);
2186 }
2187
2188 return VINF_SUCCESS;
2189}
2190
2191
2192/**
2193 * @interface_method_impl{FNDBCCMD, The 'da', 'dq', 'dd', 'dw' and 'db'
2194 * commands.}
2195 */
2196static DECLCALLBACK(int) dbgcCmdDumpMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2197{
2198 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2199
2200 /*
2201 * Validate input.
2202 */
2203 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
2204 if (cArgs == 1)
2205 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
2206 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2207
2208 /*
2209 * Figure out the element size.
2210 */
2211 unsigned cbElement;
2212 bool fAscii = false;
2213 switch (pCmd->pszCmd[1])
2214 {
2215 default:
2216 case 'b': cbElement = 1; break;
2217 case 'w': cbElement = 2; break;
2218 case 'd': cbElement = 4; break;
2219 case 'q': cbElement = 8; break;
2220 case 'a':
2221 cbElement = 1;
2222 fAscii = true;
2223 break;
2224 case '\0':
2225 fAscii = !!(pDbgc->cbDumpElement & 0x80000000);
2226 cbElement = pDbgc->cbDumpElement & 0x7fffffff;
2227 if (!cbElement)
2228 cbElement = 1;
2229 break;
2230 }
2231
2232 /*
2233 * Find address.
2234 */
2235 if (!cArgs)
2236 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_NONE;
2237 else
2238 pDbgc->DumpPos = paArgs[0];
2239
2240 /*
2241 * Range.
2242 */
2243 switch (pDbgc->DumpPos.enmRangeType)
2244 {
2245 case DBGCVAR_RANGE_NONE:
2246 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_BYTES;
2247 pDbgc->DumpPos.u64Range = 0x60;
2248 break;
2249
2250 case DBGCVAR_RANGE_ELEMENTS:
2251 if (pDbgc->DumpPos.u64Range > 2048)
2252 return DBGCCmdHlpPrintf(pCmdHlp, "error: Too many elements requested. Max is 2048 elements.\n");
2253 pDbgc->DumpPos.enmRangeType = DBGCVAR_RANGE_BYTES;
2254 pDbgc->DumpPos.u64Range = (cbElement ? cbElement : 1) * pDbgc->DumpPos.u64Range;
2255 break;
2256
2257 case DBGCVAR_RANGE_BYTES:
2258 if (pDbgc->DumpPos.u64Range > 65536)
2259 return DBGCCmdHlpPrintf(pCmdHlp, "error: The requested range is too big. Max is 64KB.\n");
2260 break;
2261
2262 default:
2263 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: Unknown range type %d.\n", pDbgc->DumpPos.enmRangeType);
2264 }
2265
2266 pDbgc->pLastPos = &pDbgc->DumpPos;
2267
2268 /*
2269 * Do the dumping.
2270 */
2271 pDbgc->cbDumpElement = cbElement | (fAscii << 31);
2272 int cbLeft = (int)pDbgc->DumpPos.u64Range;
2273 uint8_t u8Prev = '\0';
2274 for (;;)
2275 {
2276 /*
2277 * Read memory.
2278 */
2279 char achBuffer[16];
2280 size_t cbReq = RT_MIN((int)sizeof(achBuffer), cbLeft);
2281 size_t cb = RT_MIN((int)sizeof(achBuffer), cbLeft);
2282 int rc = pCmdHlp->pfnMemRead(pCmdHlp, &achBuffer, cbReq, &pDbgc->DumpPos, &cb);
2283 if (RT_FAILURE(rc))
2284 {
2285 if (u8Prev && u8Prev != '\n')
2286 DBGCCmdHlpPrintf(pCmdHlp, "\n");
2287 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading memory at %DV.\n", &pDbgc->DumpPos);
2288 }
2289
2290 /*
2291 * Display it.
2292 */
2293 memset(&achBuffer[cb], 0, sizeof(achBuffer) - cb);
2294 if (!fAscii)
2295 {
2296 DBGCCmdHlpPrintf(pCmdHlp, "%DV:", &pDbgc->DumpPos);
2297 unsigned i;
2298 for (i = 0; i < cb; i += cbElement)
2299 {
2300 const char *pszSpace = " ";
2301 if (cbElement <= 2 && i == 8 && !fAscii)
2302 pszSpace = "-";
2303 switch (cbElement)
2304 {
2305 case 1: DBGCCmdHlpPrintf(pCmdHlp, "%s%02x", pszSpace, *(uint8_t *)&achBuffer[i]); break;
2306 case 2: DBGCCmdHlpPrintf(pCmdHlp, "%s%04x", pszSpace, *(uint16_t *)&achBuffer[i]); break;
2307 case 4: DBGCCmdHlpPrintf(pCmdHlp, "%s%08x", pszSpace, *(uint32_t *)&achBuffer[i]); break;
2308 case 8: DBGCCmdHlpPrintf(pCmdHlp, "%s%016llx", pszSpace, *(uint64_t *)&achBuffer[i]); break;
2309 }
2310 }
2311
2312 /* chars column */
2313 if (pDbgc->cbDumpElement == 1)
2314 {
2315 while (i++ < sizeof(achBuffer))
2316 DBGCCmdHlpPrintf(pCmdHlp, " ");
2317 DBGCCmdHlpPrintf(pCmdHlp, " ");
2318 for (i = 0; i < cb; i += cbElement)
2319 {
2320 uint8_t u8 = *(uint8_t *)&achBuffer[i];
2321 if (RT_C_IS_PRINT(u8) && u8 < 127 && u8 >= 32)
2322 DBGCCmdHlpPrintf(pCmdHlp, "%c", u8);
2323 else
2324 DBGCCmdHlpPrintf(pCmdHlp, ".");
2325 }
2326 }
2327 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
2328 }
2329 else
2330 {
2331 /*
2332 * We print up to the first zero and stop there.
2333 * Only printables + '\t' and '\n' are printed.
2334 */
2335 if (!u8Prev)
2336 DBGCCmdHlpPrintf(pCmdHlp, "%DV:\n", &pDbgc->DumpPos);
2337 uint8_t u8 = '\0';
2338 unsigned i;
2339 for (i = 0; i < cb; i++)
2340 {
2341 u8Prev = u8;
2342 u8 = *(uint8_t *)&achBuffer[i];
2343 if ( u8 < 127
2344 && ( (RT_C_IS_PRINT(u8) && u8 >= 32)
2345 || u8 == '\t'
2346 || u8 == '\n'))
2347 DBGCCmdHlpPrintf(pCmdHlp, "%c", u8);
2348 else if (!u8)
2349 break;
2350 else
2351 DBGCCmdHlpPrintf(pCmdHlp, "\\x%x", u8);
2352 }
2353 if (u8 == '\0')
2354 cb = cbLeft = i + 1;
2355 if (cbLeft - cb <= 0 && u8Prev != '\n')
2356 DBGCCmdHlpPrintf(pCmdHlp, "\n");
2357 }
2358
2359 /*
2360 * Advance
2361 */
2362 cbLeft -= (int)cb;
2363 rc = DBGCCmdHlpEval(pCmdHlp, &pDbgc->DumpPos, "(%Dv) + %x", &pDbgc->DumpPos, cb);
2364 if (RT_FAILURE(rc))
2365 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Expression: (%Dv) + %x\n", &pDbgc->DumpPos, cb);
2366 if (cbLeft <= 0)
2367 break;
2368 }
2369
2370 NOREF(pCmd);
2371 return VINF_SUCCESS;
2372}
2373
2374
2375/**
2376 * Best guess at which paging mode currently applies to the guest
2377 * paging structures.
2378 *
2379 * This have to come up with a decent answer even when the guest
2380 * is in non-paged protected mode or real mode.
2381 *
2382 * @returns cr3.
2383 * @param pDbgc The DBGC instance.
2384 * @param pfPAE Where to store the page address extension indicator.
2385 * @param pfLME Where to store the long mode enabled indicator.
2386 * @param pfPSE Where to store the page size extension indicator.
2387 * @param pfPGE Where to store the page global enabled indicator.
2388 * @param pfNXE Where to store the no-execution enabled indicator.
2389 */
2390static RTGCPHYS dbgcGetGuestPageMode(PDBGC pDbgc, bool *pfPAE, bool *pfLME, bool *pfPSE, bool *pfPGE, bool *pfNXE)
2391{
2392 PVMCPU pVCpu = VMMR3GetCpuByIdU(pDbgc->pUVM, pDbgc->idCpu);
2393 RTGCUINTREG cr4 = CPUMGetGuestCR4(pVCpu);
2394 *pfPSE = !!(cr4 & X86_CR4_PSE);
2395 *pfPGE = !!(cr4 & X86_CR4_PGE);
2396 if (cr4 & X86_CR4_PAE)
2397 {
2398 *pfPSE = true;
2399 *pfPAE = true;
2400 }
2401 else
2402 *pfPAE = false;
2403
2404 *pfLME = CPUMGetGuestMode(pVCpu) == CPUMMODE_LONG;
2405 *pfNXE = false; /* GUEST64 GUESTNX */
2406 return CPUMGetGuestCR3(pVCpu);
2407}
2408
2409
2410/**
2411 * Determine the shadow paging mode.
2412 *
2413 * @returns cr3.
2414 * @param pDbgc The DBGC instance.
2415 * @param pfPAE Where to store the page address extension indicator.
2416 * @param pfLME Where to store the long mode enabled indicator.
2417 * @param pfPSE Where to store the page size extension indicator.
2418 * @param pfPGE Where to store the page global enabled indicator.
2419 * @param pfNXE Where to store the no-execution enabled indicator.
2420 */
2421static RTHCPHYS dbgcGetShadowPageMode(PDBGC pDbgc, bool *pfPAE, bool *pfLME, bool *pfPSE, bool *pfPGE, bool *pfNXE)
2422{
2423 PVMCPU pVCpu = VMMR3GetCpuByIdU(pDbgc->pUVM, pDbgc->idCpu);
2424
2425 *pfPSE = true;
2426 *pfPGE = false;
2427 switch (PGMGetShadowMode(pVCpu))
2428 {
2429 default:
2430 case PGMMODE_32_BIT:
2431 *pfPAE = *pfLME = *pfNXE = false;
2432 break;
2433 case PGMMODE_PAE:
2434 *pfLME = *pfNXE = false;
2435 *pfPAE = true;
2436 break;
2437 case PGMMODE_PAE_NX:
2438 *pfLME = false;
2439 *pfPAE = *pfNXE = true;
2440 break;
2441 case PGMMODE_AMD64:
2442 *pfNXE = false;
2443 *pfPAE = *pfLME = true;
2444 break;
2445 case PGMMODE_AMD64_NX:
2446 *pfPAE = *pfLME = *pfNXE = true;
2447 break;
2448 }
2449 return PGMGetHyperCR3(pVCpu);
2450}
2451
2452
2453/**
2454 * @interface_method_impl{FNDBCCMD, The 'dpd', 'dpda', 'dpdb', 'dpdg' and 'dpdh'
2455 * commands.}
2456 */
2457static DECLCALLBACK(int) dbgcCmdDumpPageDir(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2458{
2459 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2460
2461 /*
2462 * Validate input.
2463 */
2464 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
2465 if (cArgs == 1 && pCmd->pszCmd[3] == 'a')
2466 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
2467 if (cArgs == 1 && pCmd->pszCmd[3] != 'a')
2468 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
2469 || DBGCVAR_ISPOINTER(paArgs[0].enmType));
2470 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2471
2472 /*
2473 * Guest or shadow page directories? Get the paging parameters.
2474 */
2475 bool fGuest = pCmd->pszCmd[3] != 'h';
2476 if (!pCmd->pszCmd[3] || pCmd->pszCmd[3] == 'a')
2477 fGuest = paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
2478 ? pDbgc->fRegCtxGuest
2479 : DBGCVAR_ISGCPOINTER(paArgs[0].enmType);
2480
2481 bool fPAE, fLME, fPSE, fPGE, fNXE;
2482 uint64_t cr3 = fGuest
2483 ? dbgcGetGuestPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE)
2484 : dbgcGetShadowPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE);
2485 const unsigned cbEntry = fPAE ? sizeof(X86PTEPAE) : sizeof(X86PTE);
2486
2487 /*
2488 * Setup default argument if none was specified.
2489 * Fix address / index confusion.
2490 */
2491 DBGCVAR VarDefault;
2492 if (!cArgs)
2493 {
2494 if (pCmd->pszCmd[3] == 'a')
2495 {
2496 if (fLME || fPAE)
2497 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");
2498 if (fGuest)
2499 DBGCVAR_INIT_GC_PHYS(&VarDefault, cr3);
2500 else
2501 DBGCVAR_INIT_HC_PHYS(&VarDefault, cr3);
2502 }
2503 else
2504 DBGCVAR_INIT_GC_FLAT(&VarDefault, 0);
2505 paArgs = &VarDefault;
2506 cArgs = 1;
2507 }
2508 else if (paArgs[0].enmType == DBGCVAR_TYPE_NUMBER)
2509 {
2510 /* If it's a number (not an address), it's an index, so convert it to an address. */
2511 Assert(pCmd->pszCmd[3] != 'a');
2512 VarDefault = paArgs[0];
2513 if (fPAE)
2514 return DBGCCmdHlpPrintf(pCmdHlp, "PDE indexing is only implemented for 32-bit paging.\n");
2515 if (VarDefault.u.u64Number >= PAGE_SIZE / cbEntry)
2516 return DBGCCmdHlpPrintf(pCmdHlp, "PDE index is out of range [0..%d].\n", PAGE_SIZE / cbEntry - 1);
2517 VarDefault.u.u64Number <<= X86_PD_SHIFT;
2518 VarDefault.enmType = DBGCVAR_TYPE_GC_FLAT;
2519 paArgs = &VarDefault;
2520 }
2521
2522 /*
2523 * Locate the PDE to start displaying at.
2524 *
2525 * The 'dpda' command takes the address of a PDE, while the others are guest
2526 * virtual address which PDEs should be displayed. So, 'dpda' is rather simple
2527 * while the others require us to do all the tedious walking thru the paging
2528 * hierarchy to find the intended PDE.
2529 */
2530 unsigned iEntry = ~0U; /* The page directory index. ~0U for 'dpta'. */
2531 DBGCVAR VarGCPtr; /* The GC address corresponding to the current PDE (iEntry != ~0U). */
2532 DBGCVAR VarPDEAddr; /* The address of the current PDE. */
2533 unsigned cEntries; /* The number of entries to display. */
2534 unsigned cEntriesMax; /* The max number of entries to display. */
2535 int rc;
2536 if (pCmd->pszCmd[3] == 'a')
2537 {
2538 VarPDEAddr = paArgs[0];
2539 switch (VarPDEAddr.enmRangeType)
2540 {
2541 case DBGCVAR_RANGE_BYTES: cEntries = VarPDEAddr.u64Range / cbEntry; break;
2542 case DBGCVAR_RANGE_ELEMENTS: cEntries = VarPDEAddr.u64Range; break;
2543 default: cEntries = 10; break;
2544 }
2545 cEntriesMax = PAGE_SIZE / cbEntry;
2546 }
2547 else
2548 {
2549 /*
2550 * Determine the range.
2551 */
2552 switch (paArgs[0].enmRangeType)
2553 {
2554 case DBGCVAR_RANGE_BYTES: cEntries = paArgs[0].u64Range / PAGE_SIZE; break;
2555 case DBGCVAR_RANGE_ELEMENTS: cEntries = paArgs[0].u64Range; break;
2556 default: cEntries = 10; break;
2557 }
2558
2559 /*
2560 * Normalize the input address, it must be a flat GC address.
2561 */
2562 rc = DBGCCmdHlpEval(pCmdHlp, &VarGCPtr, "%%(%Dv)", &paArgs[0]);
2563 if (RT_FAILURE(rc))
2564 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "%%(%Dv)", &paArgs[0]);
2565 if (VarGCPtr.enmType == DBGCVAR_TYPE_HC_FLAT)
2566 {
2567 VarGCPtr.u.GCFlat = (uintptr_t)VarGCPtr.u.pvHCFlat;
2568 VarGCPtr.enmType = DBGCVAR_TYPE_GC_FLAT;
2569 }
2570 if (fPAE)
2571 VarGCPtr.u.GCFlat &= ~(((RTGCPTR)1 << X86_PD_PAE_SHIFT) - 1);
2572 else
2573 VarGCPtr.u.GCFlat &= ~(((RTGCPTR)1 << X86_PD_SHIFT) - 1);
2574
2575 /*
2576 * Do the paging walk until we get to the page directory.
2577 */
2578 DBGCVAR VarCur;
2579 if (fGuest)
2580 DBGCVAR_INIT_GC_PHYS(&VarCur, cr3);
2581 else
2582 DBGCVAR_INIT_HC_PHYS(&VarCur, cr3);
2583 if (fLME)
2584 {
2585 /* Page Map Level 4 Lookup. */
2586 /* Check if it's a valid address first? */
2587 VarCur.u.u64Number &= X86_PTE_PAE_PG_MASK;
2588 VarCur.u.u64Number += (((uint64_t)VarGCPtr.u.GCFlat >> X86_PML4_SHIFT) & X86_PML4_MASK) * sizeof(X86PML4E);
2589 X86PML4E Pml4e;
2590 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pml4e, sizeof(Pml4e), &VarCur, NULL);
2591 if (RT_FAILURE(rc))
2592 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PML4E memory at %DV.\n", &VarCur);
2593 if (!Pml4e.n.u1Present)
2594 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory pointer table is not present for %Dv.\n", &VarGCPtr);
2595
2596 VarCur.u.u64Number = Pml4e.u & X86_PML4E_PG_MASK;
2597 Assert(fPAE);
2598 }
2599 if (fPAE)
2600 {
2601 /* Page directory pointer table. */
2602 X86PDPE Pdpe;
2603 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE) * sizeof(Pdpe);
2604 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pdpe, sizeof(Pdpe), &VarCur, NULL);
2605 if (RT_FAILURE(rc))
2606 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDPE memory at %DV.\n", &VarCur);
2607 if (!Pdpe.n.u1Present)
2608 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory is not present for %Dv.\n", &VarGCPtr);
2609
2610 iEntry = (VarGCPtr.u.GCFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK;
2611 VarPDEAddr = VarCur;
2612 VarPDEAddr.u.u64Number = Pdpe.u & X86_PDPE_PG_MASK;
2613 VarPDEAddr.u.u64Number += iEntry * sizeof(X86PDEPAE);
2614 }
2615 else
2616 {
2617 /* 32-bit legacy - CR3 == page directory. */
2618 iEntry = (VarGCPtr.u.GCFlat >> X86_PD_SHIFT) & X86_PD_MASK;
2619 VarPDEAddr = VarCur;
2620 VarPDEAddr.u.u64Number += iEntry * sizeof(X86PDE);
2621 }
2622 cEntriesMax = (PAGE_SIZE - iEntry) / cbEntry;
2623 }
2624
2625 /* adjust cEntries */
2626 cEntries = RT_MAX(1, cEntries);
2627 cEntries = RT_MIN(cEntries, cEntriesMax);
2628
2629 /*
2630 * The display loop.
2631 */
2632 DBGCCmdHlpPrintf(pCmdHlp, iEntry != ~0U ? "%DV (index %#x):\n" : "%DV:\n",
2633 &VarPDEAddr, iEntry);
2634 do
2635 {
2636 /*
2637 * Read.
2638 */
2639 X86PDEPAE Pde;
2640 Pde.u = 0;
2641 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pde, cbEntry, &VarPDEAddr, NULL);
2642 if (RT_FAILURE(rc))
2643 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarPDEAddr);
2644
2645 /*
2646 * Display.
2647 */
2648 if (iEntry != ~0U)
2649 {
2650 DBGCCmdHlpPrintf(pCmdHlp, "%03x %DV: ", iEntry, &VarGCPtr);
2651 iEntry++;
2652 }
2653 if (fPSE && Pde.b.u1Size)
2654 DBGCCmdHlpPrintf(pCmdHlp,
2655 fPAE
2656 ? "%016llx big phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s %s"
2657 : "%08llx big phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s %s",
2658 Pde.u,
2659 Pde.u & X86_PDE_PAE_PG_MASK,
2660 Pde.b.u1Present ? "p " : "np",
2661 Pde.b.u1Write ? "w" : "r",
2662 Pde.b.u1User ? "u" : "s",
2663 Pde.b.u1Accessed ? "a " : "na",
2664 Pde.b.u1Dirty ? "d " : "nd",
2665 Pde.b.u3Available,
2666 Pde.b.u1Global ? (fPGE ? "g" : "G") : " ",
2667 Pde.b.u1WriteThru ? "pwt" : " ",
2668 Pde.b.u1CacheDisable ? "pcd" : " ",
2669 Pde.b.u1PAT ? "pat" : "",
2670 Pde.b.u1NoExecute ? (fNXE ? "nx" : "NX") : " ");
2671 else
2672 DBGCCmdHlpPrintf(pCmdHlp,
2673 fPAE
2674 ? "%016llx 4kb phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s"
2675 : "%08llx 4kb phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s",
2676 Pde.u,
2677 Pde.u & X86_PDE_PAE_PG_MASK,
2678 Pde.n.u1Present ? "p " : "np",
2679 Pde.n.u1Write ? "w" : "r",
2680 Pde.n.u1User ? "u" : "s",
2681 Pde.n.u1Accessed ? "a " : "na",
2682 Pde.u & RT_BIT(6) ? "6 " : " ",
2683 Pde.n.u3Available,
2684 Pde.u & RT_BIT(8) ? "8" : " ",
2685 Pde.n.u1WriteThru ? "pwt" : " ",
2686 Pde.n.u1CacheDisable ? "pcd" : " ",
2687 Pde.u & RT_BIT(7) ? "7" : "",
2688 Pde.n.u1NoExecute ? (fNXE ? "nx" : "NX") : " ");
2689 if (Pde.u & UINT64_C(0x7fff000000000000))
2690 DBGCCmdHlpPrintf(pCmdHlp, " weird=%RX64", (Pde.u & UINT64_C(0x7fff000000000000)));
2691 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
2692 if (RT_FAILURE(rc))
2693 return rc;
2694
2695 /*
2696 * Advance.
2697 */
2698 VarPDEAddr.u.u64Number += cbEntry;
2699 if (iEntry != ~0U)
2700 VarGCPtr.u.GCFlat += fPAE ? RT_BIT_32(X86_PD_PAE_SHIFT) : RT_BIT_32(X86_PD_SHIFT);
2701 } while (cEntries-- > 0);
2702
2703 return VINF_SUCCESS;
2704}
2705
2706
2707/**
2708 * @interface_method_impl{FNDBCCMD, The 'dpdb' command.}
2709 */
2710static DECLCALLBACK(int) dbgcCmdDumpPageDirBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2711{
2712 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2713 int rc1 = pCmdHlp->pfnExec(pCmdHlp, "dpdg %DV", &paArgs[0]);
2714 int rc2 = pCmdHlp->pfnExec(pCmdHlp, "dpdh %DV", &paArgs[0]);
2715 if (RT_FAILURE(rc1))
2716 return rc1;
2717 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
2718 return rc2;
2719}
2720
2721
2722/**
2723 * @interface_method_impl{FNDBCCMD, The 'dph*' commands and main part of 'm'.}
2724 */
2725static DECLCALLBACK(int) dbgcCmdDumpPageHierarchy(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2726{
2727 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2728 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2729
2730 /*
2731 * Figure the context and base flags.
2732 */
2733 uint32_t fFlags = DBGFPGDMP_FLAGS_PAGE_INFO | DBGFPGDMP_FLAGS_PRINT_CR3;
2734 if (pCmd->pszCmd[0] == 'm')
2735 fFlags |= DBGFPGDMP_FLAGS_GUEST | DBGFPGDMP_FLAGS_SHADOW;
2736 else if (pCmd->pszCmd[3] == '\0')
2737 fFlags |= pDbgc->fRegCtxGuest ? DBGFPGDMP_FLAGS_GUEST : DBGFPGDMP_FLAGS_SHADOW;
2738 else if (pCmd->pszCmd[3] == 'g')
2739 fFlags |= DBGFPGDMP_FLAGS_GUEST;
2740 else if (pCmd->pszCmd[3] == 'h')
2741 fFlags |= DBGFPGDMP_FLAGS_SHADOW;
2742 else
2743 AssertFailed();
2744
2745 if (pDbgc->cPagingHierarchyDumps == 0)
2746 fFlags |= DBGFPGDMP_FLAGS_HEADER;
2747 pDbgc->cPagingHierarchyDumps = (pDbgc->cPagingHierarchyDumps + 1) % 42;
2748
2749 /*
2750 * Get the range.
2751 */
2752 PCDBGCVAR pRange = cArgs > 0 ? &paArgs[0] : pDbgc->pLastPos;
2753 RTGCPTR GCPtrFirst = NIL_RTGCPTR;
2754 int rc = DBGCCmdHlpVarToFlatAddr(pCmdHlp, pRange, &GCPtrFirst);
2755 if (RT_FAILURE(rc))
2756 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to convert %DV to a flat address: %Rrc", pRange, rc);
2757
2758 uint64_t cbRange;
2759 rc = DBGCCmdHlpVarGetRange(pCmdHlp, pRange, PAGE_SIZE, PAGE_SIZE * 8, &cbRange);
2760 if (RT_FAILURE(rc))
2761 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to obtain the range of %DV: %Rrc", pRange, rc);
2762
2763 RTGCPTR GCPtrLast = RTGCPTR_MAX - GCPtrFirst;
2764 if (cbRange >= GCPtrLast)
2765 GCPtrLast = RTGCPTR_MAX;
2766 else if (!cbRange)
2767 GCPtrLast = GCPtrFirst;
2768 else
2769 GCPtrLast = GCPtrFirst + cbRange - 1;
2770
2771 /*
2772 * Do we have a CR3?
2773 */
2774 uint64_t cr3 = 0;
2775 if (cArgs > 1)
2776 {
2777 if ((fFlags & (DBGFPGDMP_FLAGS_GUEST | DBGFPGDMP_FLAGS_SHADOW)) == (DBGFPGDMP_FLAGS_GUEST | DBGFPGDMP_FLAGS_SHADOW))
2778 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No CR3 or mode arguments when dumping both context, please.");
2779 if (paArgs[1].enmType != DBGCVAR_TYPE_NUMBER)
2780 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The CR3 argument is not a number: %DV", &paArgs[1]);
2781 cr3 = paArgs[1].u.u64Number;
2782 }
2783 else
2784 fFlags |= DBGFPGDMP_FLAGS_CURRENT_CR3;
2785
2786 /*
2787 * Do we have a mode?
2788 */
2789 if (cArgs > 2)
2790 {
2791 if (paArgs[2].enmType != DBGCVAR_TYPE_STRING)
2792 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The mode argument is not a string: %DV", &paArgs[2]);
2793 static const struct MODETOFLAGS
2794 {
2795 const char *pszName;
2796 uint32_t fFlags;
2797 } s_aModeToFlags[] =
2798 {
2799 { "ept", DBGFPGDMP_FLAGS_EPT },
2800 { "legacy", 0 },
2801 { "legacy-np", DBGFPGDMP_FLAGS_NP },
2802 { "pse", DBGFPGDMP_FLAGS_PSE },
2803 { "pse-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_NP },
2804 { "pae", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE },
2805 { "pae-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_NP },
2806 { "pae-nx", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_NXE },
2807 { "pae-nx-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_NXE | DBGFPGDMP_FLAGS_NP },
2808 { "long", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME },
2809 { "long-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME | DBGFPGDMP_FLAGS_NP },
2810 { "long-nx", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME | DBGFPGDMP_FLAGS_NXE },
2811 { "long-nx-np", DBGFPGDMP_FLAGS_PSE | DBGFPGDMP_FLAGS_PAE | DBGFPGDMP_FLAGS_LME | DBGFPGDMP_FLAGS_NXE | DBGFPGDMP_FLAGS_NP }
2812 };
2813 int i = RT_ELEMENTS(s_aModeToFlags);
2814 while (i-- > 0)
2815 if (!strcmp(s_aModeToFlags[i].pszName, paArgs[2].u.pszString))
2816 {
2817 fFlags |= s_aModeToFlags[i].fFlags;
2818 break;
2819 }
2820 if (i < 0)
2821 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Unknown mode: \"%s\"", paArgs[2].u.pszString);
2822 }
2823 else
2824 fFlags |= DBGFPGDMP_FLAGS_CURRENT_MODE;
2825
2826 /*
2827 * Call the worker.
2828 */
2829 rc = DBGFR3PagingDumpEx(pUVM, pDbgc->idCpu, fFlags, cr3, GCPtrFirst, GCPtrLast, 99 /*cMaxDepth*/,
2830 DBGCCmdHlpGetDbgfOutputHlp(pCmdHlp));
2831 if (RT_FAILURE(rc))
2832 return DBGCCmdHlpFail(pCmdHlp, pCmd, "DBGFR3PagingDumpEx: %Rrc\n", rc);
2833 return VINF_SUCCESS;
2834}
2835
2836
2837
2838/**
2839 * @interface_method_impl{FNDBCCMD, The 'dpg*' commands.}
2840 */
2841static DECLCALLBACK(int) dbgcCmdDumpPageTable(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2842{
2843 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2844
2845 /*
2846 * Validate input.
2847 */
2848 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 1);
2849 if (pCmd->pszCmd[3] == 'a')
2850 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
2851 else
2852 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
2853 || DBGCVAR_ISPOINTER(paArgs[0].enmType));
2854 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
2855
2856 /*
2857 * Guest or shadow page tables? Get the paging parameters.
2858 */
2859 bool fGuest = pCmd->pszCmd[3] != 'h';
2860 if (!pCmd->pszCmd[3] || pCmd->pszCmd[3] == 'a')
2861 fGuest = paArgs[0].enmType == DBGCVAR_TYPE_NUMBER
2862 ? pDbgc->fRegCtxGuest
2863 : DBGCVAR_ISGCPOINTER(paArgs[0].enmType);
2864
2865 bool fPAE, fLME, fPSE, fPGE, fNXE;
2866 uint64_t cr3 = fGuest
2867 ? dbgcGetGuestPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE)
2868 : dbgcGetShadowPageMode(pDbgc, &fPAE, &fLME, &fPSE, &fPGE, &fNXE);
2869 const unsigned cbEntry = fPAE ? sizeof(X86PTEPAE) : sizeof(X86PTE);
2870
2871 /*
2872 * Locate the PTE to start displaying at.
2873 *
2874 * The 'dpta' command takes the address of a PTE, while the others are guest
2875 * virtual address which PTEs should be displayed. So, 'pdta' is rather simple
2876 * while the others require us to do all the tedious walking thru the paging
2877 * hierarchy to find the intended PTE.
2878 */
2879 unsigned iEntry = ~0U; /* The page table index. ~0U for 'dpta'. */
2880 DBGCVAR VarGCPtr; /* The GC address corresponding to the current PTE (iEntry != ~0U). */
2881 DBGCVAR VarPTEAddr; /* The address of the current PTE. */
2882 unsigned cEntries; /* The number of entries to display. */
2883 unsigned cEntriesMax; /* The max number of entries to display. */
2884 int rc;
2885 if (pCmd->pszCmd[3] == 'a')
2886 {
2887 VarPTEAddr = paArgs[0];
2888 switch (VarPTEAddr.enmRangeType)
2889 {
2890 case DBGCVAR_RANGE_BYTES: cEntries = VarPTEAddr.u64Range / cbEntry; break;
2891 case DBGCVAR_RANGE_ELEMENTS: cEntries = VarPTEAddr.u64Range; break;
2892 default: cEntries = 10; break;
2893 }
2894 cEntriesMax = PAGE_SIZE / cbEntry;
2895 }
2896 else
2897 {
2898 /*
2899 * Determine the range.
2900 */
2901 switch (paArgs[0].enmRangeType)
2902 {
2903 case DBGCVAR_RANGE_BYTES: cEntries = paArgs[0].u64Range / PAGE_SIZE; break;
2904 case DBGCVAR_RANGE_ELEMENTS: cEntries = paArgs[0].u64Range; break;
2905 default: cEntries = 10; break;
2906 }
2907
2908 /*
2909 * Normalize the input address, it must be a flat GC address.
2910 */
2911 rc = DBGCCmdHlpEval(pCmdHlp, &VarGCPtr, "%%(%Dv)", &paArgs[0]);
2912 if (RT_FAILURE(rc))
2913 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "%%(%Dv)", &paArgs[0]);
2914 if (VarGCPtr.enmType == DBGCVAR_TYPE_HC_FLAT)
2915 {
2916 VarGCPtr.u.GCFlat = (uintptr_t)VarGCPtr.u.pvHCFlat;
2917 VarGCPtr.enmType = DBGCVAR_TYPE_GC_FLAT;
2918 }
2919 VarGCPtr.u.GCFlat &= ~(RTGCPTR)PAGE_OFFSET_MASK;
2920
2921 /*
2922 * Do the paging walk until we get to the page table.
2923 */
2924 DBGCVAR VarCur;
2925 if (fGuest)
2926 DBGCVAR_INIT_GC_PHYS(&VarCur, cr3);
2927 else
2928 DBGCVAR_INIT_HC_PHYS(&VarCur, cr3);
2929 if (fLME)
2930 {
2931 /* Page Map Level 4 Lookup. */
2932 /* Check if it's a valid address first? */
2933 VarCur.u.u64Number &= X86_PTE_PAE_PG_MASK;
2934 VarCur.u.u64Number += (((uint64_t)VarGCPtr.u.GCFlat >> X86_PML4_SHIFT) & X86_PML4_MASK) * sizeof(X86PML4E);
2935 X86PML4E Pml4e;
2936 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pml4e, sizeof(Pml4e), &VarCur, NULL);
2937 if (RT_FAILURE(rc))
2938 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PML4E memory at %DV.\n", &VarCur);
2939 if (!Pml4e.n.u1Present)
2940 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory pointer table is not present for %Dv.\n", &VarGCPtr);
2941
2942 VarCur.u.u64Number = Pml4e.u & X86_PML4E_PG_MASK;
2943 Assert(fPAE);
2944 }
2945 if (fPAE)
2946 {
2947 /* Page directory pointer table. */
2948 X86PDPE Pdpe;
2949 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE) * sizeof(Pdpe);
2950 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pdpe, sizeof(Pdpe), &VarCur, NULL);
2951 if (RT_FAILURE(rc))
2952 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDPE memory at %DV.\n", &VarCur);
2953 if (!Pdpe.n.u1Present)
2954 return DBGCCmdHlpPrintf(pCmdHlp, "Page directory is not present for %Dv.\n", &VarGCPtr);
2955
2956 VarCur.u.u64Number = Pdpe.u & X86_PDPE_PG_MASK;
2957
2958 /* Page directory (PAE). */
2959 X86PDEPAE Pde;
2960 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK) * sizeof(Pde);
2961 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pde, sizeof(Pde), &VarCur, NULL);
2962 if (RT_FAILURE(rc))
2963 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarCur);
2964 if (!Pde.n.u1Present)
2965 return DBGCCmdHlpPrintf(pCmdHlp, "Page table is not present for %Dv.\n", &VarGCPtr);
2966 if (fPSE && Pde.n.u1Size)
2967 return pCmdHlp->pfnExec(pCmdHlp, "dpd%s %Dv L3", &pCmd->pszCmd[3], &VarGCPtr);
2968
2969 iEntry = (VarGCPtr.u.GCFlat >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK;
2970 VarPTEAddr = VarCur;
2971 VarPTEAddr.u.u64Number = Pde.u & X86_PDE_PAE_PG_MASK;
2972 VarPTEAddr.u.u64Number += iEntry * sizeof(X86PTEPAE);
2973 }
2974 else
2975 {
2976 /* Page directory (legacy). */
2977 X86PDE Pde;
2978 VarCur.u.u64Number += ((VarGCPtr.u.GCFlat >> X86_PD_SHIFT) & X86_PD_MASK) * sizeof(Pde);
2979 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pde, sizeof(Pde), &VarCur, NULL);
2980 if (RT_FAILURE(rc))
2981 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PDE memory at %DV.\n", &VarCur);
2982 if (!Pde.n.u1Present)
2983 return DBGCCmdHlpPrintf(pCmdHlp, "Page table is not present for %Dv.\n", &VarGCPtr);
2984 if (fPSE && Pde.n.u1Size)
2985 return pCmdHlp->pfnExec(pCmdHlp, "dpd%s %Dv L3", &pCmd->pszCmd[3], &VarGCPtr);
2986
2987 iEntry = (VarGCPtr.u.GCFlat >> X86_PT_SHIFT) & X86_PT_MASK;
2988 VarPTEAddr = VarCur;
2989 VarPTEAddr.u.u64Number = Pde.u & X86_PDE_PG_MASK;
2990 VarPTEAddr.u.u64Number += iEntry * sizeof(X86PTE);
2991 }
2992 cEntriesMax = (PAGE_SIZE - iEntry) / cbEntry;
2993 }
2994
2995 /* adjust cEntries */
2996 cEntries = RT_MAX(1, cEntries);
2997 cEntries = RT_MIN(cEntries, cEntriesMax);
2998
2999 /*
3000 * The display loop.
3001 */
3002 DBGCCmdHlpPrintf(pCmdHlp, iEntry != ~0U ? "%DV (base %DV / index %#x):\n" : "%DV:\n",
3003 &VarPTEAddr, &VarGCPtr, iEntry);
3004 do
3005 {
3006 /*
3007 * Read.
3008 */
3009 X86PTEPAE Pte;
3010 Pte.u = 0;
3011 rc = pCmdHlp->pfnMemRead(pCmdHlp, &Pte, cbEntry, &VarPTEAddr, NULL);
3012 if (RT_FAILURE(rc))
3013 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Reading PTE memory at %DV.\n", &VarPTEAddr);
3014
3015 /*
3016 * Display.
3017 */
3018 if (iEntry != ~0U)
3019 {
3020 DBGCCmdHlpPrintf(pCmdHlp, "%03x %DV: ", iEntry, &VarGCPtr);
3021 iEntry++;
3022 }
3023 DBGCCmdHlpPrintf(pCmdHlp,
3024 fPAE
3025 ? "%016llx 4kb phys=%016llx %s %s %s %s %s avl=%02x %s %s %s %s %s"
3026 : "%08llx 4kb phys=%08llx %s %s %s %s %s avl=%02x %s %s %s %s %s",
3027 Pte.u,
3028 Pte.u & X86_PTE_PAE_PG_MASK,
3029 Pte.n.u1Present ? "p " : "np",
3030 Pte.n.u1Write ? "w" : "r",
3031 Pte.n.u1User ? "u" : "s",
3032 Pte.n.u1Accessed ? "a " : "na",
3033 Pte.n.u1Dirty ? "d " : "nd",
3034 Pte.n.u3Available,
3035 Pte.n.u1Global ? (fPGE ? "g" : "G") : " ",
3036 Pte.n.u1WriteThru ? "pwt" : " ",
3037 Pte.n.u1CacheDisable ? "pcd" : " ",
3038 Pte.n.u1PAT ? "pat" : " ",
3039 Pte.n.u1NoExecute ? (fNXE ? "nx" : "NX") : " "
3040 );
3041 if (Pte.u & UINT64_C(0x7fff000000000000))
3042 DBGCCmdHlpPrintf(pCmdHlp, " weird=%RX64", (Pte.u & UINT64_C(0x7fff000000000000)));
3043 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
3044 if (RT_FAILURE(rc))
3045 return rc;
3046
3047 /*
3048 * Advance.
3049 */
3050 VarPTEAddr.u.u64Number += cbEntry;
3051 if (iEntry != ~0U)
3052 VarGCPtr.u.GCFlat += PAGE_SIZE;
3053 } while (cEntries-- > 0);
3054
3055 return VINF_SUCCESS;
3056}
3057
3058
3059/**
3060 * @interface_method_impl{FNDBCCMD, The 'dptb' command.}
3061 */
3062static DECLCALLBACK(int) dbgcCmdDumpPageTableBoth(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3063{
3064 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3065 int rc1 = pCmdHlp->pfnExec(pCmdHlp, "dptg %DV", &paArgs[0]);
3066 int rc2 = pCmdHlp->pfnExec(pCmdHlp, "dpth %DV", &paArgs[0]);
3067 if (RT_FAILURE(rc1))
3068 return rc1;
3069 NOREF(pCmd); NOREF(cArgs);
3070 return rc2;
3071}
3072
3073
3074/**
3075 * @interface_method_impl{FNDBCCMD, The 'dt' command.}
3076 */
3077static DECLCALLBACK(int) dbgcCmdDumpTSS(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3078{
3079 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3080 int rc;
3081
3082 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3083 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs <= 1);
3084 if (cArgs == 1)
3085 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[0].enmType != DBGCVAR_TYPE_STRING
3086 && paArgs[0].enmType != DBGCVAR_TYPE_SYMBOL);
3087
3088 /*
3089 * Check if the command indicates the type.
3090 */
3091 enum { kTss16, kTss32, kTss64, kTssToBeDetermined } enmTssType = kTssToBeDetermined;
3092 if (!strcmp(pCmd->pszCmd, "dt16"))
3093 enmTssType = kTss16;
3094 else if (!strcmp(pCmd->pszCmd, "dt32"))
3095 enmTssType = kTss32;
3096 else if (!strcmp(pCmd->pszCmd, "dt64"))
3097 enmTssType = kTss64;
3098
3099 /*
3100 * We can get a TSS selector (number), a far pointer using a TSS selector, or some kind of TSS pointer.
3101 */
3102 uint32_t SelTss = UINT32_MAX;
3103 DBGCVAR VarTssAddr;
3104 if (cArgs == 0)
3105 {
3106 /** @todo consider querying the hidden bits instead (missing API). */
3107 uint16_t SelTR;
3108 rc = DBGFR3RegCpuQueryU16(pUVM, pDbgc->idCpu, DBGFREG_TR, &SelTR);
3109 if (RT_FAILURE(rc))
3110 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to query TR, rc=%Rrc\n", rc);
3111 DBGCVAR_INIT_GC_FAR(&VarTssAddr, SelTR, 0);
3112 SelTss = SelTR;
3113 }
3114 else if (paArgs[0].enmType == DBGCVAR_TYPE_NUMBER)
3115 {
3116 if (paArgs[0].u.u64Number < 0xffff)
3117 DBGCVAR_INIT_GC_FAR(&VarTssAddr, (RTSEL)paArgs[0].u.u64Number, 0);
3118 else
3119 {
3120 if (paArgs[0].enmRangeType == DBGCVAR_RANGE_ELEMENTS)
3121 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Element count doesn't combine with a TSS address.\n");
3122 DBGCVAR_INIT_GC_FLAT(&VarTssAddr, paArgs[0].u.u64Number);
3123 if (paArgs[0].enmRangeType == DBGCVAR_RANGE_BYTES)
3124 {
3125 VarTssAddr.enmRangeType = paArgs[0].enmRangeType;
3126 VarTssAddr.u64Range = paArgs[0].u64Range;
3127 }
3128 }
3129 }
3130 else
3131 VarTssAddr = paArgs[0];
3132
3133 /*
3134 * Deal with TSS:ign by means of the GDT.
3135 */
3136 if (VarTssAddr.enmType == DBGCVAR_TYPE_GC_FAR)
3137 {
3138 SelTss = VarTssAddr.u.GCFar.sel;
3139 DBGFSELINFO SelInfo;
3140 rc = DBGFR3SelQueryInfo(pUVM, pDbgc->idCpu, VarTssAddr.u.GCFar.sel, DBGFSELQI_FLAGS_DT_GUEST, &SelInfo);
3141 if (RT_FAILURE(rc))
3142 return DBGCCmdHlpFail(pCmdHlp, pCmd, "DBGFR3SelQueryInfo(,%u,%d,,) -> %Rrc.\n",
3143 pDbgc->idCpu, VarTssAddr.u.GCFar.sel, rc);
3144
3145 if (SelInfo.u.Raw.Gen.u1DescType)
3146 return DBGCCmdHlpFail(pCmdHlp, pCmd, "%04x is not a TSS selector. (!sys)\n", VarTssAddr.u.GCFar.sel);
3147
3148 switch (SelInfo.u.Raw.Gen.u4Type)
3149 {
3150 case X86_SEL_TYPE_SYS_286_TSS_BUSY:
3151 case X86_SEL_TYPE_SYS_286_TSS_AVAIL:
3152 if (enmTssType == kTssToBeDetermined)
3153 enmTssType = kTss16;
3154 break;
3155
3156 case X86_SEL_TYPE_SYS_386_TSS_BUSY: /* AMD64 too */
3157 case X86_SEL_TYPE_SYS_386_TSS_AVAIL:
3158 if (enmTssType == kTssToBeDetermined)
3159 enmTssType = SelInfo.fFlags & DBGFSELINFO_FLAGS_LONG_MODE ? kTss64 : kTss32;
3160 break;
3161
3162 default:
3163 return DBGCCmdHlpFail(pCmdHlp, pCmd, "%04x is not a TSS selector. (type=%x)\n",
3164 VarTssAddr.u.GCFar.sel, SelInfo.u.Raw.Gen.u4Type);
3165 }
3166
3167 DBGCVAR_INIT_GC_FLAT(&VarTssAddr, SelInfo.GCPtrBase);
3168 DBGCVAR_SET_RANGE(&VarTssAddr, DBGCVAR_RANGE_BYTES, RT_MAX(SelInfo.cbLimit + 1, SelInfo.cbLimit));
3169 }
3170
3171 /*
3172 * Determine the TSS type if none is currently given.
3173 */
3174 if (enmTssType == kTssToBeDetermined)
3175 {
3176 if ( VarTssAddr.u64Range > 0
3177 && VarTssAddr.u64Range < sizeof(X86TSS32) - 4)
3178 enmTssType = kTss16;
3179 else
3180 {
3181 uint64_t uEfer;
3182 rc = DBGFR3RegCpuQueryU64(pUVM, pDbgc->idCpu, DBGFREG_MSR_K6_EFER, &uEfer);
3183 if ( RT_FAILURE(rc)
3184 || !(uEfer & MSR_K6_EFER_LMA) )
3185 enmTssType = kTss32;
3186 else
3187 enmTssType = kTss64;
3188 }
3189 }
3190
3191 /*
3192 * Figure the min/max sizes.
3193 * ASSUMES max TSS size is 64 KB.
3194 */
3195 uint32_t cbTssMin;
3196 uint32_t cbTssMax;
3197 switch (enmTssType)
3198 {
3199 case kTss16:
3200 cbTssMin = cbTssMax = sizeof(X86TSS16);
3201 break;
3202 case kTss32:
3203 cbTssMin = RT_OFFSETOF(X86TSS32, IntRedirBitmap);
3204 cbTssMax = _64K;
3205 break;
3206 case kTss64:
3207 cbTssMin = RT_OFFSETOF(X86TSS64, IntRedirBitmap);
3208 cbTssMax = _64K;
3209 break;
3210 default:
3211 AssertFailedReturn(VERR_INTERNAL_ERROR);
3212 }
3213 uint32_t cbTss = VarTssAddr.enmRangeType == DBGCVAR_RANGE_BYTES ? (uint32_t)VarTssAddr.u64Range : 0;
3214 if (cbTss == 0)
3215 cbTss = cbTssMin;
3216 else if (cbTss < cbTssMin)
3217 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Minimum TSS size is %u bytes, you specified %llu (%llx) bytes.\n",
3218 cbTssMin, VarTssAddr.u64Range, VarTssAddr.u64Range);
3219 else if (cbTss > cbTssMax)
3220 cbTss = cbTssMax;
3221 DBGCVAR_SET_RANGE(&VarTssAddr, DBGCVAR_RANGE_BYTES, cbTss);
3222
3223 /*
3224 * Read the TSS into a temporary buffer.
3225 */
3226 uint8_t abBuf[_64K];
3227 size_t cbTssRead;
3228 rc = DBGCCmdHlpMemRead(pCmdHlp, abBuf, cbTss, &VarTssAddr, &cbTssRead);
3229 if (RT_FAILURE(rc))
3230 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to read TSS at %Dv: %Rrc\n", &VarTssAddr, rc);
3231 if (cbTssRead < cbTssMin)
3232 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Failed to read essential parts of the TSS (read %zu, min %zu).\n",
3233 cbTssRead, cbTssMin);
3234 if (cbTssRead < cbTss)
3235 memset(&abBuf[cbTssRead], 0xff, cbTss - cbTssRead);
3236
3237
3238 /*
3239 * Format the TSS.
3240 */
3241 uint16_t offIoBitmap;
3242 switch (enmTssType)
3243 {
3244 case kTss16:
3245 {
3246 PCX86TSS16 pTss = (PCX86TSS16)&abBuf[0];
3247 if (SelTss != UINT32_MAX)
3248 DBGCCmdHlpPrintf(pCmdHlp, "%04x TSS16 at %Dv\n", SelTss, &VarTssAddr);
3249 else
3250 DBGCCmdHlpPrintf(pCmdHlp, "TSS16 at %Dv\n", &VarTssAddr);
3251 DBGCCmdHlpPrintf(pCmdHlp,
3252 "ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x\n"
3253 "ip=%04x sp=%04x bp=%04x\n"
3254 "cs=%04x ss=%04x ds=%04x es=%04x flags=%04x\n"
3255 "ss:sp0=%04x:%04x ss:sp1=%04x:%04x ss:sp2=%04x:%04x\n"
3256 "prev=%04x ldtr=%04x\n"
3257 ,
3258 pTss->ax, pTss->bx, pTss->cx, pTss->dx, pTss->si, pTss->di,
3259 pTss->ip, pTss->sp, pTss->bp,
3260 pTss->cs, pTss->ss, pTss->ds, pTss->es, pTss->flags,
3261 pTss->ss0, pTss->sp0, pTss->ss1, pTss->sp1, pTss->ss2, pTss->sp2,
3262 pTss->selPrev, pTss->selLdt);
3263 if (pTss->cs != 0)
3264 pCmdHlp->pfnExec(pCmdHlp, "u %04x:%04x L 0", pTss->cs, pTss->ip);
3265 offIoBitmap = 0;
3266 break;
3267 }
3268
3269 case kTss32:
3270 {
3271 PCX86TSS32 pTss = (PCX86TSS32)&abBuf[0];
3272 if (SelTss != UINT32_MAX)
3273 DBGCCmdHlpPrintf(pCmdHlp, "%04x TSS32 at %Dv (min=%04x)\n", SelTss, &VarTssAddr, cbTssMin);
3274 else
3275 DBGCCmdHlpPrintf(pCmdHlp, "TSS32 at %Dv (min=%04x)\n", &VarTssAddr, cbTssMin);
3276 DBGCCmdHlpPrintf(pCmdHlp,
3277 "eax=%08x bx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n"
3278 "eip=%08x esp=%08x ebp=%08x\n"
3279 "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n"
3280 "ss:esp0=%04x:%08x ss:esp1=%04x:%08x ss:esp2=%04x:%08x\n"
3281 "prev=%04x ldtr=%04x cr3=%08x debug=%u iomap=%04x\n"
3282 ,
3283 pTss->eax, pTss->ebx, pTss->ecx, pTss->edx, pTss->esi, pTss->edi,
3284 pTss->eip, pTss->esp, pTss->ebp,
3285 pTss->cs, pTss->ss, pTss->ds, pTss->es, pTss->fs, pTss->gs, pTss->eflags,
3286 pTss->ss0, pTss->esp0, pTss->ss1, pTss->esp1, pTss->ss2, pTss->esp2,
3287 pTss->selPrev, pTss->selLdt, pTss->cr3, pTss->fDebugTrap, pTss->offIoBitmap);
3288 if (pTss->cs != 0)
3289 pCmdHlp->pfnExec(pCmdHlp, "u %04x:%08x L 0", pTss->cs, pTss->eip);
3290 offIoBitmap = pTss->offIoBitmap;
3291 break;
3292 }
3293
3294 case kTss64:
3295 {
3296 PCX86TSS64 pTss = (PCX86TSS64)&abBuf[0];
3297 if (SelTss != UINT32_MAX)
3298 DBGCCmdHlpPrintf(pCmdHlp, "%04x TSS64 at %Dv (min=%04x)\n", SelTss, &VarTssAddr, cbTssMin);
3299 else
3300 DBGCCmdHlpPrintf(pCmdHlp, "TSS64 at %Dv (min=%04x)\n", &VarTssAddr, cbTssMin);
3301 DBGCCmdHlpPrintf(pCmdHlp,
3302 "rsp0=%016RX16 rsp1=%016RX16 rsp2=%016RX16\n"
3303 "ist1=%016RX16 ist2=%016RX16\n"
3304 "ist3=%016RX16 ist4=%016RX16\n"
3305 "ist5=%016RX16 ist6=%016RX16\n"
3306 "ist7=%016RX16 iomap=%04x\n"
3307 ,
3308 pTss->rsp0, pTss->rsp1, pTss->rsp2,
3309 pTss->ist1, pTss->ist2,
3310 pTss->ist3, pTss->ist4,
3311 pTss->ist5, pTss->ist6,
3312 pTss->ist7, pTss->offIoBitmap);
3313 offIoBitmap = pTss->offIoBitmap;
3314 break;
3315 }
3316
3317 default:
3318 AssertFailedReturn(VERR_INTERNAL_ERROR);
3319 }
3320
3321 /*
3322 * Dump the interrupt redirection bitmap.
3323 */
3324 if (enmTssType != kTss16)
3325 {
3326 if ( offIoBitmap > cbTssMin
3327 && offIoBitmap < cbTss) /** @todo check exactly what the edge cases are here. */
3328 {
3329 if (offIoBitmap - cbTssMin >= 32)
3330 {
3331 DBGCCmdHlpPrintf(pCmdHlp, "Interrupt redirection:\n");
3332 uint8_t const *pbIntRedirBitmap = &abBuf[offIoBitmap - 32];
3333 uint32_t iStart = 0;
3334 bool fPrev = ASMBitTest(pbIntRedirBitmap, 0); /* LE/BE issue */
3335 for (uint32_t i = 0; i < 256; i++)
3336 {
3337 bool fThis = ASMBitTest(pbIntRedirBitmap, i);
3338 if (fThis != fPrev)
3339 {
3340 DBGCCmdHlpPrintf(pCmdHlp, "%02x-%02x %s\n", iStart, i - 1, fPrev ? "Protected mode" : "Redirected");
3341 fPrev = fThis;
3342 iStart = i;
3343 }
3344 }
3345 if (iStart != 255)
3346 DBGCCmdHlpPrintf(pCmdHlp, "%02x-%02x %s\n", iStart, 255, fPrev ? "Protected mode" : "Redirected");
3347 }
3348 else
3349 DBGCCmdHlpPrintf(pCmdHlp, "Invalid interrupt redirection bitmap size: %u (%#x), expected 32 bytes.\n",
3350 offIoBitmap - cbTssMin, offIoBitmap - cbTssMin);
3351 }
3352 else if (offIoBitmap > 0)
3353 DBGCCmdHlpPrintf(pCmdHlp, "No interrupt redirection bitmap (-%#x)\n", cbTssMin - offIoBitmap);
3354 else
3355 DBGCCmdHlpPrintf(pCmdHlp, "No interrupt redirection bitmap\n");
3356 }
3357
3358 /*
3359 * Dump the I/O permission bitmap if present. The IOPM cannot start below offset 0x64
3360 * (that applies to both 32-bit and 64-bit TSSs since their size is the same).
3361 */
3362 if (enmTssType != kTss16)
3363 {
3364 if (offIoBitmap < cbTss && offIoBitmap >= 0x64)
3365 {
3366 uint32_t cPorts = RT_MIN((cbTss - offIoBitmap) * 8, _64K);
3367 DBGCVAR VarAddr;
3368 DBGCCmdHlpEval(pCmdHlp, &VarAddr, "%DV + %#x", &VarTssAddr, offIoBitmap);
3369 DBGCCmdHlpPrintf(pCmdHlp, "I/O bitmap at %DV - %#x ports:\n", &VarAddr, cPorts);
3370
3371 uint8_t const *pbIoBitmap = &abBuf[offIoBitmap];
3372 uint32_t iStart = 0;
3373 bool fPrev = ASMBitTest(pbIoBitmap, 0);
3374 uint32_t cLine = 0;
3375 for (uint32_t i = 1; i < cPorts; i++)
3376 {
3377 bool fThis = ASMBitTest(pbIoBitmap, i);
3378 if (fThis != fPrev)
3379 {
3380 cLine++;
3381 DBGCCmdHlpPrintf(pCmdHlp, "%04x-%04x %s%s", iStart, i-1,
3382 fPrev ? "GP" : "OK", (cLine % 6) == 0 ? "\n" : " ");
3383 fPrev = fThis;
3384 iStart = i;
3385 }
3386 }
3387 if (iStart != _64K-1)
3388 DBGCCmdHlpPrintf(pCmdHlp, "%04x-%04x %s\n", iStart, _64K-1, fPrev ? "GP" : "OK");
3389 }
3390 else if (offIoBitmap > 0)
3391 DBGCCmdHlpPrintf(pCmdHlp, "No I/O bitmap (-%#x)\n", cbTssMin - offIoBitmap);
3392 else
3393 DBGCCmdHlpPrintf(pCmdHlp, "No I/O bitmap\n");
3394 }
3395
3396 return VINF_SUCCESS;
3397}
3398
3399
3400/**
3401 * @interface_method_impl{FNDBCCMD, The 'm' command.}
3402 */
3403static DECLCALLBACK(int) dbgcCmdMemoryInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3404{
3405 DBGCCmdHlpPrintf(pCmdHlp, "Address: %DV\n", &paArgs[0]);
3406 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3407 return dbgcCmdDumpPageHierarchy(pCmd, pCmdHlp, pUVM, paArgs, cArgs);
3408}
3409
3410
3411/**
3412 * Converts one or more variables into a byte buffer for a
3413 * given unit size.
3414 *
3415 * @returns VBox status codes:
3416 * @retval VERR_TOO_MUCH_DATA if the buffer is too small, bitched.
3417 * @retval VERR_INTERNAL_ERROR on bad variable type, bitched.
3418 * @retval VINF_SUCCESS on success.
3419 *
3420 * @param pvBuf The buffer to convert into.
3421 * @param pcbBuf The buffer size on input. The size of the result on output.
3422 * @param cbUnit The unit size to apply when converting.
3423 * The high bit is used to indicate unicode string.
3424 * @param paVars The array of variables to convert.
3425 * @param cVars The number of variables.
3426 */
3427int dbgcVarsToBytes(PDBGCCMDHLP pCmdHlp, void *pvBuf, uint32_t *pcbBuf, size_t cbUnit, PCDBGCVAR paVars, unsigned cVars)
3428{
3429 union
3430 {
3431 uint8_t *pu8;
3432 uint16_t *pu16;
3433 uint32_t *pu32;
3434 uint64_t *pu64;
3435 } u, uEnd;
3436 u.pu8 = (uint8_t *)pvBuf;
3437 uEnd.pu8 = u.pu8 + *pcbBuf;
3438
3439 unsigned i;
3440 for (i = 0; i < cVars && u.pu8 < uEnd.pu8; i++)
3441 {
3442 switch (paVars[i].enmType)
3443 {
3444 case DBGCVAR_TYPE_GC_FAR:
3445 case DBGCVAR_TYPE_GC_FLAT:
3446 case DBGCVAR_TYPE_GC_PHYS:
3447 case DBGCVAR_TYPE_HC_FLAT:
3448 case DBGCVAR_TYPE_HC_PHYS:
3449 case DBGCVAR_TYPE_NUMBER:
3450 {
3451 uint64_t u64 = paVars[i].u.u64Number;
3452 switch (cbUnit & 0x1f)
3453 {
3454 case 1:
3455 do
3456 {
3457 *u.pu8++ = u64;
3458 u64 >>= 8;
3459 } while (u64);
3460 break;
3461 case 2:
3462 do
3463 {
3464 *u.pu16++ = u64;
3465 u64 >>= 16;
3466 } while (u64);
3467 break;
3468 case 4:
3469 *u.pu32++ = u64;
3470 u64 >>= 32;
3471 if (u64)
3472 *u.pu32++ = u64;
3473 break;
3474 case 8:
3475 *u.pu64++ = u64;
3476 break;
3477 }
3478 break;
3479 }
3480
3481 case DBGCVAR_TYPE_STRING:
3482 case DBGCVAR_TYPE_SYMBOL:
3483 {
3484 const char *psz = paVars[i].u.pszString;
3485 size_t cbString = strlen(psz);
3486 if (cbUnit & RT_BIT_32(31))
3487 {
3488 /* Explode char to unit. */
3489 if (cbString > (uintptr_t)(uEnd.pu8 - u.pu8) * (cbUnit & 0x1f))
3490 {
3491 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_TOO_MUCH_DATA, "Max %d bytes.\n", uEnd.pu8 - (uint8_t *)pvBuf);
3492 return VERR_TOO_MUCH_DATA;
3493 }
3494 while (*psz)
3495 {
3496 switch (cbUnit & 0x1f)
3497 {
3498 case 1: *u.pu8++ = *psz; break;
3499 case 2: *u.pu16++ = *psz; break;
3500 case 4: *u.pu32++ = *psz; break;
3501 case 8: *u.pu64++ = *psz; break;
3502 }
3503 psz++;
3504 }
3505 }
3506 else
3507 {
3508 /* Raw copy with zero padding if the size isn't aligned. */
3509 if (cbString > (uintptr_t)(uEnd.pu8 - u.pu8))
3510 {
3511 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_TOO_MUCH_DATA, "Max %d bytes.\n", uEnd.pu8 - (uint8_t *)pvBuf);
3512 return VERR_TOO_MUCH_DATA;
3513 }
3514
3515 size_t cbCopy = cbString & ~(cbUnit - 1);
3516 memcpy(u.pu8, psz, cbCopy);
3517 u.pu8 += cbCopy;
3518 psz += cbCopy;
3519
3520 size_t cbReminder = cbString & (cbUnit - 1);
3521 if (cbReminder)
3522 {
3523 memcpy(u.pu8, psz, cbString & (cbUnit - 1));
3524 memset(u.pu8 + cbReminder, 0, cbUnit - cbReminder);
3525 u.pu8 += cbUnit;
3526 }
3527 }
3528 break;
3529 }
3530
3531 default:
3532 *pcbBuf = u.pu8 - (uint8_t *)pvBuf;
3533 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_INTERNAL_ERROR,
3534 "i=%d enmType=%d\n", i, paVars[i].enmType);
3535 return VERR_INTERNAL_ERROR;
3536 }
3537 }
3538 *pcbBuf = u.pu8 - (uint8_t *)pvBuf;
3539 if (i != cVars)
3540 {
3541 pCmdHlp->pfnVBoxError(pCmdHlp, VERR_TOO_MUCH_DATA, "Max %d bytes.\n", uEnd.pu8 - (uint8_t *)pvBuf);
3542 return VERR_TOO_MUCH_DATA;
3543 }
3544 return VINF_SUCCESS;
3545}
3546
3547
3548/**
3549 * @interface_method_impl{FNDBCCMD, The 'eb', 'ew', 'ed' and 'eq' commands.}
3550 */
3551static DECLCALLBACK(int) dbgcCmdEditMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3552{
3553 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3554 unsigned iArg;
3555
3556 /*
3557 * Validate input.
3558 */
3559 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs >= 2);
3560 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISPOINTER(paArgs[0].enmType));
3561 for (iArg = 1; iArg < cArgs; iArg++)
3562 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER);
3563 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3564
3565 /*
3566 * Figure out the element size.
3567 */
3568 unsigned cbElement;
3569 switch (pCmd->pszCmd[1])
3570 {
3571 default:
3572 case 'b': cbElement = 1; break;
3573 case 'w': cbElement = 2; break;
3574 case 'd': cbElement = 4; break;
3575 case 'q': cbElement = 8; break;
3576 }
3577
3578 /*
3579 * Do setting.
3580 */
3581 DBGCVAR Addr = paArgs[0];
3582 for (iArg = 1;;)
3583 {
3584 size_t cbWritten;
3585 int rc = pCmdHlp->pfnMemWrite(pCmdHlp, &paArgs[iArg].u, cbElement, &Addr, &cbWritten);
3586 if (RT_FAILURE(rc))
3587 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Writing memory at %DV.\n", &Addr);
3588 if (cbWritten != cbElement)
3589 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Only wrote %u out of %u bytes!\n", cbWritten, cbElement);
3590
3591 /* advance. */
3592 iArg++;
3593 if (iArg >= cArgs)
3594 break;
3595 rc = DBGCCmdHlpEval(pCmdHlp, &Addr, "%Dv + %#x", &Addr, cbElement);
3596 if (RT_FAILURE(rc))
3597 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "%%(%Dv)", &paArgs[0]);
3598 }
3599
3600 return VINF_SUCCESS;
3601}
3602
3603
3604/**
3605 * Executes the search.
3606 *
3607 * @returns VBox status code.
3608 * @param pCmdHlp The command helpers.
3609 * @param pUVM The user mode VM handle.
3610 * @param pAddress The address to start searching from. (undefined on output)
3611 * @param cbRange The address range to search. Must not wrap.
3612 * @param pabBytes The byte pattern to search for.
3613 * @param cbBytes The size of the pattern.
3614 * @param cbUnit The search unit.
3615 * @param cMaxHits The max number of hits.
3616 * @param pResult Where to store the result if it's a function invocation.
3617 */
3618static int dbgcCmdWorkerSearchMemDoIt(PDBGCCMDHLP pCmdHlp, PUVM pUVM, PDBGFADDRESS pAddress, RTGCUINTPTR cbRange,
3619 const uint8_t *pabBytes, uint32_t cbBytes,
3620 uint32_t cbUnit, uint64_t cMaxHits, PDBGCVAR pResult)
3621{
3622 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3623
3624 /*
3625 * Do the search.
3626 */
3627 uint64_t cHits = 0;
3628 for (;;)
3629 {
3630 /* search */
3631 DBGFADDRESS HitAddress;
3632 int rc = DBGFR3MemScan(pUVM, pDbgc->idCpu, pAddress, cbRange, 1, pabBytes, cbBytes, &HitAddress);
3633 if (RT_FAILURE(rc))
3634 {
3635 if (rc != VERR_DBGF_MEM_NOT_FOUND)
3636 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3MemScan\n");
3637
3638 /* update the current address so we can save it (later). */
3639 pAddress->off += cbRange;
3640 pAddress->FlatPtr += cbRange;
3641 cbRange = 0;
3642 break;
3643 }
3644
3645 /* report result */
3646 DBGCVAR VarCur;
3647 rc = DBGCCmdHlpVarFromDbgfAddr(pCmdHlp, &HitAddress, &VarCur);
3648 if (RT_FAILURE(rc))
3649 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGCCmdHlpVarFromDbgfAddr\n");
3650 if (!pResult)
3651 pCmdHlp->pfnExec(pCmdHlp, "db %DV LB 10", &VarCur);
3652 else
3653 DBGCVAR_ASSIGN(pResult, &VarCur);
3654
3655 /* advance */
3656 cbRange -= HitAddress.FlatPtr - pAddress->FlatPtr;
3657 *pAddress = HitAddress;
3658 pAddress->FlatPtr += cbBytes;
3659 pAddress->off += cbBytes;
3660 if (cbRange <= cbBytes)
3661 {
3662 cbRange = 0;
3663 break;
3664 }
3665 cbRange -= cbBytes;
3666
3667 if (++cHits >= cMaxHits)
3668 {
3669 /// @todo save the search.
3670 break;
3671 }
3672 }
3673
3674 /*
3675 * Save the search so we can resume it...
3676 */
3677 if (pDbgc->abSearch != pabBytes)
3678 {
3679 memcpy(pDbgc->abSearch, pabBytes, cbBytes);
3680 pDbgc->cbSearch = cbBytes;
3681 pDbgc->cbSearchUnit = cbUnit;
3682 }
3683 pDbgc->cMaxSearchHits = cMaxHits;
3684 pDbgc->SearchAddr = *pAddress;
3685 pDbgc->cbSearchRange = cbRange;
3686
3687 return cHits ? VINF_SUCCESS : VERR_DBGC_COMMAND_FAILED;
3688}
3689
3690
3691/**
3692 * Resumes the previous search.
3693 *
3694 * @returns VBox status code.
3695 * @param pCmdHlp Pointer to the command helper functions.
3696 * @param pUVM The user mode VM handle.
3697 * @param pResult Where to store the result of a function invocation.
3698 */
3699static int dbgcCmdWorkerSearchMemResume(PDBGCCMDHLP pCmdHlp, PUVM pUVM, PDBGCVAR pResult)
3700{
3701 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3702
3703 /*
3704 * Make sure there is a previous command.
3705 */
3706 if (!pDbgc->cbSearch)
3707 {
3708 DBGCCmdHlpPrintf(pCmdHlp, "Error: No previous search\n");
3709 return VERR_DBGC_COMMAND_FAILED;
3710 }
3711
3712 /*
3713 * Make range and address adjustments.
3714 */
3715 DBGFADDRESS Address = pDbgc->SearchAddr;
3716 if (Address.FlatPtr == ~(RTGCUINTPTR)0)
3717 {
3718 Address.FlatPtr -= Address.off;
3719 Address.off = 0;
3720 }
3721
3722 RTGCUINTPTR cbRange = pDbgc->cbSearchRange;
3723 if (!cbRange)
3724 cbRange = ~(RTGCUINTPTR)0;
3725 if (Address.FlatPtr + cbRange < pDbgc->SearchAddr.FlatPtr)
3726 cbRange = ~(RTGCUINTPTR)0 - pDbgc->SearchAddr.FlatPtr + !!pDbgc->SearchAddr.FlatPtr;
3727
3728 return dbgcCmdWorkerSearchMemDoIt(pCmdHlp, pUVM, &Address, cbRange, pDbgc->abSearch, pDbgc->cbSearch,
3729 pDbgc->cbSearchUnit, pDbgc->cMaxSearchHits, pResult);
3730}
3731
3732
3733/**
3734 * Search memory, worker for the 's' and 's?' functions.
3735 *
3736 * @returns VBox status.
3737 * @param pCmdHlp Pointer to the command helper functions.
3738 * @param pUVM The user mode VM handle.
3739 * @param pAddress Where to start searching. If no range, search till end of address space.
3740 * @param cMaxHits The maximum number of hits.
3741 * @param chType The search type.
3742 * @param paPatArgs The pattern variable array.
3743 * @param cPatArgs Number of pattern variables.
3744 * @param pResult Where to store the result of a function invocation.
3745 */
3746static int dbgcCmdWorkerSearchMem(PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR pAddress, uint64_t cMaxHits, char chType,
3747 PCDBGCVAR paPatArgs, unsigned cPatArgs, PDBGCVAR pResult)
3748{
3749 if (pResult)
3750 DBGCVAR_INIT_GC_FLAT(pResult, 0);
3751
3752 /*
3753 * Convert the search pattern into bytes and DBGFR3MemScan can deal with.
3754 */
3755 uint32_t cbUnit;
3756 switch (chType)
3757 {
3758 case 'a':
3759 case 'b': cbUnit = 1; break;
3760 case 'u': cbUnit = 2 | RT_BIT_32(31); break;
3761 case 'w': cbUnit = 2; break;
3762 case 'd': cbUnit = 4; break;
3763 case 'q': cbUnit = 8; break;
3764 default:
3765 return pCmdHlp->pfnVBoxError(pCmdHlp, VERR_INVALID_PARAMETER, "chType=%c\n", chType);
3766 }
3767 uint8_t abBytes[RT_SIZEOFMEMB(DBGC, abSearch)];
3768 uint32_t cbBytes = sizeof(abBytes);
3769 int rc = dbgcVarsToBytes(pCmdHlp, abBytes, &cbBytes, cbUnit, paPatArgs, cPatArgs);
3770 if (RT_FAILURE(rc))
3771 return VERR_DBGC_COMMAND_FAILED;
3772
3773 /*
3774 * Make DBGF address and fix the range.
3775 */
3776 DBGFADDRESS Address;
3777 rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, pAddress, &Address);
3778 if (RT_FAILURE(rc))
3779 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "VarToDbgfAddr(,%Dv,)\n", pAddress);
3780
3781 RTGCUINTPTR cbRange;
3782 switch (pAddress->enmRangeType)
3783 {
3784 case DBGCVAR_RANGE_BYTES:
3785 cbRange = pAddress->u64Range;
3786 if (cbRange != pAddress->u64Range)
3787 cbRange = ~(RTGCUINTPTR)0;
3788 break;
3789
3790 case DBGCVAR_RANGE_ELEMENTS:
3791 cbRange = (RTGCUINTPTR)(pAddress->u64Range * cbUnit);
3792 if ( cbRange != pAddress->u64Range * cbUnit
3793 || cbRange < pAddress->u64Range)
3794 cbRange = ~(RTGCUINTPTR)0;
3795 break;
3796
3797 default:
3798 cbRange = ~(RTGCUINTPTR)0;
3799 break;
3800 }
3801 if (Address.FlatPtr + cbRange < Address.FlatPtr)
3802 cbRange = ~(RTGCUINTPTR)0 - Address.FlatPtr + !!Address.FlatPtr;
3803
3804 /*
3805 * Ok, do it.
3806 */
3807 return dbgcCmdWorkerSearchMemDoIt(pCmdHlp, pUVM, &Address, cbRange, abBytes, cbBytes, cbUnit, cMaxHits, pResult);
3808}
3809
3810
3811/**
3812 * @interface_method_impl{FNDBCCMD, The 's' command.}
3813 */
3814static DECLCALLBACK(int) dbgcCmdSearchMem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3815{
3816 /* check that the parser did what it's supposed to do. */
3817 //if ( cArgs <= 2
3818 // && paArgs[0].enmType != DBGCVAR_TYPE_STRING)
3819 // return DBGCCmdHlpPrintf(pCmdHlp, "parser error\n");
3820
3821 /*
3822 * Repeat previous search?
3823 */
3824 if (cArgs == 0)
3825 return dbgcCmdWorkerSearchMemResume(pCmdHlp, pUVM, NULL);
3826
3827 /*
3828 * Parse arguments.
3829 */
3830
3831 return -1;
3832}
3833
3834
3835/**
3836 * @interface_method_impl{FNDBCCMD, The 's?' command.}
3837 */
3838static DECLCALLBACK(int) dbgcCmdSearchMemType(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3839{
3840 /* check that the parser did what it's supposed to do. */
3841 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs >= 2 && DBGCVAR_ISGCPOINTER(paArgs[0].enmType));
3842 return dbgcCmdWorkerSearchMem(pCmdHlp, pUVM, &paArgs[0], 25, pCmd->pszCmd[1], paArgs + 1, cArgs - 1, NULL);
3843}
3844
3845
3846/**
3847 * List near symbol.
3848 *
3849 * @returns VBox status code.
3850 * @param pCmdHlp Pointer to command helper functions.
3851 * @param pUVM The user mode VM handle.
3852 * @param pArg Pointer to the address or symbol to lookup.
3853 */
3854static int dbgcDoListNear(PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR pArg)
3855{
3856 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3857
3858 RTDBGSYMBOL Symbol;
3859 int rc;
3860 if (pArg->enmType == DBGCVAR_TYPE_SYMBOL)
3861 {
3862 /*
3863 * Lookup the symbol address.
3864 */
3865 rc = DBGFR3AsSymbolByName(pUVM, pDbgc->hDbgAs, pArg->u.pszString, &Symbol, NULL);
3866 if (RT_FAILURE(rc))
3867 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3AsSymbolByName(,,%s,)\n", pArg->u.pszString);
3868
3869 rc = DBGCCmdHlpPrintf(pCmdHlp, "%RTptr %s\n", Symbol.Value, Symbol.szName);
3870 }
3871 else
3872 {
3873 /*
3874 * Convert it to a flat GC address and lookup that address.
3875 */
3876 DBGCVAR AddrVar;
3877 rc = DBGCCmdHlpEval(pCmdHlp, &AddrVar, "%%(%DV)", pArg);
3878 if (RT_FAILURE(rc))
3879 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "%%(%DV)\n", pArg);
3880
3881 RTINTPTR offDisp;
3882 DBGFADDRESS Addr;
3883 rc = DBGFR3AsSymbolByAddr(pUVM, pDbgc->hDbgAs, DBGFR3AddrFromFlat(pDbgc->pUVM, &Addr, AddrVar.u.GCFlat),
3884 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offDisp, &Symbol, NULL);
3885 if (RT_FAILURE(rc))
3886 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3ASymbolByAddr(,,%RGv,,)\n", AddrVar.u.GCFlat);
3887
3888 if (!offDisp)
3889 rc = DBGCCmdHlpPrintf(pCmdHlp, "%DV %s", &AddrVar, Symbol.szName);
3890 else if (offDisp > 0)
3891 rc = DBGCCmdHlpPrintf(pCmdHlp, "%DV %s + %RGv", &AddrVar, Symbol.szName, offDisp);
3892 else
3893 rc = DBGCCmdHlpPrintf(pCmdHlp, "%DV %s - %RGv", &AddrVar, Symbol.szName, -offDisp);
3894 if ((RTGCINTPTR)Symbol.cb > -offDisp)
3895 rc = DBGCCmdHlpPrintf(pCmdHlp, " LB %RGv\n", Symbol.cb + offDisp);
3896 else
3897 rc = DBGCCmdHlpPrintf(pCmdHlp, "\n");
3898 }
3899
3900 return rc;
3901}
3902
3903
3904/**
3905 * @interface_method_impl{FNDBCCMD, The 'ln' (listnear) command.}
3906 */
3907static DECLCALLBACK(int) dbgcCmdListNear(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3908{
3909 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3910 if (!cArgs)
3911 {
3912 /*
3913 * Current cs:eip symbol.
3914 */
3915 DBGCVAR AddrVar;
3916 const char *pszFmtExpr = pDbgc->fRegCtxGuest ? "%%(cs:eip)" : "%%(.cs:.eip)";
3917 int rc = DBGCCmdHlpEval(pCmdHlp, &AddrVar, pszFmtExpr);
3918 if (RT_FAILURE(rc))
3919 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "%s\n", pszFmtExpr + 1);
3920 return dbgcDoListNear(pCmdHlp, pUVM, &AddrVar);
3921 }
3922
3923/** @todo Fix the darn parser, it's resolving symbols specified as arguments before we get in here. */
3924 /*
3925 * Iterate arguments.
3926 */
3927 for (unsigned iArg = 0; iArg < cArgs; iArg++)
3928 {
3929 int rc = dbgcDoListNear(pCmdHlp, pUVM, &paArgs[iArg]);
3930 if (RT_FAILURE(rc))
3931 return rc;
3932 }
3933
3934 NOREF(pCmd);
3935 return VINF_SUCCESS;
3936}
3937
3938
3939/**
3940 * Matches the module patters against a module name.
3941 *
3942 * @returns true if matching, otherwise false.
3943 * @param pszName The module name.
3944 * @param paArgs The module pattern argument list.
3945 * @param cArgs Number of arguments.
3946 */
3947static bool dbgcCmdListModuleMatch(const char *pszName, PCDBGCVAR paArgs, unsigned cArgs)
3948{
3949 for (uint32_t i = 0; i < cArgs; i++)
3950 if (RTStrSimplePatternMatch(paArgs[i].u.pszString, pszName))
3951 return true;
3952 return false;
3953}
3954
3955
3956/**
3957 * @interface_method_impl{FNDBCCMD, The 'ln' (list near) command.}
3958 */
3959static DECLCALLBACK(int) dbgcCmdListModules(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3960{
3961 bool const fMappings = pCmd->pszCmd[2] == 'o';
3962 bool const fVerbose = pCmd->pszCmd[strlen(pCmd->pszCmd) - 1] == 'v';
3963 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
3964
3965 /*
3966 * Iterate the modules in the current address space and print info about
3967 * those matching the input.
3968 */
3969 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, pDbgc->hDbgAs);
3970 uint32_t cMods = RTDbgAsModuleCount(hAs);
3971 for (uint32_t iMod = 0; iMod < cMods; iMod++)
3972 {
3973 RTDBGMOD hMod = RTDbgAsModuleByIndex(hAs, iMod);
3974 if (hMod != NIL_RTDBGMOD)
3975 {
3976 bool const fDeferred = RTDbgModIsDeferred(hMod);
3977 bool const fExports = RTDbgModIsExports(hMod);
3978 uint32_t const cSegs = fDeferred ? 1 : RTDbgModSegmentCount(hMod);
3979 const char * const pszName = RTDbgModName(hMod);
3980 const char * const pszImgFile = RTDbgModImageFile(hMod);
3981 const char * const pszImgFileUsed = RTDbgModImageFileUsed(hMod);
3982 const char * const pszDbgFile = RTDbgModDebugFile(hMod);
3983 if ( cArgs == 0
3984 || dbgcCmdListModuleMatch(pszName, paArgs, cArgs))
3985 {
3986 /*
3987 * Find the mapping with the lower address, preferring a full
3988 * image mapping, for the main line.
3989 */
3990 RTDBGASMAPINFO aMappings[128];
3991 uint32_t cMappings = RT_ELEMENTS(aMappings);
3992 int rc = RTDbgAsModuleQueryMapByIndex(hAs, iMod, &aMappings[0], &cMappings, 0 /*fFlags*/);
3993 if (RT_SUCCESS(rc))
3994 {
3995 bool fFull = false;
3996 RTUINTPTR uMin = RTUINTPTR_MAX;
3997 for (uint32_t iMap = 0; iMap < cMappings; iMap++)
3998 if ( aMappings[iMap].Address < uMin
3999 && ( !fFull
4000 || aMappings[iMap].iSeg == NIL_RTDBGSEGIDX))
4001 uMin = aMappings[iMap].Address;
4002 if (!fVerbose || !pszImgFile)
4003 DBGCCmdHlpPrintf(pCmdHlp, "%RGv %04x %s%s\n", (RTGCUINTPTR)uMin, cSegs, pszName,
4004 fExports ? " (exports)" : fDeferred ? " (deferred)" : "");
4005 else
4006 DBGCCmdHlpPrintf(pCmdHlp, "%RGv %04x %-12s %s%s\n", (RTGCUINTPTR)uMin, cSegs, pszName, pszImgFile,
4007 fExports ? " (exports)" : fDeferred ? " (deferred)" : "");
4008 if (fVerbose && pszImgFileUsed)
4009 DBGCCmdHlpPrintf(pCmdHlp, " Local image: %s\n", pszImgFileUsed);
4010 if (fVerbose && pszDbgFile)
4011 DBGCCmdHlpPrintf(pCmdHlp, " Debug file: %s\n", pszDbgFile);
4012
4013 if (fMappings)
4014 {
4015 /* sort by address first - not very efficient. */
4016 for (uint32_t i = 0; i + 1 < cMappings; i++)
4017 for (uint32_t j = i + 1; j < cMappings; j++)
4018 if (aMappings[j].Address < aMappings[i].Address)
4019 {
4020 RTDBGASMAPINFO Tmp = aMappings[j];
4021 aMappings[j] = aMappings[i];
4022 aMappings[i] = Tmp;
4023 }
4024
4025 /* print */
4026 if ( cMappings == 1
4027 && aMappings[0].iSeg == NIL_RTDBGSEGIDX
4028 && !fDeferred)
4029 {
4030 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4031 {
4032 RTDBGSEGMENT SegInfo;
4033 rc = RTDbgModSegmentByIndex(hMod, iSeg, &SegInfo);
4034 if (RT_SUCCESS(rc))
4035 {
4036 if (SegInfo.uRva != RTUINTPTR_MAX)
4037 DBGCCmdHlpPrintf(pCmdHlp, " %RGv %RGv #%02x %s\n",
4038 (RTGCUINTPTR)(aMappings[0].Address + SegInfo.uRva),
4039 (RTGCUINTPTR)SegInfo.cb, iSeg, SegInfo.szName);
4040 else
4041 DBGCCmdHlpPrintf(pCmdHlp, " %*s %RGv #%02x %s\n",
4042 sizeof(RTGCUINTPTR)*2, "noload",
4043 (RTGCUINTPTR)SegInfo.cb, iSeg, SegInfo.szName);
4044 }
4045 else
4046 DBGCCmdHlpPrintf(pCmdHlp, " Error query segment #%u: %Rrc\n", iSeg, rc);
4047 }
4048 }
4049 else
4050 {
4051 for (uint32_t iMap = 0; iMap < cMappings; iMap++)
4052 if (aMappings[iMap].iSeg == NIL_RTDBGSEGIDX)
4053 DBGCCmdHlpPrintf(pCmdHlp, " %RGv %RGv <everything>\n",
4054 (RTGCUINTPTR)aMappings[iMap].Address,
4055 (RTGCUINTPTR)RTDbgModImageSize(hMod));
4056 else if (!fDeferred)
4057 {
4058 RTDBGSEGMENT SegInfo;
4059 rc = RTDbgModSegmentByIndex(hMod, aMappings[iMap].iSeg, &SegInfo);
4060 if (RT_FAILURE(rc))
4061 {
4062 RT_ZERO(SegInfo);
4063 strcpy(SegInfo.szName, "error");
4064 }
4065 DBGCCmdHlpPrintf(pCmdHlp, " %RGv %RGv #%02x %s\n",
4066 (RTGCUINTPTR)aMappings[iMap].Address,
4067 (RTGCUINTPTR)SegInfo.cb,
4068 aMappings[iMap].iSeg, SegInfo.szName);
4069 }
4070 else
4071 DBGCCmdHlpPrintf(pCmdHlp, " %RGv #%02x\n",
4072 (RTGCUINTPTR)aMappings[iMap].Address, aMappings[iMap].iSeg);
4073 }
4074 }
4075 }
4076 else
4077 DBGCCmdHlpPrintf(pCmdHlp, "%.*s %04x %s (rc=%Rrc)\n",
4078 sizeof(RTGCPTR) * 2, "???????????", cSegs, pszName, rc);
4079 /** @todo missing address space API for enumerating the mappings. */
4080 }
4081 RTDbgModRelease(hMod);
4082 }
4083 }
4084 RTDbgAsRelease(hAs);
4085
4086 NOREF(pCmd);
4087 return VINF_SUCCESS;
4088}
4089
4090
4091
4092/**
4093 * @callback_method_impl{Reads a unsigned 8-bit value.}
4094 */
4095static DECLCALLBACK(int) dbgcFuncReadU8(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4096 PDBGCVAR pResult)
4097{
4098 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4099 AssertReturn(DBGCVAR_ISPOINTER(paArgs[0].enmType), VERR_DBGC_PARSE_BUG);
4100 AssertReturn(paArgs[0].enmRangeType == DBGCVAR_RANGE_NONE, VERR_DBGC_PARSE_BUG);
4101
4102 uint8_t b;
4103 int rc = DBGCCmdHlpMemRead(pCmdHlp, &b, sizeof(b), &paArgs[0], NULL);
4104 if (RT_FAILURE(rc))
4105 return rc;
4106 DBGCVAR_INIT_NUMBER(pResult, b);
4107
4108 NOREF(pFunc);
4109 return VINF_SUCCESS;
4110}
4111
4112
4113/**
4114 * @callback_method_impl{Reads a unsigned 16-bit value.}
4115 */
4116static DECLCALLBACK(int) dbgcFuncReadU16(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4117 PDBGCVAR pResult)
4118{
4119 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4120 AssertReturn(DBGCVAR_ISPOINTER(paArgs[0].enmType), VERR_DBGC_PARSE_BUG);
4121 AssertReturn(paArgs[0].enmRangeType == DBGCVAR_RANGE_NONE, VERR_DBGC_PARSE_BUG);
4122
4123 uint16_t u16;
4124 int rc = DBGCCmdHlpMemRead(pCmdHlp, &u16, sizeof(u16), &paArgs[0], NULL);
4125 if (RT_FAILURE(rc))
4126 return rc;
4127 DBGCVAR_INIT_NUMBER(pResult, u16);
4128
4129 NOREF(pFunc);
4130 return VINF_SUCCESS;
4131}
4132
4133
4134/**
4135 * @callback_method_impl{Reads a unsigned 32-bit value.}
4136 */
4137static DECLCALLBACK(int) dbgcFuncReadU32(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4138 PDBGCVAR pResult)
4139{
4140 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4141 AssertReturn(DBGCVAR_ISPOINTER(paArgs[0].enmType), VERR_DBGC_PARSE_BUG);
4142 AssertReturn(paArgs[0].enmRangeType == DBGCVAR_RANGE_NONE, VERR_DBGC_PARSE_BUG);
4143
4144 uint32_t u32;
4145 int rc = DBGCCmdHlpMemRead(pCmdHlp, &u32, sizeof(u32), &paArgs[0], NULL);
4146 if (RT_FAILURE(rc))
4147 return rc;
4148 DBGCVAR_INIT_NUMBER(pResult, u32);
4149
4150 NOREF(pFunc);
4151 return VINF_SUCCESS;
4152}
4153
4154
4155/**
4156 * @callback_method_impl{Reads a unsigned 64-bit value.}
4157 */
4158static DECLCALLBACK(int) dbgcFuncReadU64(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4159 PDBGCVAR pResult)
4160{
4161 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4162 AssertReturn(DBGCVAR_ISPOINTER(paArgs[0].enmType), VERR_DBGC_PARSE_BUG);
4163 AssertReturn(paArgs[0].enmRangeType == DBGCVAR_RANGE_NONE, VERR_DBGC_PARSE_BUG);
4164
4165 uint64_t u64;
4166 int rc = DBGCCmdHlpMemRead(pCmdHlp, &u64, sizeof(u64), &paArgs[0], NULL);
4167 if (RT_FAILURE(rc))
4168 return rc;
4169 DBGCVAR_INIT_NUMBER(pResult, u64);
4170
4171 NOREF(pFunc);
4172 return VINF_SUCCESS;
4173}
4174
4175
4176/**
4177 * @callback_method_impl{Reads a unsigned pointer-sized value.}
4178 */
4179static DECLCALLBACK(int) dbgcFuncReadPtr(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4180 PDBGCVAR pResult)
4181{
4182 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4183 AssertReturn(DBGCVAR_ISPOINTER(paArgs[0].enmType), VERR_DBGC_PARSE_BUG);
4184 AssertReturn(paArgs[0].enmRangeType == DBGCVAR_RANGE_NONE, VERR_DBGC_PARSE_BUG);
4185
4186 CPUMMODE enmMode = DBGCCmdHlpGetCpuMode(pCmdHlp);
4187 if (enmMode == CPUMMODE_LONG)
4188 return dbgcFuncReadU64(pFunc, pCmdHlp, pUVM, paArgs, cArgs, pResult);
4189 return dbgcFuncReadU32(pFunc, pCmdHlp, pUVM, paArgs, cArgs, pResult);
4190}
4191
4192
4193/**
4194 * @callback_method_impl{The hi(value) function implementation.}
4195 */
4196static DECLCALLBACK(int) dbgcFuncHi(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4197 PDBGCVAR pResult)
4198{
4199 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4200
4201 uint16_t uHi;
4202 switch (paArgs[0].enmType)
4203 {
4204 case DBGCVAR_TYPE_GC_FLAT: uHi = (uint16_t)(paArgs[0].u.GCFlat >> 16); break;
4205 case DBGCVAR_TYPE_GC_FAR: uHi = (uint16_t)paArgs[0].u.GCFar.sel; break;
4206 case DBGCVAR_TYPE_GC_PHYS: uHi = (uint16_t)(paArgs[0].u.GCPhys >> 16); break;
4207 case DBGCVAR_TYPE_HC_FLAT: uHi = (uint16_t)((uintptr_t)paArgs[0].u.pvHCFlat >> 16); break;
4208 case DBGCVAR_TYPE_HC_PHYS: uHi = (uint16_t)(paArgs[0].u.HCPhys >> 16); break;
4209 case DBGCVAR_TYPE_NUMBER: uHi = (uint16_t)(paArgs[0].u.u64Number >> 16); break;
4210 default:
4211 AssertFailedReturn(VERR_DBGC_PARSE_BUG);
4212 }
4213 DBGCVAR_INIT_NUMBER(pResult, uHi);
4214 DBGCVAR_SET_RANGE(pResult, paArgs[0].enmRangeType, paArgs[0].u64Range);
4215
4216 NOREF(pFunc); NOREF(pCmdHlp); NOREF(pUVM);
4217 return VINF_SUCCESS;
4218}
4219
4220
4221/**
4222 * @callback_method_impl{The low(value) function implementation.}
4223 */
4224static DECLCALLBACK(int) dbgcFuncLow(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4225 PDBGCVAR pResult)
4226{
4227 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4228
4229 uint16_t uLow;
4230 switch (paArgs[0].enmType)
4231 {
4232 case DBGCVAR_TYPE_GC_FLAT: uLow = (uint16_t)paArgs[0].u.GCFlat; break;
4233 case DBGCVAR_TYPE_GC_FAR: uLow = (uint16_t)paArgs[0].u.GCFar.off; break;
4234 case DBGCVAR_TYPE_GC_PHYS: uLow = (uint16_t)paArgs[0].u.GCPhys; break;
4235 case DBGCVAR_TYPE_HC_FLAT: uLow = (uint16_t)(uintptr_t)paArgs[0].u.pvHCFlat; break;
4236 case DBGCVAR_TYPE_HC_PHYS: uLow = (uint16_t)paArgs[0].u.HCPhys; break;
4237 case DBGCVAR_TYPE_NUMBER: uLow = (uint16_t)paArgs[0].u.u64Number; break;
4238 default:
4239 AssertFailedReturn(VERR_DBGC_PARSE_BUG);
4240 }
4241 DBGCVAR_INIT_NUMBER(pResult, uLow);
4242 DBGCVAR_SET_RANGE(pResult, paArgs[0].enmRangeType, paArgs[0].u64Range);
4243
4244 NOREF(pFunc); NOREF(pCmdHlp); NOREF(pUVM);
4245 return VINF_SUCCESS;
4246}
4247
4248
4249/**
4250 * @callback_method_impl{The low(value) function implementation.}
4251 */
4252static DECLCALLBACK(int) dbgcFuncNot(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
4253 PDBGCVAR pResult)
4254{
4255 AssertReturn(cArgs == 1, VERR_DBGC_PARSE_BUG);
4256 NOREF(pFunc); NOREF(pCmdHlp); NOREF(pUVM);
4257 return DBGCCmdHlpEval(pCmdHlp, pResult, "!(%Dv)", &paArgs[0]);
4258}
4259
4260
4261/** Generic pointer argument wo/ range. */
4262static const DBGCVARDESC g_aArgPointerWoRange[] =
4263{
4264 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
4265 { 1, 1, DBGCVAR_CAT_POINTER_NO_RANGE, 0, "value", "Address or number." },
4266};
4267
4268/** Generic pointer or number argument. */
4269static const DBGCVARDESC g_aArgPointerNumber[] =
4270{
4271 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
4272 { 1, 1, DBGCVAR_CAT_POINTER_NUMBER, 0, "value", "Address or number." },
4273};
4274
4275
4276
4277/** Function descriptors for the CodeView / WinDbg emulation.
4278 * The emulation isn't attempting to be identical, only somewhat similar.
4279 */
4280const DBGCFUNC g_aFuncsCodeView[] =
4281{
4282 { "by", 1, 1, &g_aArgPointerWoRange[0], RT_ELEMENTS(g_aArgPointerWoRange), 0, dbgcFuncReadU8, "address", "Reads a byte at the given address." },
4283 { "dwo", 1, 1, &g_aArgPointerWoRange[0], RT_ELEMENTS(g_aArgPointerWoRange), 0, dbgcFuncReadU32, "address", "Reads a 32-bit value at the given address." },
4284 { "hi", 1, 1, &g_aArgPointerNumber[0], RT_ELEMENTS(g_aArgPointerNumber), 0, dbgcFuncHi, "value", "Returns the high 16-bit bits of a value." },
4285 { "low", 1, 1, &g_aArgPointerNumber[0], RT_ELEMENTS(g_aArgPointerNumber), 0, dbgcFuncLow, "value", "Returns the low 16-bit bits of a value." },
4286 { "not", 1, 1, &g_aArgPointerNumber[0], RT_ELEMENTS(g_aArgPointerNumber), 0, dbgcFuncNot, "address", "Boolean NOT." },
4287 { "poi", 1, 1, &g_aArgPointerWoRange[0], RT_ELEMENTS(g_aArgPointerWoRange), 0, dbgcFuncReadPtr, "address", "Reads a pointer sized (CS) value at the given address." },
4288 { "qwo", 1, 1, &g_aArgPointerWoRange[0], RT_ELEMENTS(g_aArgPointerWoRange), 0, dbgcFuncReadU64, "address", "Reads a 32-bit value at the given address." },
4289 { "wo", 1, 1, &g_aArgPointerWoRange[0], RT_ELEMENTS(g_aArgPointerWoRange), 0, dbgcFuncReadU16, "address", "Reads a 16-bit value at the given address." },
4290};
4291
4292/** The number of functions in the CodeView/WinDbg emulation. */
4293const uint32_t g_cFuncsCodeView = RT_ELEMENTS(g_aFuncsCodeView);
4294
Note: See TracBrowser for help on using the repository browser.

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