1 /*
2 * ReactOS kernel
3 * Copyright (C) 2005 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/kdbg/kdb_cli.c
22 * PURPOSE: Kernel debugger command line interface
23 * PROGRAMMER: Gregor Anich (blight@blight.eu.org)
24 * Hervé Poussineau
25 * UPDATE HISTORY:
26 * Created 16/01/2005
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <ntoskrnl.h>
32
33 #include "kdb.h"
34 #include "../kd/kdterminal.h"
35
36 #define NDEBUG
37 #include "debug.h"
38
39 /* DEFINES *******************************************************************/
40
41 #define KDB_ENTER_CONDITION_TO_STRING(cond) \
42 ((cond) == KdbDoNotEnter ? "never" : \
43 ((cond) == KdbEnterAlways ? "always" : \
44 ((cond) == KdbEnterFromKmode ? "kmode" : "umode")))
45
46 #define KDB_ACCESS_TYPE_TO_STRING(type) \
47 ((type) == KdbAccessRead ? "read" : \
48 ((type) == KdbAccessWrite ? "write" : \
49 ((type) == KdbAccessReadWrite ? "rdwr" : "exec")))
50
51 #define NPX_STATE_TO_STRING(state) \
52 ((state) == NPX_STATE_LOADED ? "Loaded" : \
53 ((state) == NPX_STATE_NOT_LOADED ? "Not loaded" : "Unknown"))
54
55 /* PROTOTYPES ****************************************************************/
56
57 static BOOLEAN KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]);
58 static BOOLEAN KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[]);
59 static BOOLEAN KdbpCmdRegs(ULONG Argc, PCHAR Argv[]);
60 static BOOLEAN KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]);
61
62 static BOOLEAN KdbpCmdContinue(ULONG Argc, PCHAR Argv[]);
63 static BOOLEAN KdbpCmdStep(ULONG Argc, PCHAR Argv[]);
64 static BOOLEAN KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[]);
65 static BOOLEAN KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[]);
66 static BOOLEAN KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]);
67
68 static BOOLEAN KdbpCmdThread(ULONG Argc, PCHAR Argv[]);
69 static BOOLEAN KdbpCmdProc(ULONG Argc, PCHAR Argv[]);
70
71 static BOOLEAN KdbpCmdMod(ULONG Argc, PCHAR Argv[]);
72 static BOOLEAN KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]);
73 static BOOLEAN KdbpCmdPcr(ULONG Argc, PCHAR Argv[]);
74 #ifdef _M_IX86
75 static BOOLEAN KdbpCmdTss(ULONG Argc, PCHAR Argv[]);
76 #endif
77
78 static BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]);
79 static BOOLEAN KdbpCmdReboot(ULONG Argc, PCHAR Argv[]);
80 static BOOLEAN KdbpCmdFilter(ULONG Argc, PCHAR Argv[]);
81 static BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
82 static BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
83 static BOOLEAN KdbpCmdDmesg(ULONG Argc, PCHAR Argv[]);
84
85 BOOLEAN ExpKdbgExtPool(ULONG Argc, PCHAR Argv[]);
86 BOOLEAN ExpKdbgExtPoolUsed(ULONG Argc, PCHAR Argv[]);
87 BOOLEAN ExpKdbgExtPoolFind(ULONG Argc, PCHAR Argv[]);
88 BOOLEAN ExpKdbgExtFileCache(ULONG Argc, PCHAR Argv[]);
89 BOOLEAN ExpKdbgExtDefWrites(ULONG Argc, PCHAR Argv[]);
90 BOOLEAN ExpKdbgExtIrpFind(ULONG Argc, PCHAR Argv[]);
91 BOOLEAN ExpKdbgExtHandle(ULONG Argc, PCHAR Argv[]);
92
93 extern char __ImageBase;
94
95 #ifdef __ROS_DWARF__
96 static BOOLEAN KdbpCmdPrintStruct(ULONG Argc, PCHAR Argv[]);
97 #endif
98
99 /* Be more descriptive than intrinsics */
100 #ifndef Ke386GetGlobalDescriptorTable
101 # define Ke386GetGlobalDescriptorTable __sgdt
102 #endif
103 #ifndef Ke386GetLocalDescriptorTable
104 # define Ke386GetLocalDescriptorTable __sldt
105 #endif
106
107 /* Portability */
108 FORCEINLINE
109 ULONG_PTR
strtoulptr(const char * nptr,char ** endptr,int base)110 strtoulptr(const char* nptr, char** endptr, int base)
111 {
112 #ifdef _M_IX86
113 return strtoul(nptr, endptr, base);
114 #else
115 return strtoull(nptr, endptr, base);
116 #endif
117 }
118
119 /* GLOBALS *******************************************************************/
120
121 typedef
122 BOOLEAN
123 (NTAPI *PKDBG_CLI_ROUTINE)(
124 IN PCHAR Command,
125 IN ULONG Argc,
126 IN PCH Argv[]);
127
128 static PKDBG_CLI_ROUTINE KdbCliCallbacks[10];
129 static BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */
130 static BOOLEAN KdbBreakOnModuleLoad = FALSE; /* Set to TRUE to break into KDB when a module is loaded */
131
132 static ULONG KdbNumberOfRowsPrinted = 0;
133 static ULONG KdbNumberOfColsPrinted = 0;
134 static BOOLEAN KdbOutputAborted = FALSE;
135 static BOOLEAN KdbRepeatLastCommand = FALSE;
136
137 volatile PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */
138 BOOLEAN KdbpBugCheckRequested = FALSE;
139
140 /* Variables for Dmesg */
141 static const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024;
142 static PCHAR KdpDmesgBuffer = NULL;
143 static volatile ULONG KdpDmesgCurrentPosition = 0;
144 static volatile ULONG KdpDmesgFreeBytes = 0;
145 static volatile ULONG KdbDmesgTotalWritten = 0;
146 static volatile BOOLEAN KdbpIsInDmesgMode = FALSE;
147 static KSPIN_LOCK KdpDmesgLogSpinLock;
148
149 const CSTRING KdbPromptStr = RTL_CONSTANT_STRING("kdb:> ");
150
151 //
152 // Debug Filter Component Table
153 //
154 #define KD_DEBUG_PRINT_FILTER(Name) \
155 { #Name, DPFLTR_##Name##_ID }
156
157 static struct
158 {
159 PCSTR Name;
160 ULONG Id;
161 }
162 ComponentTable[] =
163 {
164 //
165 // Default components
166 //
167 { "WIN2000", MAXULONG },
168 KD_DEBUG_PRINT_FILTER(DEFAULT),
169 //
170 // Standard components
171 //
172 KD_DEBUG_PRINT_FILTER(SYSTEM),
173 KD_DEBUG_PRINT_FILTER(SMSS),
174 KD_DEBUG_PRINT_FILTER(SETUP),
175 KD_DEBUG_PRINT_FILTER(NTFS),
176 KD_DEBUG_PRINT_FILTER(FSTUB),
177 KD_DEBUG_PRINT_FILTER(CRASHDUMP),
178 KD_DEBUG_PRINT_FILTER(CDAUDIO),
179 KD_DEBUG_PRINT_FILTER(CDROM),
180 KD_DEBUG_PRINT_FILTER(CLASSPNP),
181 KD_DEBUG_PRINT_FILTER(DISK),
182 KD_DEBUG_PRINT_FILTER(REDBOOK),
183 KD_DEBUG_PRINT_FILTER(STORPROP),
184 KD_DEBUG_PRINT_FILTER(SCSIPORT),
185 KD_DEBUG_PRINT_FILTER(SCSIMINIPORT),
186 KD_DEBUG_PRINT_FILTER(CONFIG),
187 KD_DEBUG_PRINT_FILTER(I8042PRT),
188 KD_DEBUG_PRINT_FILTER(SERMOUSE),
189 KD_DEBUG_PRINT_FILTER(LSERMOUS),
190 KD_DEBUG_PRINT_FILTER(KBDHID),
191 KD_DEBUG_PRINT_FILTER(MOUHID),
192 KD_DEBUG_PRINT_FILTER(KBDCLASS),
193 KD_DEBUG_PRINT_FILTER(MOUCLASS),
194 KD_DEBUG_PRINT_FILTER(TWOTRACK),
195 KD_DEBUG_PRINT_FILTER(WMILIB),
196 KD_DEBUG_PRINT_FILTER(ACPI),
197 KD_DEBUG_PRINT_FILTER(AMLI),
198 KD_DEBUG_PRINT_FILTER(HALIA64),
199 KD_DEBUG_PRINT_FILTER(VIDEO),
200 KD_DEBUG_PRINT_FILTER(SVCHOST),
201 KD_DEBUG_PRINT_FILTER(VIDEOPRT),
202 KD_DEBUG_PRINT_FILTER(TCPIP),
203 KD_DEBUG_PRINT_FILTER(DMSYNTH),
204 KD_DEBUG_PRINT_FILTER(NTOSPNP),
205 KD_DEBUG_PRINT_FILTER(FASTFAT),
206 KD_DEBUG_PRINT_FILTER(SAMSS),
207 KD_DEBUG_PRINT_FILTER(PNPMGR),
208 KD_DEBUG_PRINT_FILTER(NETAPI),
209 KD_DEBUG_PRINT_FILTER(SCSERVER),
210 KD_DEBUG_PRINT_FILTER(SCCLIENT),
211 KD_DEBUG_PRINT_FILTER(SERIAL),
212 KD_DEBUG_PRINT_FILTER(SERENUM),
213 KD_DEBUG_PRINT_FILTER(UHCD),
214 KD_DEBUG_PRINT_FILTER(RPCPROXY),
215 KD_DEBUG_PRINT_FILTER(AUTOCHK),
216 KD_DEBUG_PRINT_FILTER(DCOMSS),
217 KD_DEBUG_PRINT_FILTER(UNIMODEM),
218 KD_DEBUG_PRINT_FILTER(SIS),
219 KD_DEBUG_PRINT_FILTER(FLTMGR),
220 KD_DEBUG_PRINT_FILTER(WMICORE),
221 KD_DEBUG_PRINT_FILTER(BURNENG),
222 KD_DEBUG_PRINT_FILTER(IMAPI),
223 KD_DEBUG_PRINT_FILTER(SXS),
224 KD_DEBUG_PRINT_FILTER(FUSION),
225 KD_DEBUG_PRINT_FILTER(IDLETASK),
226 KD_DEBUG_PRINT_FILTER(SOFTPCI),
227 KD_DEBUG_PRINT_FILTER(TAPE),
228 KD_DEBUG_PRINT_FILTER(MCHGR),
229 KD_DEBUG_PRINT_FILTER(IDEP),
230 KD_DEBUG_PRINT_FILTER(PCIIDE),
231 KD_DEBUG_PRINT_FILTER(FLOPPY),
232 KD_DEBUG_PRINT_FILTER(FDC),
233 KD_DEBUG_PRINT_FILTER(TERMSRV),
234 KD_DEBUG_PRINT_FILTER(W32TIME),
235 KD_DEBUG_PRINT_FILTER(PREFETCHER),
236 KD_DEBUG_PRINT_FILTER(RSFILTER),
237 KD_DEBUG_PRINT_FILTER(FCPORT),
238 KD_DEBUG_PRINT_FILTER(PCI),
239 KD_DEBUG_PRINT_FILTER(DMIO),
240 KD_DEBUG_PRINT_FILTER(DMCONFIG),
241 KD_DEBUG_PRINT_FILTER(DMADMIN),
242 KD_DEBUG_PRINT_FILTER(WSOCKTRANSPORT),
243 KD_DEBUG_PRINT_FILTER(VSS),
244 KD_DEBUG_PRINT_FILTER(PNPMEM),
245 KD_DEBUG_PRINT_FILTER(PROCESSOR),
246 KD_DEBUG_PRINT_FILTER(DMSERVER),
247 KD_DEBUG_PRINT_FILTER(SR),
248 KD_DEBUG_PRINT_FILTER(INFINIBAND),
249 KD_DEBUG_PRINT_FILTER(IHVDRIVER),
250 KD_DEBUG_PRINT_FILTER(IHVVIDEO),
251 KD_DEBUG_PRINT_FILTER(IHVAUDIO),
252 KD_DEBUG_PRINT_FILTER(IHVNETWORK),
253 KD_DEBUG_PRINT_FILTER(IHVSTREAMING),
254 KD_DEBUG_PRINT_FILTER(IHVBUS),
255 KD_DEBUG_PRINT_FILTER(HPS),
256 KD_DEBUG_PRINT_FILTER(RTLTHREADPOOL),
257 KD_DEBUG_PRINT_FILTER(LDR),
258 KD_DEBUG_PRINT_FILTER(TCPIP6),
259 KD_DEBUG_PRINT_FILTER(ISAPNP),
260 KD_DEBUG_PRINT_FILTER(SHPC),
261 KD_DEBUG_PRINT_FILTER(STORPORT),
262 KD_DEBUG_PRINT_FILTER(STORMINIPORT),
263 KD_DEBUG_PRINT_FILTER(PRINTSPOOLER),
264 KD_DEBUG_PRINT_FILTER(VSSDYNDISK),
265 KD_DEBUG_PRINT_FILTER(VERIFIER),
266 KD_DEBUG_PRINT_FILTER(VDS),
267 KD_DEBUG_PRINT_FILTER(VDSBAS),
268 KD_DEBUG_PRINT_FILTER(VDSDYN), // Specified in Vista+
269 KD_DEBUG_PRINT_FILTER(VDSDYNDR),
270 KD_DEBUG_PRINT_FILTER(VDSLDR), // Specified in Vista+
271 KD_DEBUG_PRINT_FILTER(VDSUTIL),
272 KD_DEBUG_PRINT_FILTER(DFRGIFC),
273 KD_DEBUG_PRINT_FILTER(MM),
274 KD_DEBUG_PRINT_FILTER(DFSC),
275 KD_DEBUG_PRINT_FILTER(WOW64),
276 //
277 // Components specified in Vista+, some of which we also use in ReactOS
278 //
279 KD_DEBUG_PRINT_FILTER(ALPC),
280 KD_DEBUG_PRINT_FILTER(WDI),
281 KD_DEBUG_PRINT_FILTER(PERFLIB),
282 KD_DEBUG_PRINT_FILTER(KTM),
283 KD_DEBUG_PRINT_FILTER(IOSTRESS),
284 KD_DEBUG_PRINT_FILTER(HEAP),
285 KD_DEBUG_PRINT_FILTER(WHEA),
286 KD_DEBUG_PRINT_FILTER(USERGDI),
287 KD_DEBUG_PRINT_FILTER(MMCSS),
288 KD_DEBUG_PRINT_FILTER(TPM),
289 KD_DEBUG_PRINT_FILTER(THREADORDER),
290 KD_DEBUG_PRINT_FILTER(ENVIRON),
291 KD_DEBUG_PRINT_FILTER(EMS),
292 KD_DEBUG_PRINT_FILTER(WDT),
293 KD_DEBUG_PRINT_FILTER(FVEVOL),
294 KD_DEBUG_PRINT_FILTER(NDIS),
295 KD_DEBUG_PRINT_FILTER(NVCTRACE),
296 KD_DEBUG_PRINT_FILTER(LUAFV),
297 KD_DEBUG_PRINT_FILTER(APPCOMPAT),
298 KD_DEBUG_PRINT_FILTER(USBSTOR),
299 KD_DEBUG_PRINT_FILTER(SBP2PORT),
300 KD_DEBUG_PRINT_FILTER(COVERAGE),
301 KD_DEBUG_PRINT_FILTER(CACHEMGR),
302 KD_DEBUG_PRINT_FILTER(MOUNTMGR),
303 KD_DEBUG_PRINT_FILTER(CFR),
304 KD_DEBUG_PRINT_FILTER(TXF),
305 KD_DEBUG_PRINT_FILTER(KSECDD),
306 KD_DEBUG_PRINT_FILTER(FLTREGRESS),
307 KD_DEBUG_PRINT_FILTER(MPIO),
308 KD_DEBUG_PRINT_FILTER(MSDSM),
309 KD_DEBUG_PRINT_FILTER(UDFS),
310 KD_DEBUG_PRINT_FILTER(PSHED),
311 KD_DEBUG_PRINT_FILTER(STORVSP),
312 KD_DEBUG_PRINT_FILTER(LSASS),
313 KD_DEBUG_PRINT_FILTER(SSPICLI),
314 KD_DEBUG_PRINT_FILTER(CNG),
315 KD_DEBUG_PRINT_FILTER(EXFAT),
316 KD_DEBUG_PRINT_FILTER(FILETRACE),
317 KD_DEBUG_PRINT_FILTER(XSAVE),
318 KD_DEBUG_PRINT_FILTER(SE),
319 KD_DEBUG_PRINT_FILTER(DRIVEEXTENDER),
320 //
321 // Components specified in Windows 8
322 //
323 KD_DEBUG_PRINT_FILTER(POWER),
324 KD_DEBUG_PRINT_FILTER(CRASHDUMPXHCI),
325 KD_DEBUG_PRINT_FILTER(GPIO),
326 KD_DEBUG_PRINT_FILTER(REFS),
327 KD_DEBUG_PRINT_FILTER(WER),
328 //
329 // Components specified in Windows 10
330 //
331 KD_DEBUG_PRINT_FILTER(CAPIMG),
332 KD_DEBUG_PRINT_FILTER(VPCI),
333 KD_DEBUG_PRINT_FILTER(STORAGECLASSMEMORY),
334 KD_DEBUG_PRINT_FILTER(FSLIB),
335 };
336 #undef KD_DEBUG_PRINT_FILTER
337
338 //
339 // Command Table
340 //
341 static const struct
342 {
343 PCHAR Name;
344 PCHAR Syntax;
345 PCHAR Help;
346 BOOLEAN (*Fn)(ULONG Argc, PCHAR Argv[]);
347 } KdbDebuggerCommands[] = {
348 /* Data */
349 { NULL, NULL, "Data", NULL },
350 { "?", "? expression", "Evaluate expression.", KdbpCmdEvalExpression },
351 #ifdef _M_IX86 // FIXME: this is broken on x64
352 { "disasm", "disasm [address] [L count]", "Disassemble count instructions at address.", KdbpCmdDisassembleX },
353 #endif // _M_IX86
354 { "x", "x [address] [L count]", "Display count dwords, starting at address.", KdbpCmdDisassembleX },
355 { "regs", "regs", "Display general purpose registers.", KdbpCmdRegs },
356 { "sregs", "sregs", "Display status registers.", KdbpCmdRegs },
357 { "dregs", "dregs", "Display debug registers.", KdbpCmdRegs },
358 { "bt", "bt [*frameaddr|thread id]", "Prints current backtrace or from given frame address.", KdbpCmdBackTrace },
359 #ifdef __ROS_DWARF__
360 { "dt", "dt [mod] [type] [addr]", "Print a struct. The address is optional.", KdbpCmdPrintStruct },
361 #endif
362 /* Flow control */
363 { NULL, NULL, "Flow control", NULL },
364 { "cont", "cont", "Continue execution (leave debugger).", KdbpCmdContinue },
365 { "step", "step [count]", "Execute single instructions, stepping into interrupts.", KdbpCmdStep },
366 { "next", "next [count]", "Execute single instructions, skipping calls and reps.", KdbpCmdStep },
367 { "bl", "bl", "List breakpoints.", KdbpCmdBreakPointList },
368 { "be", "be [breakpoint]", "Enable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
369 { "bd", "bd [breakpoint]", "Disable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
370 { "bc", "bc [breakpoint]", "Clear breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
371 { "bpx", "bpx [address] [IF condition]", "Set software execution breakpoint at address.", KdbpCmdBreakPoint },
372 { "bpm", "bpm [r|w|rw|x] [byte|word|dword] [address] [IF condition]", "Set memory breakpoint at address.", KdbpCmdBreakPoint },
373
374 /* Process/Thread */
375 { NULL, NULL, "Process/Thread", NULL },
376 { "thread", "thread [list[ pid]|[attach ]tid]", "List threads in current or specified process, display thread with given id or attach to thread.", KdbpCmdThread },
377 { "proc", "proc [list|[attach ]pid]", "List processes, display process with given id or attach to process.", KdbpCmdProc },
378
379 /* System information */
380 { NULL, NULL, "System info", NULL },
381 { "mod", "mod [address]", "List all modules or the one containing address.", KdbpCmdMod },
382 { "gdt", "gdt", "Display the global descriptor table.", KdbpCmdGdtLdtIdt },
383 { "ldt", "ldt", "Display the local descriptor table.", KdbpCmdGdtLdtIdt },
384 { "idt", "idt", "Display the interrupt descriptor table.", KdbpCmdGdtLdtIdt },
385 { "pcr", "pcr", "Display the processor control region.", KdbpCmdPcr },
386 #ifdef _M_IX86
387 { "tss", "tss [selector|*descaddr]", "Display the current task state segment, or the one specified by its selector number or descriptor address.", KdbpCmdTss },
388 #endif
389
390 /* Others */
391 { NULL, NULL, "Others", NULL },
392 { "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck },
393 { "reboot", "reboot", "Reboots the system.", KdbpCmdReboot},
394 { "filter", "filter [error|warning|trace|info|level]+|-[componentname|default]", "Enable/disable debug channels.", KdbpCmdFilter },
395 { "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet },
396 { "dmesg", "dmesg", "Display debug messages on screen, with navigation on pages.", KdbpCmdDmesg },
397 { "kmsg", "kmsg", "Kernel dmesg. Alias for dmesg.", KdbpCmdDmesg },
398 { "help", "help", "Display help screen.", KdbpCmdHelp },
399 { "!pool", "!pool [Address [Flags]]", "Display information about pool allocations.", ExpKdbgExtPool },
400 { "!poolused", "!poolused [Flags [Tag]]", "Display pool usage.", ExpKdbgExtPoolUsed },
401 { "!poolfind", "!poolfind Tag [Pool]", "Search for pool tag allocations.", ExpKdbgExtPoolFind },
402 { "!filecache", "!filecache", "Display cache usage.", ExpKdbgExtFileCache },
403 { "!defwrites", "!defwrites", "Display cache write values.", ExpKdbgExtDefWrites },
404 { "!irpfind", "!irpfind [Pool [startaddress [criteria data]]]", "Lists IRPs potentially matching criteria.", ExpKdbgExtIrpFind },
405 { "!handle", "!handle [Handle]", "Displays info about handles.", ExpKdbgExtHandle },
406 };
407
408 /* FUNCTIONS *****************************************************************/
409
410 /*!\brief Evaluates an expression...
411 *
412 * Much like KdbpRpnEvaluateExpression, but prints the error message (if any)
413 * at the given offset.
414 *
415 * \param Expression Expression to evaluate.
416 * \param ErrOffset Offset (in characters) to print the error message at.
417 * \param Result Receives the result on success.
418 *
419 * \retval TRUE Success.
420 * \retval FALSE Failure.
421 */
422 static BOOLEAN
KdbpEvaluateExpression(IN PCHAR Expression,IN LONG ErrOffset,OUT PULONGLONG Result)423 KdbpEvaluateExpression(
424 IN PCHAR Expression,
425 IN LONG ErrOffset,
426 OUT PULONGLONG Result)
427 {
428 static CHAR ErrMsgBuffer[130] = "^ ";
429 LONG ExpressionErrOffset = -1;
430 PCHAR ErrMsg = ErrMsgBuffer;
431 BOOLEAN Ok;
432
433 Ok = KdbpRpnEvaluateExpression(Expression, KdbCurrentTrapFrame, Result,
434 &ExpressionErrOffset, ErrMsgBuffer + 2);
435 if (!Ok)
436 {
437 if (ExpressionErrOffset >= 0)
438 ExpressionErrOffset += ErrOffset;
439 else
440 ErrMsg += 2;
441
442 KdbpPrint("%*s%s\n", ExpressionErrOffset, "", ErrMsg);
443 }
444
445 return Ok;
446 }
447
448 BOOLEAN
449 NTAPI
KdbpGetHexNumber(IN PCHAR pszNum,OUT ULONG_PTR * pulValue)450 KdbpGetHexNumber(
451 IN PCHAR pszNum,
452 OUT ULONG_PTR *pulValue)
453 {
454 char *endptr;
455
456 /* Skip optional '0x' prefix */
457 if ((pszNum[0] == '0') && ((pszNum[1] == 'x') || (pszNum[1] == 'X')))
458 pszNum += 2;
459
460 /* Make a number from the string (hex) */
461 *pulValue = strtoul(pszNum, &endptr, 16);
462
463 return (*endptr == '\0');
464 }
465
466 /*!\brief Evaluates an expression and displays the result.
467 */
468 static BOOLEAN
KdbpCmdEvalExpression(ULONG Argc,PCHAR Argv[])469 KdbpCmdEvalExpression(
470 ULONG Argc,
471 PCHAR Argv[])
472 {
473 ULONG i;
474 SIZE_T len;
475 ULONGLONG Result = 0;
476 ULONG ul;
477 LONG l = 0;
478 BOOLEAN Ok;
479
480 if (Argc < 2)
481 {
482 KdbpPrint("?: Argument required\n");
483 return TRUE;
484 }
485
486 /* Put the arguments back together */
487 Argc--;
488 for (i = 1; i < Argc; i++)
489 {
490 len = strlen(Argv[i]);
491 Argv[i][len] = ' ';
492 }
493
494 /* Evaluate the expression */
495 Ok = KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result);
496 if (Ok)
497 {
498 if (Result > 0x00000000ffffffffLL)
499 {
500 if (Result & 0x8000000000000000LL)
501 KdbpPrint("0x%016I64x %20I64u %20I64d\n", Result, Result, Result);
502 else
503 KdbpPrint("0x%016I64x %20I64u\n", Result, Result);
504 }
505 else
506 {
507 ul = (ULONG)Result;
508
509 if (ul <= 0xff && ul >= 0x80)
510 l = (LONG)((CHAR)ul);
511 else if (ul <= 0xffff && ul >= 0x8000)
512 l = (LONG)((SHORT)ul);
513 else
514 l = (LONG)ul;
515
516 if (l < 0)
517 KdbpPrint("0x%08lx %10lu %10ld\n", ul, ul, l);
518 else
519 KdbpPrint("0x%08lx %10lu\n", ul, ul);
520 }
521 }
522
523 return TRUE;
524 }
525
526 #ifdef __ROS_DWARF__
527
528 /*!\brief Print a struct
529 */
530 static VOID
KdbpPrintStructInternal(PROSSYM_INFO Info,PCHAR Indent,BOOLEAN DoRead,PVOID BaseAddress,PROSSYM_AGGREGATE Aggregate)531 KdbpPrintStructInternal
532 (PROSSYM_INFO Info,
533 PCHAR Indent,
534 BOOLEAN DoRead,
535 PVOID BaseAddress,
536 PROSSYM_AGGREGATE Aggregate)
537 {
538 ULONG i;
539 ULONGLONG Result;
540 PROSSYM_AGGREGATE_MEMBER Member;
541 ULONG IndentLen = strlen(Indent);
542 ROSSYM_AGGREGATE MemberAggregate = {0 };
543
544 for (i = 0; i < Aggregate->NumElements; i++) {
545 Member = &Aggregate->Elements[i];
546 KdbpPrint("%s%p+%x: %s", Indent, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size, Member->Name ? Member->Name : "<anoymous>");
547 if (DoRead) {
548 if (!strcmp(Member->Type, "_UNICODE_STRING")) {
549 KdbpPrint("\"");
550 KdbpPrintUnicodeString(((PCHAR)BaseAddress) + Member->BaseOffset);
551 KdbpPrint("\"\n");
552 continue;
553 } else if (!strcmp(Member->Type, "PUNICODE_STRING")) {
554 KdbpPrint("\"");
555 KdbpPrintUnicodeString(*(((PUNICODE_STRING*)((PCHAR)BaseAddress) + Member->BaseOffset)));
556 KdbpPrint("\"\n");
557 continue;
558 }
559 switch (Member->Size) {
560 case 1:
561 case 2:
562 case 4:
563 case 8: {
564 Result = 0;
565 if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
566 if (Member->Bits) {
567 Result >>= Member->FirstBit;
568 Result &= ((1 << Member->Bits) - 1);
569 }
570 KdbpPrint(" %lx\n", Result);
571 }
572 else goto readfail;
573 break;
574 }
575 default: {
576 if (Member->Size < 8) {
577 if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
578 ULONG j;
579 for (j = 0; j < Member->Size; j++) {
580 KdbpPrint(" %02x", (int)(Result & 0xff));
581 Result >>= 8;
582 }
583 } else goto readfail;
584 } else {
585 KdbpPrint(" %s @ %p {\n", Member->Type, ((PCHAR)BaseAddress) + Member->BaseOffset);
586 Indent[IndentLen] = ' ';
587 if (RosSymAggregate(Info, Member->Type, &MemberAggregate)) {
588 KdbpPrintStructInternal(Info, Indent, DoRead, ((PCHAR)BaseAddress) + Member->BaseOffset, &MemberAggregate);
589 RosSymFreeAggregate(&MemberAggregate);
590 }
591 Indent[IndentLen] = 0;
592 KdbpPrint("%s}\n", Indent);
593 } break;
594 }
595 }
596 } else {
597 readfail:
598 if (Member->Size <= 8) {
599 KdbpPrint(" ??\n");
600 } else {
601 KdbpPrint(" %s @ %x {\n", Member->Type, Member->BaseOffset);
602 Indent[IndentLen] = ' ';
603 if (RosSymAggregate(Info, Member->Type, &MemberAggregate)) {
604 KdbpPrintStructInternal(Info, Indent, DoRead, BaseAddress, &MemberAggregate);
605 RosSymFreeAggregate(&MemberAggregate);
606 }
607 Indent[IndentLen] = 0;
608 KdbpPrint("%s}\n", Indent);
609 }
610 }
611 }
612 }
613
614 PROSSYM_INFO KdbpSymFindCachedFile(PUNICODE_STRING ModName);
615
616 static BOOLEAN
KdbpCmdPrintStruct(ULONG Argc,PCHAR Argv[])617 KdbpCmdPrintStruct(
618 ULONG Argc,
619 PCHAR Argv[])
620 {
621 ULONG i;
622 ULONGLONG Result = 0;
623 PVOID BaseAddress = NULL;
624 ROSSYM_AGGREGATE Aggregate = {0};
625 UNICODE_STRING ModName = {0};
626 ANSI_STRING AnsiName = {0};
627 CHAR Indent[100] = {0};
628 PROSSYM_INFO Info;
629
630 if (Argc < 3) goto end;
631 AnsiName.Length = AnsiName.MaximumLength = strlen(Argv[1]);
632 AnsiName.Buffer = Argv[1];
633 RtlAnsiStringToUnicodeString(&ModName, &AnsiName, TRUE);
634 Info = KdbpSymFindCachedFile(&ModName);
635
636 if (!Info || !RosSymAggregate(Info, Argv[2], &Aggregate)) {
637 DPRINT1("Could not get aggregate\n");
638 goto end;
639 }
640
641 // Get an argument for location if it was given
642 if (Argc > 3) {
643 ULONG len;
644 PCHAR ArgStart = Argv[3];
645 DPRINT("Trying to get expression\n");
646 for (i = 3; i < Argc - 1; i++)
647 {
648 len = strlen(Argv[i]);
649 Argv[i][len] = ' ';
650 }
651
652 /* Evaluate the expression */
653 DPRINT("Arg: %s\n", ArgStart);
654 if (KdbpEvaluateExpression(ArgStart, strlen(ArgStart), &Result))
655 BaseAddress = (PVOID)(ULONG_PTR)Result;
656 }
657 DPRINT("BaseAddress: %p\n", BaseAddress);
658 KdbpPrintStructInternal(Info, Indent, !!BaseAddress, BaseAddress, &Aggregate);
659 end:
660 RosSymFreeAggregate(&Aggregate);
661 RtlFreeUnicodeString(&ModName);
662 return TRUE;
663 }
664 #endif // __ROS_DWARF__
665
666 /*!\brief Retrieves the component ID corresponding to a given component name.
667 *
668 * \param ComponentName The name of the component.
669 * \param ComponentId Receives the component id on success.
670 *
671 * \retval TRUE Success.
672 * \retval FALSE Failure.
673 */
674 static BOOLEAN
KdbpGetComponentId(IN PCSTR ComponentName,OUT PULONG ComponentId)675 KdbpGetComponentId(
676 IN PCSTR ComponentName,
677 OUT PULONG ComponentId)
678 {
679 ULONG i;
680
681 for (i = 0; i < RTL_NUMBER_OF(ComponentTable); i++)
682 {
683 if (_stricmp(ComponentName, ComponentTable[i].Name) == 0)
684 {
685 *ComponentId = ComponentTable[i].Id;
686 return TRUE;
687 }
688 }
689
690 return FALSE;
691 }
692
693 /*!\brief Displays the list of active debug channels, or enable/disable debug channels.
694 */
695 static BOOLEAN
KdbpCmdFilter(ULONG Argc,PCHAR Argv[])696 KdbpCmdFilter(
697 ULONG Argc,
698 PCHAR Argv[])
699 {
700 ULONG i, j, ComponentId, Level;
701 ULONG set = DPFLTR_MASK, clear = DPFLTR_MASK;
702 PCHAR pend;
703 PCSTR opt, p;
704
705 static struct
706 {
707 PCSTR Name;
708 ULONG Level;
709 }
710 debug_classes[] =
711 {
712 { "error", 1 << DPFLTR_ERROR_LEVEL },
713 { "warning", 1 << DPFLTR_WARNING_LEVEL },
714 { "trace", 1 << DPFLTR_TRACE_LEVEL },
715 { "info", 1 << DPFLTR_INFO_LEVEL },
716 };
717
718 if (Argc <= 1)
719 {
720 /* Display the list of available debug filter components */
721 KdbpPrint("REMARKS:\n"
722 "- The 'WIN2000' system-wide debug filter component is used for DbgPrint()\n"
723 " messages without Component ID and Level.\n"
724 "- The 'DEFAULT' debug filter component is used for DbgPrint() messages with\n"
725 " an unknown Component ID.\n\n");
726 KdbpPrint("The list of debug filter components currently available on your system is:\n\n");
727 KdbpPrint(" Component Name Component ID\n"
728 " ================== ================\n");
729 for (i = 0; i < RTL_NUMBER_OF(ComponentTable); i++)
730 {
731 KdbpPrint("%20s 0x%08lx\n", ComponentTable[i].Name, ComponentTable[i].Id);
732 }
733 return TRUE;
734 }
735
736 for (i = 1; i < Argc; i++)
737 {
738 opt = Argv[i];
739 p = opt + strcspn(opt, "+-");
740 if (!p[0]) p = opt; /* Assume it's a debug channel name */
741
742 if (p > opt)
743 {
744 for (j = 0; j < RTL_NUMBER_OF(debug_classes); j++)
745 {
746 SIZE_T len = strlen(debug_classes[j].Name);
747 if (len != (p - opt))
748 continue;
749 if (_strnicmp(opt, debug_classes[j].Name, len) == 0) /* Found it */
750 {
751 if (*p == '+')
752 set |= debug_classes[j].Level;
753 else
754 clear |= debug_classes[j].Level;
755 break;
756 }
757 }
758 if (j == RTL_NUMBER_OF(debug_classes))
759 {
760 Level = strtoul(opt, &pend, 0);
761 if (pend != p)
762 {
763 KdbpPrint("filter: bad class name '%.*s'\n", p - opt, opt);
764 continue;
765 }
766 if (*p == '+')
767 set |= Level;
768 else
769 clear |= Level;
770 }
771 }
772 else
773 {
774 if (*p == '-')
775 clear = MAXULONG;
776 else
777 set = MAXULONG;
778 }
779 if (*p == '+' || *p == '-')
780 p++;
781
782 if (!KdbpGetComponentId(p, &ComponentId))
783 {
784 KdbpPrint("filter: '%s' is not a valid component name!\n", p);
785 return TRUE;
786 }
787
788 /* Get current mask value */
789 NtSetDebugFilterState(ComponentId, set, TRUE);
790 NtSetDebugFilterState(ComponentId, clear, FALSE);
791 }
792
793 return TRUE;
794 }
795
796 /*!\brief Disassembles 10 instructions at eip or given address or
797 * displays 16 dwords from memory at given address.
798 */
799 static BOOLEAN
KdbpCmdDisassembleX(ULONG Argc,PCHAR Argv[])800 KdbpCmdDisassembleX(
801 ULONG Argc,
802 PCHAR Argv[])
803 {
804 ULONG Count;
805 ULONG ul;
806 INT i;
807 ULONGLONG Result = 0;
808 ULONG_PTR Address = KeGetContextPc(KdbCurrentTrapFrame);
809 LONG InstLen;
810
811 if (Argv[0][0] == 'x') /* display memory */
812 Count = 16;
813 else /* disassemble */
814 Count = 10;
815
816 if (Argc >= 2)
817 {
818 /* Check for [L count] part */
819 ul = 0;
820 if (strcmp(Argv[Argc-2], "L") == 0)
821 {
822 ul = strtoul(Argv[Argc-1], NULL, 0);
823 if (ul > 0)
824 {
825 Count = ul;
826 Argc -= 2;
827 }
828 }
829 else if (Argv[Argc-1][0] == 'L')
830 {
831 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
832 if (ul > 0)
833 {
834 Count = ul;
835 Argc--;
836 }
837 }
838
839 /* Put the remaining arguments back together */
840 Argc--;
841 for (ul = 1; ul < Argc; ul++)
842 {
843 Argv[ul][strlen(Argv[ul])] = ' ';
844 }
845 Argc++;
846 }
847
848 /* Evaluate the expression */
849 if (Argc > 1)
850 {
851 if (!KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result))
852 return TRUE;
853
854 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
855 KdbpPrint("Warning: Address %I64x is beeing truncated\n",Result);
856
857 Address = (ULONG_PTR)Result;
858 }
859 else if (Argv[0][0] == 'x')
860 {
861 KdbpPrint("x: Address argument required.\n");
862 return TRUE;
863 }
864
865 if (Argv[0][0] == 'x')
866 {
867 /* Display dwords */
868 ul = 0;
869
870 while (Count > 0)
871 {
872 if (!KdbSymPrintAddress((PVOID)Address, NULL))
873 KdbpPrint("<%p>:", (PVOID)Address);
874 else
875 KdbpPrint(":");
876
877 i = min(4, Count);
878 Count -= i;
879
880 while (--i >= 0)
881 {
882 if (!NT_SUCCESS(KdbpSafeReadMemory(&ul, (PVOID)Address, sizeof(ul))))
883 KdbpPrint(" ????????");
884 else
885 KdbpPrint(" %08x", ul);
886
887 Address += sizeof(ul);
888 }
889
890 KdbpPrint("\n");
891 }
892 }
893 else
894 {
895 /* Disassemble */
896 while (Count-- > 0)
897 {
898 if (!KdbSymPrintAddress((PVOID)Address, NULL))
899 KdbpPrint("<%08x>: ", Address);
900 else
901 KdbpPrint(": ");
902
903 InstLen = KdbpDisassemble(Address, KdbUseIntelSyntax);
904 if (InstLen < 0)
905 {
906 KdbpPrint("<INVALID>\n");
907 return TRUE;
908 }
909
910 KdbpPrint("\n");
911 Address += InstLen;
912 }
913 }
914
915 return TRUE;
916 }
917
918 /*!\brief Displays CPU registers.
919 */
920 static BOOLEAN
KdbpCmdRegs(ULONG Argc,PCHAR Argv[])921 KdbpCmdRegs(
922 ULONG Argc,
923 PCHAR Argv[])
924 {
925 PCONTEXT Context = KdbCurrentTrapFrame;
926 INT i;
927 static const PCHAR EflagsBits[32] = { " CF", NULL, " PF", " BIT3", " AF", " BIT5",
928 " ZF", " SF", " TF", " IF", " DF", " OF",
929 NULL, NULL, " NT", " BIT15", " RF", " VF",
930 " AC", " VIF", " VIP", " ID", " BIT22",
931 " BIT23", " BIT24", " BIT25", " BIT26",
932 " BIT27", " BIT28", " BIT29", " BIT30",
933 " BIT31" };
934
935 if (Argv[0][0] == 'r') /* regs */
936 {
937 #ifdef _M_IX86
938 KdbpPrint("CS:EIP 0x%04x:0x%08x\n"
939 "SS:ESP 0x%04x:0x%08x\n"
940 " EAX 0x%08x EBX 0x%08x\n"
941 " ECX 0x%08x EDX 0x%08x\n"
942 " ESI 0x%08x EDI 0x%08x\n"
943 " EBP 0x%08x\n",
944 Context->SegCs & 0xFFFF, Context->Eip,
945 Context->SegSs, Context->Esp,
946 Context->Eax, Context->Ebx,
947 Context->Ecx, Context->Edx,
948 Context->Esi, Context->Edi,
949 Context->Ebp);
950 #else
951 KdbpPrint("CS:RIP 0x%04x:0x%p\n"
952 "SS:RSP 0x%04x:0x%p\n"
953 " RAX 0x%p RBX 0x%p\n"
954 " RCX 0x%p RDX 0x%p\n"
955 " RSI 0x%p RDI 0x%p\n"
956 " RBP 0x%p\n",
957 Context->SegCs & 0xFFFF, Context->Rip,
958 Context->SegSs, Context->Rsp,
959 Context->Rax, Context->Rbx,
960 Context->Rcx, Context->Rdx,
961 Context->Rsi, Context->Rdi,
962 Context->Rbp);
963 #endif
964 /* Display the EFlags */
965 KdbpPrint("EFLAGS 0x%08x ", Context->EFlags);
966 for (i = 0; i < 32; i++)
967 {
968 if (i == 1)
969 {
970 if ((Context->EFlags & (1 << 1)) == 0)
971 KdbpPrint(" !BIT1");
972 }
973 else if (i == 12)
974 {
975 KdbpPrint(" IOPL%d", (Context->EFlags >> 12) & 3);
976 }
977 else if (i == 13)
978 {
979 }
980 else if ((Context->EFlags & (1 << i)) != 0)
981 {
982 KdbpPrint(EflagsBits[i]);
983 }
984 }
985 KdbpPrint("\n");
986 }
987 else if (Argv[0][0] == 's') /* sregs */
988 {
989 KdbpPrint("CS 0x%04x Index 0x%04x %cDT RPL%d\n",
990 Context->SegCs & 0xffff, (Context->SegCs & 0xffff) >> 3,
991 (Context->SegCs & (1 << 2)) ? 'L' : 'G', Context->SegCs & 3);
992 KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n",
993 Context->SegDs, Context->SegDs >> 3, (Context->SegDs & (1 << 2)) ? 'L' : 'G', Context->SegDs & 3);
994 KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n",
995 Context->SegEs, Context->SegEs >> 3, (Context->SegEs & (1 << 2)) ? 'L' : 'G', Context->SegEs & 3);
996 KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n",
997 Context->SegFs, Context->SegFs >> 3, (Context->SegFs & (1 << 2)) ? 'L' : 'G', Context->SegFs & 3);
998 KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n",
999 Context->SegGs, Context->SegGs >> 3, (Context->SegGs & (1 << 2)) ? 'L' : 'G', Context->SegGs & 3);
1000 KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
1001 Context->SegSs, Context->SegSs >> 3, (Context->SegSs & (1 << 2)) ? 'L' : 'G', Context->SegSs & 3);
1002 }
1003 else /* dregs */
1004 {
1005 ASSERT(Argv[0][0] == 'd');
1006 KdbpPrint("DR0 0x%08x\n"
1007 "DR1 0x%08x\n"
1008 "DR2 0x%08x\n"
1009 "DR3 0x%08x\n"
1010 "DR6 0x%08x\n"
1011 "DR7 0x%08x\n",
1012 Context->Dr0, Context->Dr1, Context->Dr2, Context->Dr3,
1013 Context->Dr6, Context->Dr7);
1014 }
1015
1016 return TRUE;
1017 }
1018
1019 #ifdef _M_IX86
1020 static PKTSS
KdbpRetrieveTss(IN USHORT TssSelector,OUT PULONG pType OPTIONAL,IN PKDESCRIPTOR pGdtr OPTIONAL)1021 KdbpRetrieveTss(
1022 IN USHORT TssSelector,
1023 OUT PULONG pType OPTIONAL,
1024 IN PKDESCRIPTOR pGdtr OPTIONAL)
1025 {
1026 KDESCRIPTOR Gdtr;
1027 KGDTENTRY Desc;
1028 PKTSS Tss;
1029
1030 /* Retrieve the Global Descriptor Table (user-provided or system) */
1031 if (pGdtr)
1032 Gdtr = *pGdtr;
1033 else
1034 Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
1035
1036 /* Check limits */
1037 if ((TssSelector & (sizeof(KGDTENTRY) - 1)) ||
1038 (TssSelector < sizeof(KGDTENTRY)) ||
1039 (TssSelector + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
1040 {
1041 return NULL;
1042 }
1043
1044 /* Retrieve the descriptor */
1045 if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
1046 (PVOID)(Gdtr.Base + TssSelector),
1047 sizeof(KGDTENTRY))))
1048 {
1049 return NULL;
1050 }
1051
1052 /* Check for TSS32(Avl) or TSS32(Busy) */
1053 if (Desc.HighWord.Bits.Type != 9 && Desc.HighWord.Bits.Type != 11)
1054 {
1055 return NULL;
1056 }
1057 if (pType) *pType = Desc.HighWord.Bits.Type;
1058
1059 Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
1060 Desc.HighWord.Bytes.BaseMid << 16 |
1061 Desc.HighWord.Bytes.BaseHi << 24);
1062
1063 return Tss;
1064 }
1065
1066 FORCEINLINE BOOLEAN
KdbpIsNestedTss(IN USHORT TssSelector,IN PKTSS Tss)1067 KdbpIsNestedTss(
1068 IN USHORT TssSelector,
1069 IN PKTSS Tss)
1070 {
1071 USHORT Backlink;
1072
1073 if (!Tss)
1074 return FALSE;
1075
1076 #ifdef _M_AMD64
1077 // HACK
1078 return FALSE;
1079 #else
1080 /* Retrieve the TSS Backlink */
1081 if (!NT_SUCCESS(KdbpSafeReadMemory(&Backlink,
1082 (PVOID)&Tss->Backlink,
1083 sizeof(USHORT))))
1084 {
1085 return FALSE;
1086 }
1087 #endif
1088
1089 return (Backlink != 0 && Backlink != TssSelector);
1090 }
1091
1092 static BOOLEAN
KdbpContextFromPrevTss(IN OUT PCONTEXT Context,OUT PUSHORT TssSelector,IN OUT PKTSS * pTss,IN PKDESCRIPTOR pGdtr)1093 KdbpContextFromPrevTss(
1094 IN OUT PCONTEXT Context,
1095 OUT PUSHORT TssSelector,
1096 IN OUT PKTSS* pTss,
1097 IN PKDESCRIPTOR pGdtr)
1098 {
1099 ULONG_PTR Eip, Ebp;
1100 USHORT Backlink;
1101 PKTSS Tss = *pTss;
1102
1103 #ifdef _M_AMD64
1104 // HACK
1105 return FALSE;
1106 #else
1107 /* Retrieve the TSS Backlink */
1108 if (!NT_SUCCESS(KdbpSafeReadMemory(&Backlink,
1109 (PVOID)&Tss->Backlink,
1110 sizeof(USHORT))))
1111 {
1112 return FALSE;
1113 }
1114
1115 /* Retrieve the parent TSS */
1116 Tss = KdbpRetrieveTss(Backlink, NULL, pGdtr);
1117 if (!Tss)
1118 return FALSE;
1119
1120 if (!NT_SUCCESS(KdbpSafeReadMemory(&Eip,
1121 (PVOID)&Tss->Eip,
1122 sizeof(ULONG_PTR))))
1123 {
1124 return FALSE;
1125 }
1126
1127 if (!NT_SUCCESS(KdbpSafeReadMemory(&Ebp,
1128 (PVOID)&Tss->Ebp,
1129 sizeof(ULONG_PTR))))
1130 {
1131 return FALSE;
1132 }
1133
1134 /* Return the parent TSS and its trap frame */
1135 *TssSelector = Backlink;
1136 *pTss = Tss;
1137 Context->Eip = Eip;
1138 Context->Ebp = Ebp;
1139 #endif
1140 return TRUE;
1141 }
1142 #endif // _M_IX86
1143
1144 #ifdef _M_AMD64
1145
1146 static
1147 BOOLEAN
GetNextFrame(_Inout_ PCONTEXT Context)1148 GetNextFrame(
1149 _Inout_ PCONTEXT Context)
1150 {
1151 PRUNTIME_FUNCTION FunctionEntry;
1152 ULONG64 ImageBase, EstablisherFrame;
1153 PVOID HandlerData;
1154
1155 _SEH2_TRY
1156 {
1157 /* Lookup the FunctionEntry for the current RIP */
1158 FunctionEntry = RtlLookupFunctionEntry(Context->Rip, &ImageBase, NULL);
1159 if (FunctionEntry == NULL)
1160 {
1161 /* No function entry, so this must be a leaf function. Pop the return address from the stack.
1162 Note: this can happen after the first frame as the result of an exception */
1163 Context->Rip = *(DWORD64*)Context->Rsp;
1164 Context->Rsp += sizeof(DWORD64);
1165 return TRUE;
1166 }
1167 else
1168 {
1169 RtlVirtualUnwind(UNW_FLAG_NHANDLER,
1170 ImageBase,
1171 Context->Rip,
1172 FunctionEntry,
1173 Context,
1174 &HandlerData,
1175 &EstablisherFrame,
1176 NULL);
1177 }
1178 }
1179 _SEH2_EXCEPT(1)
1180 {
1181 return FALSE;
1182 }
1183 _SEH2_END
1184
1185 return TRUE;
1186 }
1187
1188 static BOOLEAN
KdbpCmdBackTrace(ULONG Argc,PCHAR Argv[])1189 KdbpCmdBackTrace(
1190 ULONG Argc,
1191 PCHAR Argv[])
1192 {
1193 CONTEXT Context = *KdbCurrentTrapFrame;
1194
1195 /* Walk through the frames */
1196 KdbpPrint("Frames:\n");
1197 do
1198 {
1199 BOOLEAN GotNextFrame;
1200
1201 KdbpPrint("[%p] ", (PVOID)Context.Rsp);
1202
1203 /* Print the location after the call instruction */
1204 if (!KdbSymPrintAddress((PVOID)Context.Rip, &Context))
1205 KdbpPrint("<%p>", (PVOID)Context.Rip);
1206 KdbpPrint("\n");
1207
1208 if (KdbOutputAborted)
1209 break;
1210
1211 GotNextFrame = GetNextFrame(&Context);
1212 if (!GotNextFrame)
1213 {
1214 KdbpPrint("Couldn't get next frame\n");
1215 break;
1216 }
1217 } while ((Context.Rip != 0) && (Context.Rsp != 0));
1218
1219 return TRUE;
1220 }
1221 #else
1222 /*!\brief Displays a backtrace.
1223 */
1224 static BOOLEAN
KdbpCmdBackTrace(ULONG Argc,PCHAR Argv[])1225 KdbpCmdBackTrace(
1226 ULONG Argc,
1227 PCHAR Argv[])
1228 {
1229 ULONG ul;
1230 ULONGLONG Result = 0;
1231 CONTEXT Context = *KdbCurrentTrapFrame;
1232 ULONG_PTR Frame = KeGetContextFrameRegister(&Context);
1233 ULONG_PTR Address;
1234
1235 if (Argc >= 2)
1236 {
1237 /* Check for [L count] part */
1238 ul = 0;
1239 if (strcmp(Argv[Argc-2], "L") == 0)
1240 {
1241 ul = strtoul(Argv[Argc-1], NULL, 0);
1242 if (ul > 0)
1243 {
1244 Argc -= 2;
1245 }
1246 }
1247 else if (Argv[Argc-1][0] == 'L')
1248 {
1249 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
1250 if (ul > 0)
1251 {
1252 Argc--;
1253 }
1254 }
1255
1256 /* Put the remaining arguments back together */
1257 Argc--;
1258 for (ul = 1; ul < Argc; ul++)
1259 {
1260 Argv[ul][strlen(Argv[ul])] = ' ';
1261 }
1262 Argc++;
1263 }
1264
1265 /* Check if a Frame Address or Thread ID is given */
1266 if (Argc > 1)
1267 {
1268 if (Argv[1][0] == '*')
1269 {
1270 Argv[1]++;
1271
1272 /* Evaluate the expression */
1273 if (!KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result))
1274 return TRUE;
1275
1276 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1277 KdbpPrint("Warning: Address %I64x is beeing truncated\n", Result);
1278
1279 Frame = (ULONG_PTR)Result;
1280 }
1281 else
1282 {
1283 KdbpPrint("Thread backtrace not supported yet!\n");
1284 return TRUE;
1285 }
1286 }
1287
1288 #ifdef _M_IX86
1289 KDESCRIPTOR Gdtr;
1290 USHORT TssSelector;
1291 PKTSS Tss;
1292
1293 /* Retrieve the Global Descriptor Table */
1294 Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
1295
1296 /* Retrieve the current (active) TSS */
1297 TssSelector = Ke386GetTr();
1298 Tss = KdbpRetrieveTss(TssSelector, NULL, &Gdtr);
1299 if (KdbpIsNestedTss(TssSelector, Tss))
1300 {
1301 /* Display the active TSS if it is nested */
1302 KdbpPrint("[Active TSS 0x%04x @ 0x%p]\n", TssSelector, Tss);
1303 }
1304 #endif
1305
1306 /* If no Frame Address or Thread ID was given, try printing the function at EIP */
1307 if (Argc <= 1)
1308 {
1309 KdbpPrint("Eip:\n");
1310 if (!KdbSymPrintAddress((PVOID)KeGetContextPc(&Context), &Context))
1311 KdbpPrint("<%p>\n", KeGetContextPc(&Context));
1312 else
1313 KdbpPrint("\n");
1314 }
1315
1316 /* Walk through the frames */
1317 KdbpPrint("Frames:\n");
1318 for (;;)
1319 {
1320 BOOLEAN GotNextFrame;
1321
1322 if (Frame == 0)
1323 goto CheckForParentTSS;
1324
1325 Address = 0;
1326 if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof(ULONG_PTR))))
1327 {
1328 KdbpPrint("Couldn't access memory at 0x%p!\n", Frame + sizeof(ULONG_PTR));
1329 goto CheckForParentTSS;
1330 }
1331
1332 if (Address == 0)
1333 goto CheckForParentTSS;
1334
1335 GotNextFrame = NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof(ULONG_PTR)));
1336 if (GotNextFrame)
1337 {
1338 KeSetContextFrameRegister(&Context, Frame);
1339 }
1340 // else
1341 // Frame = 0;
1342
1343 /* Print the location of the call instruction (assumed 5 bytes length) */
1344 if (!KdbSymPrintAddress((PVOID)(Address - 5), &Context))
1345 KdbpPrint("<%08x>\n", Address);
1346 else
1347 KdbpPrint("\n");
1348
1349 if (KdbOutputAborted)
1350 break;
1351
1352 if (!GotNextFrame)
1353 {
1354 KdbpPrint("Couldn't access memory at 0x%p!\n", Frame);
1355 goto CheckForParentTSS; // break;
1356 }
1357
1358 continue;
1359
1360 CheckForParentTSS:
1361 #ifndef _M_IX86
1362 break;
1363 #else
1364 /*
1365 * We have ended the stack walking for the current (active) TSS.
1366 * Check whether this TSS was nested, and if so switch to its parent
1367 * and walk its stack.
1368 */
1369 if (!KdbpIsNestedTss(TssSelector, Tss))
1370 break; // The TSS is not nested, we stop there.
1371
1372 GotNextFrame = KdbpContextFromPrevTss(&Context, &TssSelector, &Tss, &Gdtr);
1373 if (!GotNextFrame)
1374 {
1375 KdbpPrint("Couldn't access parent TSS 0x%04x\n", Tss->Backlink);
1376 break; // Cannot retrieve the parent TSS, we stop there.
1377 }
1378
1379
1380 Address = Context.Eip;
1381 Frame = Context.Ebp;
1382
1383 KdbpPrint("[Parent TSS 0x%04x @ 0x%p]\n", TssSelector, Tss);
1384
1385 if (!KdbSymPrintAddress((PVOID)Address, &Context))
1386 KdbpPrint("<%08x>\n", Address);
1387 else
1388 KdbpPrint("\n");
1389 #endif
1390 }
1391
1392 return TRUE;
1393 }
1394
1395 #endif // M_AMD64
1396
1397 /*!\brief Continues execution of the system/leaves KDB.
1398 */
1399 static BOOLEAN
KdbpCmdContinue(ULONG Argc,PCHAR Argv[])1400 KdbpCmdContinue(
1401 ULONG Argc,
1402 PCHAR Argv[])
1403 {
1404 /* Exit the main loop */
1405 return FALSE;
1406 }
1407
1408 /*!\brief Continues execution of the system/leaves KDB.
1409 */
1410 static BOOLEAN
KdbpCmdStep(ULONG Argc,PCHAR Argv[])1411 KdbpCmdStep(
1412 ULONG Argc,
1413 PCHAR Argv[])
1414 {
1415 ULONG Count = 1;
1416
1417 if (Argc > 1)
1418 {
1419 Count = strtoul(Argv[1], NULL, 0);
1420 if (Count == 0)
1421 {
1422 KdbpPrint("%s: Integer argument required\n", Argv[0]);
1423 return TRUE;
1424 }
1425 }
1426
1427 if (Argv[0][0] == 'n')
1428 KdbSingleStepOver = TRUE;
1429 else
1430 KdbSingleStepOver = FALSE;
1431
1432 /* Set the number of single steps and return to the interrupted code. */
1433 KdbNumSingleSteps = Count;
1434
1435 return FALSE;
1436 }
1437
1438 /*!\brief Lists breakpoints.
1439 */
1440 static BOOLEAN
KdbpCmdBreakPointList(ULONG Argc,PCHAR Argv[])1441 KdbpCmdBreakPointList(
1442 ULONG Argc,
1443 PCHAR Argv[])
1444 {
1445 LONG l;
1446 ULONG_PTR Address = 0;
1447 KDB_BREAKPOINT_TYPE Type = 0;
1448 KDB_ACCESS_TYPE AccessType = 0;
1449 UCHAR Size = 0;
1450 UCHAR DebugReg = 0;
1451 BOOLEAN Enabled = FALSE;
1452 BOOLEAN Global = FALSE;
1453 PEPROCESS Process = NULL;
1454 PCHAR str1, str2, ConditionExpr, GlobalOrLocal;
1455 CHAR Buffer[20];
1456
1457 l = KdbpGetNextBreakPointNr(0);
1458 if (l < 0)
1459 {
1460 KdbpPrint("No breakpoints.\n");
1461 return TRUE;
1462 }
1463
1464 KdbpPrint("Breakpoints:\n");
1465 do
1466 {
1467 if (!KdbpGetBreakPointInfo(l, &Address, &Type, &Size, &AccessType, &DebugReg,
1468 &Enabled, &Global, &Process, &ConditionExpr))
1469 {
1470 continue;
1471 }
1472
1473 if (l == KdbLastBreakPointNr)
1474 {
1475 str1 = "\x1b[1m*";
1476 str2 = "\x1b[0m";
1477 }
1478 else
1479 {
1480 str1 = " ";
1481 str2 = "";
1482 }
1483
1484 if (Global)
1485 {
1486 GlobalOrLocal = " global";
1487 }
1488 else
1489 {
1490 GlobalOrLocal = Buffer;
1491 sprintf(Buffer, " PID 0x%Ix",
1492 (ULONG_PTR)(Process ? Process->UniqueProcessId : INVALID_HANDLE_VALUE));
1493 }
1494
1495 if (Type == KdbBreakPointSoftware || Type == KdbBreakPointTemporary)
1496 {
1497 KdbpPrint(" %s%03d BPX 0x%08x%s%s%s%s%s\n",
1498 str1, l, Address,
1499 Enabled ? "" : " disabled",
1500 GlobalOrLocal,
1501 ConditionExpr ? " IF " : "",
1502 ConditionExpr ? ConditionExpr : "",
1503 str2);
1504 }
1505 else if (Type == KdbBreakPointHardware)
1506 {
1507 if (!Enabled)
1508 {
1509 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s disabled%s%s%s%s\n", str1, l, Address,
1510 KDB_ACCESS_TYPE_TO_STRING(AccessType),
1511 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
1512 GlobalOrLocal,
1513 ConditionExpr ? " IF " : "",
1514 ConditionExpr ? ConditionExpr : "",
1515 str2);
1516 }
1517 else
1518 {
1519 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s DR%d%s%s%s%s\n", str1, l, Address,
1520 KDB_ACCESS_TYPE_TO_STRING(AccessType),
1521 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
1522 DebugReg,
1523 GlobalOrLocal,
1524 ConditionExpr ? " IF " : "",
1525 ConditionExpr ? ConditionExpr : "",
1526 str2);
1527 }
1528 }
1529 }
1530 while ((l = KdbpGetNextBreakPointNr(l+1)) >= 0);
1531
1532 return TRUE;
1533 }
1534
1535 /*!\brief Enables, disables or clears a breakpoint.
1536 */
1537 static BOOLEAN
KdbpCmdEnableDisableClearBreakPoint(ULONG Argc,PCHAR Argv[])1538 KdbpCmdEnableDisableClearBreakPoint(
1539 ULONG Argc,
1540 PCHAR Argv[])
1541 {
1542 PCHAR pend;
1543 ULONG BreakPointNr;
1544
1545 if (Argc < 2)
1546 {
1547 KdbpPrint("%s: argument required\n", Argv[0]);
1548 return TRUE;
1549 }
1550
1551 pend = Argv[1];
1552 BreakPointNr = strtoul(Argv[1], &pend, 0);
1553 if (pend == Argv[1] || *pend != '\0')
1554 {
1555 KdbpPrint("%s: integer argument required\n", Argv[0]);
1556 return TRUE;
1557 }
1558
1559 if (Argv[0][1] == 'e') /* enable */
1560 {
1561 KdbpEnableBreakPoint(BreakPointNr, NULL);
1562 }
1563 else if (Argv [0][1] == 'd') /* disable */
1564 {
1565 KdbpDisableBreakPoint(BreakPointNr, NULL);
1566 }
1567 else /* clear */
1568 {
1569 ASSERT(Argv[0][1] == 'c');
1570 KdbpDeleteBreakPoint(BreakPointNr, NULL);
1571 }
1572
1573 return TRUE;
1574 }
1575
1576 /*!\brief Sets a software or hardware (memory) breakpoint at the given address.
1577 */
1578 static BOOLEAN
KdbpCmdBreakPoint(ULONG Argc,PCHAR Argv[])1579 KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[])
1580 {
1581 ULONGLONG Result = 0;
1582 ULONG_PTR Address;
1583 KDB_BREAKPOINT_TYPE Type;
1584 UCHAR Size = 0;
1585 KDB_ACCESS_TYPE AccessType = 0;
1586 ULONG AddressArgIndex, i;
1587 LONG ConditionArgIndex;
1588 BOOLEAN Global = TRUE;
1589
1590 if (Argv[0][2] == 'x') /* software breakpoint */
1591 {
1592 if (Argc < 2)
1593 {
1594 KdbpPrint("bpx: Address argument required.\n");
1595 return TRUE;
1596 }
1597
1598 AddressArgIndex = 1;
1599 Type = KdbBreakPointSoftware;
1600 }
1601 else /* memory breakpoint */
1602 {
1603 ASSERT(Argv[0][2] == 'm');
1604
1605 if (Argc < 2)
1606 {
1607 KdbpPrint("bpm: Access type argument required (one of r, w, rw, x)\n");
1608 return TRUE;
1609 }
1610
1611 if (_stricmp(Argv[1], "x") == 0)
1612 AccessType = KdbAccessExec;
1613 else if (_stricmp(Argv[1], "r") == 0)
1614 AccessType = KdbAccessRead;
1615 else if (_stricmp(Argv[1], "w") == 0)
1616 AccessType = KdbAccessWrite;
1617 else if (_stricmp(Argv[1], "rw") == 0)
1618 AccessType = KdbAccessReadWrite;
1619 else
1620 {
1621 KdbpPrint("bpm: Unknown access type '%s'\n", Argv[1]);
1622 return TRUE;
1623 }
1624
1625 if (Argc < 3)
1626 {
1627 KdbpPrint("bpm: %s argument required.\n", AccessType == KdbAccessExec ? "Address" : "Memory size");
1628 return TRUE;
1629 }
1630
1631 AddressArgIndex = 3;
1632 if (_stricmp(Argv[2], "byte") == 0)
1633 Size = 1;
1634 else if (_stricmp(Argv[2], "word") == 0)
1635 Size = 2;
1636 else if (_stricmp(Argv[2], "dword") == 0)
1637 Size = 4;
1638 else if (AccessType == KdbAccessExec)
1639 {
1640 Size = 1;
1641 AddressArgIndex--;
1642 }
1643 else
1644 {
1645 KdbpPrint("bpm: Unknown memory size '%s'\n", Argv[2]);
1646 return TRUE;
1647 }
1648
1649 if (Argc <= AddressArgIndex)
1650 {
1651 KdbpPrint("bpm: Address argument required.\n");
1652 return TRUE;
1653 }
1654
1655 Type = KdbBreakPointHardware;
1656 }
1657
1658 /* Put the arguments back together */
1659 ConditionArgIndex = -1;
1660 for (i = AddressArgIndex; i < (Argc-1); i++)
1661 {
1662 if (strcmp(Argv[i+1], "IF") == 0) /* IF found */
1663 {
1664 ConditionArgIndex = i + 2;
1665 if ((ULONG)ConditionArgIndex >= Argc)
1666 {
1667 KdbpPrint("%s: IF requires condition expression.\n", Argv[0]);
1668 return TRUE;
1669 }
1670
1671 for (i = ConditionArgIndex; i < (Argc-1); i++)
1672 Argv[i][strlen(Argv[i])] = ' ';
1673
1674 break;
1675 }
1676
1677 Argv[i][strlen(Argv[i])] = ' ';
1678 }
1679
1680 /* Evaluate the address expression */
1681 if (!KdbpEvaluateExpression(Argv[AddressArgIndex],
1682 KdbPromptStr.Length + (Argv[AddressArgIndex]-Argv[0]),
1683 &Result))
1684 {
1685 return TRUE;
1686 }
1687
1688 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1689 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0],Result);
1690
1691 Address = (ULONG_PTR)Result;
1692
1693 KdbpInsertBreakPoint(Address, Type, Size, AccessType,
1694 (ConditionArgIndex < 0) ? NULL : Argv[ConditionArgIndex],
1695 Global, NULL);
1696
1697 return TRUE;
1698 }
1699
1700 /*!\brief Lists threads or switches to another thread context.
1701 */
1702 static BOOLEAN
KdbpCmdThread(ULONG Argc,PCHAR Argv[])1703 KdbpCmdThread(
1704 ULONG Argc,
1705 PCHAR Argv[])
1706 {
1707 PLIST_ENTRY Entry;
1708 PETHREAD Thread = NULL;
1709 PEPROCESS Process = NULL;
1710 BOOLEAN ReferencedThread = FALSE, ReferencedProcess = FALSE;
1711 PULONG_PTR Stack;
1712 PULONG_PTR Frame;
1713 ULONG_PTR Pc;
1714 ULONG_PTR ul = 0;
1715 PCHAR State, pend, str1, str2;
1716 static const PCHAR ThreadStateToString[DeferredReady+1] =
1717 {
1718 "Initialized", "Ready", "Running",
1719 "Standby", "Terminated", "Waiting",
1720 "Transition", "DeferredReady"
1721 };
1722
1723 ASSERT(KdbCurrentProcess);
1724
1725 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1726 {
1727 Process = KdbCurrentProcess;
1728
1729 if (Argc >= 3)
1730 {
1731 ul = strtoulptr(Argv[2], &pend, 0);
1732 if (Argv[2] == pend)
1733 {
1734 KdbpPrint("thread: '%s' is not a valid process id!\n", Argv[2]);
1735 return TRUE;
1736 }
1737
1738 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1739 {
1740 KdbpPrint("thread: Invalid process id!\n");
1741 return TRUE;
1742 }
1743
1744 /* Remember our reference */
1745 ReferencedProcess = TRUE;
1746 }
1747
1748 Entry = Process->ThreadListHead.Flink;
1749 if (Entry == &Process->ThreadListHead)
1750 {
1751 if (Argc >= 3)
1752 KdbpPrint("No threads in process 0x%px!\n", (PVOID)ul);
1753 else
1754 KdbpPrint("No threads in current process!\n");
1755
1756 if (ReferencedProcess)
1757 ObDereferenceObject(Process);
1758
1759 return TRUE;
1760 }
1761
1762 KdbpPrint(" TID State Prior. Affinity EBP EIP\n");
1763 do
1764 {
1765 Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
1766
1767 if (Thread == KdbCurrentThread)
1768 {
1769 str1 = "\x1b[1m*";
1770 str2 = "\x1b[0m";
1771 }
1772 else
1773 {
1774 str1 = " ";
1775 str2 = "";
1776 }
1777
1778 if (!Thread->Tcb.InitialStack)
1779 {
1780 /* Thread has no kernel stack (probably terminated) */
1781 Stack = Frame = NULL;
1782 Pc = 0;
1783 }
1784 else if (Thread->Tcb.TrapFrame)
1785 {
1786 Stack = (PULONG_PTR)KeGetTrapFrameStackRegister(Thread->Tcb.TrapFrame);
1787 Frame = (PULONG_PTR)KeGetTrapFrameFrameRegister(Thread->Tcb.TrapFrame);
1788 Pc = KeGetTrapFramePc(Thread->Tcb.TrapFrame);
1789 }
1790 else
1791 {
1792 Stack = (PULONG_PTR)Thread->Tcb.KernelStack;
1793 Frame = (PULONG_PTR)Stack[4];
1794 Pc = 0;
1795
1796 if (Frame) /* FIXME: Should we attach to the process to read Ebp[1]? */
1797 KdbpSafeReadMemory(&Pc, Frame + 1, sizeof(Pc));
1798 }
1799
1800 if (Thread->Tcb.State < (DeferredReady + 1))
1801 State = ThreadStateToString[Thread->Tcb.State];
1802 else
1803 State = "Unknown";
1804
1805 KdbpPrint(" %s0x%08x %-11s %3d 0x%08x 0x%08x 0x%08x%s\n",
1806 str1,
1807 Thread->Cid.UniqueThread,
1808 State,
1809 Thread->Tcb.Priority,
1810 Thread->Tcb.Affinity,
1811 Frame,
1812 Pc,
1813 str2);
1814
1815 Entry = Entry->Flink;
1816 }
1817 while (Entry != &Process->ThreadListHead);
1818
1819 /* Release our reference, if any */
1820 if (ReferencedProcess)
1821 ObDereferenceObject(Process);
1822 }
1823 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1824 {
1825 if (Argc < 3)
1826 {
1827 KdbpPrint("thread attach: thread id argument required!\n");
1828 return TRUE;
1829 }
1830
1831 ul = strtoulptr(Argv[2], &pend, 0);
1832 if (Argv[2] == pend)
1833 {
1834 KdbpPrint("thread attach: '%s' is not a valid thread id!\n", Argv[2]);
1835 return TRUE;
1836 }
1837
1838 if (!KdbpAttachToThread((PVOID)ul))
1839 {
1840 return TRUE;
1841 }
1842
1843 KdbpPrint("Attached to thread 0x%08x.\n", ul);
1844 }
1845 else
1846 {
1847 Thread = KdbCurrentThread;
1848
1849 if (Argc >= 2)
1850 {
1851 ul = strtoulptr(Argv[1], &pend, 0);
1852 if (Argv[1] == pend)
1853 {
1854 KdbpPrint("thread: '%s' is not a valid thread id!\n", Argv[1]);
1855 return TRUE;
1856 }
1857
1858 if (!NT_SUCCESS(PsLookupThreadByThreadId((PVOID)ul, &Thread)))
1859 {
1860 KdbpPrint("thread: Invalid thread id!\n");
1861 return TRUE;
1862 }
1863
1864 /* Remember our reference */
1865 ReferencedThread = TRUE;
1866 }
1867
1868 if (Thread->Tcb.State < (DeferredReady + 1))
1869 State = ThreadStateToString[Thread->Tcb.State];
1870 else
1871 State = "Unknown";
1872
1873 KdbpPrint("%s"
1874 " TID: 0x%08x\n"
1875 " State: %s (0x%x)\n"
1876 " Priority: %d\n"
1877 " Affinity: 0x%08x\n"
1878 " Initial Stack: 0x%08x\n"
1879 " Stack Limit: 0x%08x\n"
1880 " Stack Base: 0x%08x\n"
1881 " Kernel Stack: 0x%08x\n"
1882 " Trap Frame: 0x%08x\n"
1883 #ifndef _M_AMD64
1884 " NPX State: %s (0x%x)\n"
1885 #endif
1886 , (Argc < 2) ? "Current Thread:\n" : ""
1887 , Thread->Cid.UniqueThread
1888 , State, Thread->Tcb.State
1889 , Thread->Tcb.Priority
1890 , Thread->Tcb.Affinity
1891 , Thread->Tcb.InitialStack
1892 , Thread->Tcb.StackLimit
1893 , Thread->Tcb.StackBase
1894 , Thread->Tcb.KernelStack
1895 , Thread->Tcb.TrapFrame
1896 #ifndef _M_AMD64
1897 , NPX_STATE_TO_STRING(Thread->Tcb.NpxState), Thread->Tcb.NpxState
1898 #endif
1899 );
1900
1901 /* Release our reference if we had one */
1902 if (ReferencedThread)
1903 ObDereferenceObject(Thread);
1904 }
1905
1906 return TRUE;
1907 }
1908
1909 /*!\brief Lists processes or switches to another process context.
1910 */
1911 static BOOLEAN
KdbpCmdProc(ULONG Argc,PCHAR Argv[])1912 KdbpCmdProc(
1913 ULONG Argc,
1914 PCHAR Argv[])
1915 {
1916 PLIST_ENTRY Entry;
1917 PEPROCESS Process;
1918 BOOLEAN ReferencedProcess = FALSE;
1919 PCHAR State, pend, str1, str2;
1920 ULONG_PTR ul;
1921 extern LIST_ENTRY PsActiveProcessHead;
1922
1923 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1924 {
1925 Entry = PsActiveProcessHead.Flink;
1926 if (!Entry || Entry == &PsActiveProcessHead)
1927 {
1928 KdbpPrint("No processes in the system!\n");
1929 return TRUE;
1930 }
1931
1932 KdbpPrint(" PID State Filename\n");
1933 do
1934 {
1935 Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
1936
1937 if (Process == KdbCurrentProcess)
1938 {
1939 str1 = "\x1b[1m*";
1940 str2 = "\x1b[0m";
1941 }
1942 else
1943 {
1944 str1 = " ";
1945 str2 = "";
1946 }
1947
1948 State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
1949 ((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
1950
1951 KdbpPrint(" %s0x%08x %-10s %s%s\n",
1952 str1,
1953 Process->UniqueProcessId,
1954 State,
1955 Process->ImageFileName,
1956 str2);
1957
1958 Entry = Entry->Flink;
1959 }
1960 while(Entry != &PsActiveProcessHead);
1961 }
1962 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1963 {
1964 if (Argc < 3)
1965 {
1966 KdbpPrint("process attach: process id argument required!\n");
1967 return TRUE;
1968 }
1969
1970 ul = strtoulptr(Argv[2], &pend, 0);
1971 if (Argv[2] == pend)
1972 {
1973 KdbpPrint("process attach: '%s' is not a valid process id!\n", Argv[2]);
1974 return TRUE;
1975 }
1976
1977 if (!KdbpAttachToProcess((PVOID)ul))
1978 {
1979 return TRUE;
1980 }
1981
1982 KdbpPrint("Attached to process 0x%p, thread 0x%p.\n", (PVOID)ul,
1983 KdbCurrentThread->Cid.UniqueThread);
1984 }
1985 else
1986 {
1987 Process = KdbCurrentProcess;
1988
1989 if (Argc >= 2)
1990 {
1991 ul = strtoulptr(Argv[1], &pend, 0);
1992 if (Argv[1] == pend)
1993 {
1994 KdbpPrint("proc: '%s' is not a valid process id!\n", Argv[1]);
1995 return TRUE;
1996 }
1997
1998 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1999 {
2000 KdbpPrint("proc: Invalid process id!\n");
2001 return TRUE;
2002 }
2003
2004 /* Remember our reference */
2005 ReferencedProcess = TRUE;
2006 }
2007
2008 State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
2009 ((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
2010 KdbpPrint("%s"
2011 " PID: 0x%08x\n"
2012 " State: %s (0x%x)\n"
2013 " Image Filename: %s\n",
2014 (Argc < 2) ? "Current process:\n" : "",
2015 Process->UniqueProcessId,
2016 State, Process->Pcb.State,
2017 Process->ImageFileName);
2018
2019 /* Release our reference, if any */
2020 if (ReferencedProcess)
2021 ObDereferenceObject(Process);
2022 }
2023
2024 return TRUE;
2025 }
2026
2027 /*!\brief Lists loaded modules or the one containing the specified address.
2028 */
2029 static BOOLEAN
KdbpCmdMod(ULONG Argc,PCHAR Argv[])2030 KdbpCmdMod(
2031 ULONG Argc,
2032 PCHAR Argv[])
2033 {
2034 ULONGLONG Result = 0;
2035 ULONG_PTR Address;
2036 PLDR_DATA_TABLE_ENTRY LdrEntry;
2037 BOOLEAN DisplayOnlyOneModule = FALSE;
2038 INT i = 0;
2039
2040 if (Argc >= 2)
2041 {
2042 /* Put the arguments back together */
2043 Argc--;
2044 while (--Argc >= 1)
2045 Argv[Argc][strlen(Argv[Argc])] = ' ';
2046
2047 /* Evaluate the expression */
2048 if (!KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result))
2049 {
2050 return TRUE;
2051 }
2052
2053 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
2054 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0],Result);
2055
2056 Address = (ULONG_PTR)Result;
2057
2058 if (!KdbpSymFindModule((PVOID)Address, -1, &LdrEntry))
2059 {
2060 KdbpPrint("No module containing address 0x%p found!\n", Address);
2061 return TRUE;
2062 }
2063
2064 DisplayOnlyOneModule = TRUE;
2065 }
2066 else
2067 {
2068 if (!KdbpSymFindModule(NULL, 0, &LdrEntry))
2069 {
2070 ULONG_PTR ntoskrnlBase = (ULONG_PTR)__ImageBase;
2071 KdbpPrint(" Base Size Name\n");
2072 KdbpPrint(" %p %08x %s\n", (PVOID)ntoskrnlBase, 0, "ntoskrnl.exe");
2073 return TRUE;
2074 }
2075
2076 i = 1;
2077 }
2078
2079 KdbpPrint(" Base Size Name\n");
2080 for (;;)
2081 {
2082 KdbpPrint(" %p %08x ", LdrEntry->DllBase, LdrEntry->SizeOfImage);
2083 KdbpPrintUnicodeString(&LdrEntry->BaseDllName);
2084 KdbpPrint("\n");
2085
2086 if(DisplayOnlyOneModule || !KdbpSymFindModule(NULL, i++, &LdrEntry))
2087 break;
2088 }
2089
2090 return TRUE;
2091 }
2092
2093 /*!\brief Displays GDT, LDT or IDT.
2094 */
2095 static BOOLEAN
KdbpCmdGdtLdtIdt(ULONG Argc,PCHAR Argv[])2096 KdbpCmdGdtLdtIdt(
2097 ULONG Argc,
2098 PCHAR Argv[])
2099 {
2100 KDESCRIPTOR Reg;
2101 ULONG SegDesc[2];
2102 ULONG SegBase;
2103 ULONG SegLimit;
2104 PCHAR SegType;
2105 USHORT SegSel;
2106 UCHAR Type, Dpl;
2107 INT i;
2108 ULONG ul;
2109
2110 if (Argv[0][0] == 'i')
2111 {
2112 /* Read IDTR */
2113 __sidt(&Reg.Limit);
2114
2115 if (Reg.Limit < 7)
2116 {
2117 KdbpPrint("Interrupt descriptor table is empty.\n");
2118 return TRUE;
2119 }
2120
2121 KdbpPrint("IDT Base: 0x%08x Limit: 0x%04x\n", Reg.Base, Reg.Limit);
2122 KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n");
2123
2124 for (i = 0; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
2125 {
2126 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)((ULONG_PTR)Reg.Base + i), sizeof(SegDesc))))
2127 {
2128 KdbpPrint("Couldn't access memory at 0x%p!\n", (PVOID)((ULONG_PTR)Reg.Base + i));
2129 return TRUE;
2130 }
2131
2132 Dpl = ((SegDesc[1] >> 13) & 3);
2133 if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
2134 SegType = "TASKGATE";
2135 else if ((SegDesc[1] & 0x1fe0) == 0x0e00) /* 32 bit Interrupt gate */
2136 SegType = "INTGATE32";
2137 else if ((SegDesc[1] & 0x1fe0) == 0x0600) /* 16 bit Interrupt gate */
2138 SegType = "INTGATE16";
2139 else if ((SegDesc[1] & 0x1fe0) == 0x0f00) /* 32 bit Trap gate */
2140 SegType = "TRAPGATE32";
2141 else if ((SegDesc[1] & 0x1fe0) == 0x0700) /* 16 bit Trap gate */
2142 SegType = "TRAPGATE16";
2143 else
2144 SegType = "UNKNOWN";
2145
2146 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
2147 {
2148 KdbpPrint(" %03d %-10s [NP] [NP] %02d\n",
2149 i / 8, SegType, Dpl);
2150 }
2151 else if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
2152 {
2153 SegSel = SegDesc[0] >> 16;
2154 KdbpPrint(" %03d %-10s 0x%04x %02d\n",
2155 i / 8, SegType, SegSel, Dpl);
2156 }
2157 else
2158 {
2159 SegSel = SegDesc[0] >> 16;
2160 SegBase = (SegDesc[1] & 0xffff0000) | (SegDesc[0] & 0x0000ffff);
2161 KdbpPrint(" %03d %-10s 0x%04x 0x%08x %02d\n",
2162 i / 8, SegType, SegSel, SegBase, Dpl);
2163 }
2164 }
2165 }
2166 else
2167 {
2168 ul = 0;
2169
2170 if (Argv[0][0] == 'g')
2171 {
2172 /* Read GDTR */
2173 Ke386GetGlobalDescriptorTable(&Reg.Limit);
2174 i = 8;
2175 }
2176 else
2177 {
2178 ASSERT(Argv[0][0] == 'l');
2179
2180 /* Read LDTR */
2181 Ke386GetLocalDescriptorTable(&Reg.Limit);
2182 Reg.Base = 0;
2183 i = 0;
2184 ul = 1 << 2;
2185 }
2186
2187 if (Reg.Limit < 7)
2188 {
2189 KdbpPrint("%s descriptor table is empty.\n",
2190 Argv[0][0] == 'g' ? "Global" : "Local");
2191 return TRUE;
2192 }
2193
2194 KdbpPrint("%cDT Base: 0x%08x Limit: 0x%04x\n",
2195 Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit);
2196 KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n");
2197
2198 for (; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
2199 {
2200 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)((ULONG_PTR)Reg.Base + i), sizeof(SegDesc))))
2201 {
2202 KdbpPrint("Couldn't access memory at 0x%p!\n", (ULONG_PTR)Reg.Base + i);
2203 return TRUE;
2204 }
2205
2206 Dpl = ((SegDesc[1] >> 13) & 3);
2207 Type = ((SegDesc[1] >> 8) & 0xf);
2208
2209 SegBase = SegDesc[0] >> 16;
2210 SegBase |= (SegDesc[1] & 0xff) << 16;
2211 SegBase |= SegDesc[1] & 0xff000000;
2212 SegLimit = SegDesc[0] & 0x0000ffff;
2213 SegLimit |= (SegDesc[1] >> 16) & 0xf;
2214
2215 if ((SegDesc[1] & (1 << 23)) != 0)
2216 {
2217 SegLimit *= 4096;
2218 SegLimit += 4095;
2219 }
2220 else
2221 {
2222 SegLimit++;
2223 }
2224
2225 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
2226 {
2227 switch (Type)
2228 {
2229 case 1: SegType = "TSS16(Avl)"; break;
2230 case 2: SegType = "LDT"; break;
2231 case 3: SegType = "TSS16(Busy)"; break;
2232 case 4: SegType = "CALLGATE16"; break;
2233 case 5: SegType = "TASKGATE"; break;
2234 case 6: SegType = "INTGATE16"; break;
2235 case 7: SegType = "TRAPGATE16"; break;
2236 case 9: SegType = "TSS32(Avl)"; break;
2237 case 11: SegType = "TSS32(Busy)"; break;
2238 case 12: SegType = "CALLGATE32"; break;
2239 case 14: SegType = "INTGATE32"; break;
2240 case 15: SegType = "TRAPGATE32"; break;
2241 default: SegType = "UNKNOWN"; break;
2242 }
2243
2244 if (!(Type >= 1 && Type <= 3) &&
2245 Type != 9 && Type != 11)
2246 {
2247 SegBase = 0;
2248 SegLimit = 0;
2249 }
2250 }
2251 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
2252 {
2253 if ((SegDesc[1] & (1 << 22)) != 0)
2254 SegType = "DATA32";
2255 else
2256 SegType = "DATA16";
2257 }
2258 else /* Code segment */
2259 {
2260 if ((SegDesc[1] & (1 << 22)) != 0)
2261 SegType = "CODE32";
2262 else
2263 SegType = "CODE16";
2264 }
2265
2266 if ((SegDesc[1] & (1 << 15)) == 0) /* Not present */
2267 {
2268 KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n",
2269 i / 8, i | Dpl | ul, SegType, Dpl);
2270 }
2271 else
2272 {
2273 KdbpPrint(" %03d 0x%04x %-11s 0x%08x 0x%08x %02d ",
2274 i / 8, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl);
2275
2276 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
2277 {
2278 /* FIXME: Display system segment */
2279 }
2280 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
2281 {
2282 if ((SegDesc[1] & (1 << 10)) != 0) /* Expand-down */
2283 KdbpPrint(" E");
2284
2285 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/W" : " R");
2286
2287 if ((SegDesc[1] & (1 << 8)) != 0)
2288 KdbpPrint(" A");
2289 }
2290 else /* Code segment */
2291 {
2292 if ((SegDesc[1] & (1 << 10)) != 0) /* Conforming */
2293 KdbpPrint(" C");
2294
2295 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/X" : " X");
2296
2297 if ((SegDesc[1] & (1 << 8)) != 0)
2298 KdbpPrint(" A");
2299 }
2300
2301 if ((SegDesc[1] & (1 << 20)) != 0)
2302 KdbpPrint(" AVL");
2303
2304 KdbpPrint("\n");
2305 }
2306 }
2307 }
2308
2309 return TRUE;
2310 }
2311
2312 /*!\brief Displays the KPCR
2313 */
2314 static BOOLEAN
KdbpCmdPcr(ULONG Argc,PCHAR Argv[])2315 KdbpCmdPcr(
2316 ULONG Argc,
2317 PCHAR Argv[])
2318 {
2319 PKIPCR Pcr = (PKIPCR)KeGetPcr();
2320
2321 KdbpPrint("Current PCR is at 0x%p.\n", Pcr);
2322 #ifdef _M_IX86
2323 KdbpPrint(" Tib.ExceptionList: 0x%08x\n"
2324 " Tib.StackBase: 0x%08x\n"
2325 " Tib.StackLimit: 0x%08x\n"
2326 " Tib.SubSystemTib: 0x%08x\n"
2327 " Tib.FiberData/Version: 0x%08x\n"
2328 " Tib.ArbitraryUserPointer: 0x%08x\n"
2329 " Tib.Self: 0x%08x\n"
2330 " SelfPcr: 0x%08x\n"
2331 " PCRCB: 0x%08x\n"
2332 " Irql: 0x%02x\n"
2333 " IRR: 0x%08x\n"
2334 " IrrActive: 0x%08x\n"
2335 " IDR: 0x%08x\n"
2336 " KdVersionBlock: 0x%08x\n"
2337 " IDT: 0x%08x\n"
2338 " GDT: 0x%08x\n"
2339 " TSS: 0x%08x\n"
2340 " MajorVersion: 0x%04x\n"
2341 " MinorVersion: 0x%04x\n"
2342 " SetMember: 0x%08x\n"
2343 " StallScaleFactor: 0x%08x\n"
2344 " Number: 0x%02x\n"
2345 " L2CacheAssociativity: 0x%02x\n"
2346 " VdmAlert: 0x%08x\n"
2347 " L2CacheSize: 0x%08x\n"
2348 " InterruptMode: 0x%08x\n"
2349 , Pcr->NtTib.ExceptionList, Pcr->NtTib.StackBase, Pcr->NtTib.StackLimit,
2350 Pcr->NtTib.SubSystemTib, Pcr->NtTib.FiberData, Pcr->NtTib.ArbitraryUserPointer,
2351 Pcr->NtTib.Self
2352 , Pcr->SelfPcr
2353 , Pcr->Prcb, Pcr->Irql
2354 , Pcr->IRR, Pcr->IrrActive , Pcr->IDR
2355 , Pcr->KdVersionBlock
2356 , Pcr->IDT, Pcr->GDT, Pcr->TSS
2357 , Pcr->MajorVersion, Pcr->MinorVersion
2358 , Pcr->SetMember
2359 , Pcr->StallScaleFactor
2360 , Pcr->Number
2361 , Pcr->SecondLevelCacheAssociativity
2362 , Pcr->VdmAlert
2363 , Pcr->SecondLevelCacheSize
2364 , Pcr->InterruptMode);
2365 #else
2366 KdbpPrint(" GdtBase: 0x%p\n", Pcr->GdtBase);
2367 KdbpPrint(" TssBase: 0x%p\n", Pcr->TssBase);
2368 KdbpPrint(" UserRsp: 0x%p\n", (PVOID)Pcr->UserRsp);
2369 KdbpPrint(" Self: 0x%p\n", Pcr->Self);
2370 KdbpPrint(" CurrentPrcb: 0x%p\n", Pcr->CurrentPrcb);
2371 KdbpPrint(" LockArray: 0x%p\n", Pcr->LockArray);
2372 KdbpPrint(" Used_Self: 0x%p\n", Pcr->Used_Self);
2373 KdbpPrint(" IdtBase: 0x%p\n", Pcr->IdtBase);
2374 KdbpPrint(" Irql: %u\n", Pcr->Irql);
2375 KdbpPrint(" SecondLevelCacheAssociativity: 0x%u\n", Pcr->SecondLevelCacheAssociativity);
2376 KdbpPrint(" ObsoleteNumber: %u\n", Pcr->ObsoleteNumber);
2377 KdbpPrint(" MajorVersion: 0x%x\n", Pcr->MajorVersion);
2378 KdbpPrint(" MinorVersion: 0x%x\n", Pcr->MinorVersion);
2379 KdbpPrint(" StallScaleFactor: 0x%lx\n", Pcr->StallScaleFactor);
2380 KdbpPrint(" SecondLevelCacheSize: 0x%lx\n", Pcr->SecondLevelCacheSize);
2381 KdbpPrint(" KdVersionBlock: 0x%p\n", Pcr->KdVersionBlock);
2382 #endif
2383
2384 return TRUE;
2385 }
2386
2387 #ifdef _M_IX86
2388 /*!\brief Displays the TSS
2389 */
2390 static BOOLEAN
KdbpCmdTss(ULONG Argc,PCHAR Argv[])2391 KdbpCmdTss(
2392 ULONG Argc,
2393 PCHAR Argv[])
2394 {
2395 USHORT TssSelector;
2396 PKTSS Tss = NULL;
2397
2398 if (Argc >= 2)
2399 {
2400 /*
2401 * Specified TSS via its selector [selector] or descriptor address [*descaddr].
2402 * Note that we ignore any other argument values.
2403 */
2404 PCHAR Param, pszNext;
2405 ULONG ulValue;
2406
2407 Param = Argv[1];
2408 if (Argv[1][0] == '*')
2409 ++Param;
2410
2411 ulValue = strtoul(Param, &pszNext, 0);
2412 if (pszNext && *pszNext)
2413 {
2414 KdbpPrint("Invalid TSS specification.\n");
2415 return TRUE;
2416 }
2417
2418 if (Argv[1][0] == '*')
2419 {
2420 /* Descriptor specified */
2421 TssSelector = 0; // Unknown selector!
2422 // TODO: Room for improvement: Find the TSS descriptor
2423 // in the GDT so as to validate it.
2424 Tss = (PKTSS)(ULONG_PTR)ulValue;
2425 if (!Tss)
2426 {
2427 KdbpPrint("Invalid 32-bit TSS descriptor.\n");
2428 return TRUE;
2429 }
2430 }
2431 else
2432 {
2433 /* Selector specified, retrive the corresponding TSS */
2434 TssSelector = (USHORT)ulValue;
2435 Tss = KdbpRetrieveTss(TssSelector, NULL, NULL);
2436 if (!Tss)
2437 {
2438 KdbpPrint("Invalid 32-bit TSS selector.\n");
2439 return TRUE;
2440 }
2441 }
2442 }
2443
2444 if (!Tss)
2445 {
2446 /* If no TSS was specified, use the current TSS descriptor */
2447 TssSelector = Ke386GetTr();
2448 Tss = KeGetPcr()->TSS;
2449 // NOTE: If everything works OK, Tss is the current TSS corresponding to the TR selector.
2450 }
2451
2452 KdbpPrint("%s TSS 0x%04x is at 0x%p.\n",
2453 (Tss == KeGetPcr()->TSS) ? "Current" : "Specified", TssSelector, Tss);
2454 KdbpPrint(" Backlink: 0x%04x\n"
2455 " Ss0:Esp0: 0x%04x:0x%08x\n"
2456 // NOTE: Ss1:Esp1 and Ss2:Esp2: are in the NotUsed1 field.
2457 " CR3: 0x%08x\n"
2458 " EFlags: 0x%08x\n"
2459 " Eax: 0x%08x\n"
2460 " Ebx: 0x%08x\n"
2461 " Ecx: 0x%08x\n"
2462 " Edx: 0x%08x\n"
2463 " Esi: 0x%08x\n"
2464 " Edi: 0x%08x\n"
2465 " Eip: 0x%08x\n"
2466 " Esp: 0x%08x\n"
2467 " Ebp: 0x%08x\n"
2468 " Cs: 0x%04x\n"
2469 " Ss: 0x%04x\n"
2470 " Ds: 0x%04x\n"
2471 " Es: 0x%04x\n"
2472 " Fs: 0x%04x\n"
2473 " Gs: 0x%04x\n"
2474 " LDT: 0x%04x\n"
2475 " Flags: 0x%04x\n"
2476 " IoMapBase: 0x%04x\n",
2477 Tss->Backlink, Tss->Ss0, Tss->Esp0, Tss->CR3, Tss->EFlags,
2478 Tss->Eax, Tss->Ebx, Tss->Ecx, Tss->Edx, Tss->Esi, Tss->Edi,
2479 Tss->Eip, Tss->Esp, Tss->Ebp,
2480 Tss->Cs, Tss->Ss, Tss->Ds, Tss->Es, Tss->Fs, Tss->Gs,
2481 Tss->LDT, Tss->Flags, Tss->IoMapBase);
2482
2483 return TRUE;
2484 }
2485 #endif // _M_IX86
2486
2487 /*!\brief Bugchecks the system.
2488 */
2489 static BOOLEAN
KdbpCmdBugCheck(ULONG Argc,PCHAR Argv[])2490 KdbpCmdBugCheck(
2491 ULONG Argc,
2492 PCHAR Argv[])
2493 {
2494 /* Set the flag and quit looping */
2495 KdbpBugCheckRequested = TRUE;
2496 return FALSE;
2497 }
2498
2499 static BOOLEAN
KdbpCmdReboot(ULONG Argc,PCHAR Argv[])2500 KdbpCmdReboot(
2501 ULONG Argc,
2502 PCHAR Argv[])
2503 {
2504 /* Reboot immediately (we do not return) */
2505 HalReturnToFirmware(HalRebootRoutine);
2506 return FALSE;
2507 }
2508
2509 /*!\brief Display debug messages on screen, with paging.
2510 *
2511 * Keys for per-page view: Home, End, PageUp, Arrow Up, PageDown,
2512 * all others are as PageDown.
2513 */
2514 static BOOLEAN
KdbpCmdDmesg(ULONG Argc,PCHAR Argv[])2515 KdbpCmdDmesg(
2516 ULONG Argc,
2517 PCHAR Argv[])
2518 {
2519 ULONG beg, end;
2520
2521 KdbpIsInDmesgMode = TRUE; /* Toggle logging flag */
2522 if (!KdpDmesgBuffer)
2523 {
2524 KdbpPrint("Dmesg: error, buffer is not allocated! /DEBUGPORT=SCREEN kernel param required for dmesg.\n");
2525 return TRUE;
2526 }
2527
2528 KdbpPrint("*** Dmesg *** TotalWritten=%lu, BufferSize=%lu, CurrentPosition=%lu\n",
2529 KdbDmesgTotalWritten, KdpDmesgBufferSize, KdpDmesgCurrentPosition);
2530
2531 /* Pass data to the pager */
2532 end = KdpDmesgCurrentPosition;
2533 beg = (end + KdpDmesgFreeBytes) % KdpDmesgBufferSize;
2534
2535 /* No roll-overs, and overwritten=lost bytes */
2536 if (KdbDmesgTotalWritten <= KdpDmesgBufferSize)
2537 {
2538 /* Show buffer (KdpDmesgBuffer + beg, num) */
2539 KdbpPager(KdpDmesgBuffer, KdpDmesgCurrentPosition);
2540 }
2541 else
2542 {
2543 /* Show 2 buffers: (KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg)
2544 * and: (KdpDmesgBuffer, end) */
2545 KdbpPager(KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg);
2546 KdbpPrint("*** Dmesg: buffer rollup ***\n");
2547 KdbpPager(KdpDmesgBuffer, end);
2548 }
2549 KdbpPrint("*** Dmesg: end of output ***\n");
2550
2551 KdbpIsInDmesgMode = FALSE; /* Toggle logging flag */
2552
2553 return TRUE;
2554 }
2555
2556 /*!\brief Sets or displays a config variables value.
2557 */
2558 static BOOLEAN
KdbpCmdSet(ULONG Argc,PCHAR Argv[])2559 KdbpCmdSet(
2560 ULONG Argc,
2561 PCHAR Argv[])
2562 {
2563 LONG l;
2564 BOOLEAN First;
2565 PCHAR pend = 0;
2566 KDB_ENTER_CONDITION ConditionFirst = KdbDoNotEnter;
2567 KDB_ENTER_CONDITION ConditionLast = KdbDoNotEnter;
2568
2569 static const PCHAR ExceptionNames[21] =
2570 {
2571 "ZERODEVIDE", "DEBUGTRAP", "NMI", "INT3", "OVERFLOW", "BOUND", "INVALIDOP",
2572 "NOMATHCOP", "DOUBLEFAULT", "RESERVED(9)", "INVALIDTSS", "SEGMENTNOTPRESENT",
2573 "STACKFAULT", "GPF", "PAGEFAULT", "RESERVED(15)", "MATHFAULT", "ALIGNMENTCHECK",
2574 "MACHINECHECK", "SIMDFAULT", "OTHERS"
2575 };
2576
2577 if (Argc == 1)
2578 {
2579 KdbpPrint("Available settings:\n");
2580 KdbpPrint(" syntax [intel|at&t]\n");
2581 KdbpPrint(" condition [exception|*] [first|last] [never|always|kmode|umode]\n");
2582 KdbpPrint(" break_on_module_load [true|false]\n");
2583 }
2584 else if (strcmp(Argv[1], "syntax") == 0)
2585 {
2586 if (Argc == 2)
2587 {
2588 KdbpPrint("syntax = %s\n", KdbUseIntelSyntax ? "intel" : "at&t");
2589 }
2590 else if (Argc >= 3)
2591 {
2592 if (_stricmp(Argv[2], "intel") == 0)
2593 KdbUseIntelSyntax = TRUE;
2594 else if (_stricmp(Argv[2], "at&t") == 0)
2595 KdbUseIntelSyntax = FALSE;
2596 else
2597 KdbpPrint("Unknown syntax '%s'.\n", Argv[2]);
2598 }
2599 }
2600 else if (strcmp(Argv[1], "condition") == 0)
2601 {
2602 if (Argc == 2)
2603 {
2604 KdbpPrint("Conditions: (First) (Last)\n");
2605 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames) - 1; l++)
2606 {
2607 if (!ExceptionNames[l])
2608 continue;
2609
2610 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
2611 ASSERT(FALSE);
2612
2613 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
2614 ASSERT(FALSE);
2615
2616 KdbpPrint(" #%02d %-20s %-8s %-8s\n", l, ExceptionNames[l],
2617 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2618 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2619 }
2620
2621 ASSERT(l == (RTL_NUMBER_OF(ExceptionNames) - 1));
2622 KdbpPrint(" %-20s %-8s %-8s\n", ExceptionNames[l],
2623 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2624 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2625 }
2626 else
2627 {
2628 if (Argc >= 5 && strcmp(Argv[2], "*") == 0) /* Allow * only when setting condition */
2629 {
2630 l = -1;
2631 }
2632 else
2633 {
2634 l = strtoul(Argv[2], &pend, 0);
2635
2636 if (Argv[2] == pend)
2637 {
2638 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++)
2639 {
2640 if (!ExceptionNames[l])
2641 continue;
2642
2643 if (_stricmp(ExceptionNames[l], Argv[2]) == 0)
2644 break;
2645 }
2646 }
2647
2648 if (l >= RTL_NUMBER_OF(ExceptionNames))
2649 {
2650 KdbpPrint("Unknown exception '%s'.\n", Argv[2]);
2651 return TRUE;
2652 }
2653 }
2654
2655 if (Argc > 4)
2656 {
2657 if (_stricmp(Argv[3], "first") == 0)
2658 First = TRUE;
2659 else if (_stricmp(Argv[3], "last") == 0)
2660 First = FALSE;
2661 else
2662 {
2663 KdbpPrint("set condition: second argument must be 'first' or 'last'\n");
2664 return TRUE;
2665 }
2666
2667 if (_stricmp(Argv[4], "never") == 0)
2668 ConditionFirst = KdbDoNotEnter;
2669 else if (_stricmp(Argv[4], "always") == 0)
2670 ConditionFirst = KdbEnterAlways;
2671 else if (_stricmp(Argv[4], "umode") == 0)
2672 ConditionFirst = KdbEnterFromUmode;
2673 else if (_stricmp(Argv[4], "kmode") == 0)
2674 ConditionFirst = KdbEnterFromKmode;
2675 else
2676 {
2677 KdbpPrint("set condition: third argument must be 'never', 'always', 'umode' or 'kmode'\n");
2678 return TRUE;
2679 }
2680
2681 if (!KdbpSetEnterCondition(l, First, ConditionFirst))
2682 {
2683 if (l >= 0)
2684 KdbpPrint("Couldn't change condition for exception #%02d\n", l);
2685 else
2686 KdbpPrint("Couldn't change condition for all exceptions\n", l);
2687 }
2688 }
2689 else /* Argc >= 3 */
2690 {
2691 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
2692 ASSERT(FALSE);
2693
2694 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
2695 ASSERT(FALSE);
2696
2697 if (l < (RTL_NUMBER_OF(ExceptionNames) - 1))
2698 {
2699 KdbpPrint("Condition for exception #%02d (%s): FirstChance %s LastChance %s\n",
2700 l, ExceptionNames[l],
2701 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2702 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2703 }
2704 else
2705 {
2706 KdbpPrint("Condition for all other exceptions: FirstChance %s LastChance %s\n",
2707 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2708 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2709 }
2710 }
2711 }
2712 }
2713 else if (strcmp(Argv[1], "break_on_module_load") == 0)
2714 {
2715 if (Argc == 2)
2716 KdbpPrint("break_on_module_load = %s\n", KdbBreakOnModuleLoad ? "enabled" : "disabled");
2717 else if (Argc >= 3)
2718 {
2719 if (_stricmp(Argv[2], "enable") == 0 || _stricmp(Argv[2], "enabled") == 0 || _stricmp(Argv[2], "true") == 0)
2720 KdbBreakOnModuleLoad = TRUE;
2721 else if (_stricmp(Argv[2], "disable") == 0 || _stricmp(Argv[2], "disabled") == 0 || _stricmp(Argv[2], "false") == 0)
2722 KdbBreakOnModuleLoad = FALSE;
2723 else
2724 KdbpPrint("Unknown setting '%s'.\n", Argv[2]);
2725 }
2726 }
2727 else
2728 {
2729 KdbpPrint("Unknown setting '%s'.\n", Argv[1]);
2730 }
2731
2732 return TRUE;
2733 }
2734
2735 /*!\brief Displays help screen.
2736 */
2737 static BOOLEAN
KdbpCmdHelp(ULONG Argc,PCHAR Argv[])2738 KdbpCmdHelp(
2739 ULONG Argc,
2740 PCHAR Argv[])
2741 {
2742 ULONG i;
2743
2744 KdbpPrint("Kernel debugger commands:\n");
2745 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
2746 {
2747 if (!KdbDebuggerCommands[i].Syntax) /* Command group */
2748 {
2749 if (i > 0)
2750 KdbpPrint("\n");
2751
2752 KdbpPrint("\x1b[7m* %s:\x1b[0m\n", KdbDebuggerCommands[i].Help);
2753 continue;
2754 }
2755
2756 KdbpPrint(" %-20s - %s\n",
2757 KdbDebuggerCommands[i].Syntax,
2758 KdbDebuggerCommands[i].Help);
2759 }
2760
2761 return TRUE;
2762 }
2763
2764
2765 /*
2766 * memrchr(), explicitly defined, since absent in the CRT.
2767 * Reverse memchr()
2768 * Find the last occurrence of 'c' in the buffer 's' of size 'n'.
2769 */
2770 void *
memrchr(const void * s,int c,size_t n)2771 memrchr(const void *s, int c, size_t n)
2772 {
2773 const unsigned char *cp;
2774
2775 if (n != 0)
2776 {
2777 cp = (unsigned char *)s + n;
2778 do
2779 {
2780 if (*(--cp) == (unsigned char)c)
2781 return (void *)cp;
2782 } while (--n != 0);
2783 }
2784 return NULL;
2785 }
2786
2787 /**
2788 * @brief Calculate pointer position for N lines above the current position.
2789 *
2790 * Calculate pointer position for N lines above the current displaying
2791 * position within the given buffer. Used by KdbpPager().
2792 *
2793 * @param[in] Buffer
2794 * Character buffer to operate on.
2795 *
2796 * @param[in] BufLength
2797 * Size of the buffer.
2798 *
2799 * @param[in] pCurPos
2800 * Current position within the buffer.
2801 *
2802 * @return Beginning of the previous page of text.
2803 *
2804 * @note N lines count is hardcoded to the terminal's number of rows.
2805 **/
2806 static PCHAR
CountOnePageUp(_In_ PCCH Buffer,_In_ ULONG BufLength,_In_ PCCH pCurPos,_In_ const SIZE * TermSize)2807 CountOnePageUp(
2808 _In_ PCCH Buffer,
2809 _In_ ULONG BufLength,
2810 _In_ PCCH pCurPos,
2811 _In_ const SIZE* TermSize)
2812 {
2813 PCCH p;
2814 // p0 is initial guess of Page Start
2815 ULONG p0len = TermSize->cx * TermSize->cy;
2816 PCCH p0 = pCurPos - p0len;
2817 PCCH prev_p = p0, p1;
2818 ULONG j;
2819
2820 if (pCurPos < Buffer)
2821 pCurPos = Buffer;
2822 ASSERT(pCurPos <= Buffer + BufLength);
2823
2824 p = memrchr(p0, '\n', p0len);
2825 if (!p)
2826 p = p0;
2827 for (j = TermSize->cy; j--; )
2828 {
2829 int linesCnt;
2830 p1 = memrchr(p0, '\n', p-p0);
2831 prev_p = p;
2832 p = p1;
2833 if (!p)
2834 {
2835 p = prev_p;
2836 if (!p)
2837 p = p0;
2838 break;
2839 }
2840 linesCnt = (TermSize->cx+prev_p-p-2) / TermSize->cx;
2841 if (linesCnt > 1)
2842 j -= linesCnt-1;
2843 }
2844
2845 ASSERT(p != NULL);
2846 ++p;
2847 return (PCHAR)p;
2848 }
2849
2850 static VOID
KdpFilterEscapes(_Inout_ PSTR String)2851 KdpFilterEscapes(
2852 _Inout_ PSTR String)
2853 {
2854 PCHAR p;
2855 SIZE_T i;
2856 size_t len;
2857
2858 while ((p = strrchr(String, '\x1b'))) /* Look for escape character */
2859 {
2860 len = strlen(p);
2861 if (p[1] == '[')
2862 {
2863 i = 2;
2864 while (!isalpha(p[i++]));
2865 memmove(p, p + i, len + 1 - i);
2866 }
2867 else
2868 {
2869 memmove(p, p + 1, len);
2870 }
2871 }
2872 }
2873
2874 /*!\brief Prints the given string with, page by page.
2875 *
2876 * \param Buffer Characters buffer to print.
2877 * \param BufferLen Buffer size.
2878 *
2879 * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
2880 * number of lines required to print a single line from the Buffer in the terminal.
2881 * Maximum length of buffer is limited only by memory size.
2882 * Uses KdbPrintf internally.
2883 *
2884 * Note: BufLength should be greater than (KdTermSize.cx * KdTermSize.cy).
2885 */
2886 static VOID
KdbpPagerInternal(_In_ PCHAR Buffer,_In_ ULONG BufLength,_In_ BOOLEAN DoPage)2887 KdbpPagerInternal(
2888 _In_ PCHAR Buffer,
2889 _In_ ULONG BufLength,
2890 _In_ BOOLEAN DoPage)
2891 {
2892 static BOOLEAN TerminalInitialized = FALSE;
2893 CHAR c;
2894 ULONG ScanCode;
2895 PCHAR p;
2896 SIZE_T i;
2897 LONG RowsPrintedByTerminal;
2898
2899 if (BufLength == 0)
2900 return;
2901
2902 /* Check if the user has aborted output of the current command */
2903 if (KdbOutputAborted)
2904 return;
2905
2906 /* Initialize the terminal */
2907 if (!TerminalInitialized)
2908 {
2909 TerminalInitialized = TRUE;
2910 KdpInitTerminal();
2911 }
2912
2913 /* Refresh terminal size each time when number of printed rows is 0 */
2914 if (KdbNumberOfRowsPrinted == 0)
2915 {
2916 KdpUpdateTerminalSize(&KdTermSize);
2917 }
2918
2919 /* Loop through the strings */
2920 p = Buffer;
2921 while (p[0] != '\0')
2922 {
2923 if (DoPage)
2924 {
2925 if (p > Buffer + BufLength)
2926 {
2927 KdbPrintf("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer + BufLength));
2928 return;
2929 }
2930 }
2931 i = strcspn(p, "\n");
2932
2933 if (DoPage)
2934 {
2935 /* Are we out of buffer? */
2936 if (p + i > Buffer + BufLength)
2937 break; // Leaving pager function
2938 }
2939
2940 /* Calculate the number of lines which will be printed in
2941 * the terminal when outputting the current line. */
2942 if (i > 0)
2943 RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdTermSize.cx;
2944 else
2945 RowsPrintedByTerminal = 0;
2946
2947 if (p[i] == '\n')
2948 RowsPrintedByTerminal++;
2949
2950 //KdbPrintf("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);
2951
2952 /* Display a prompt if we printed one screen full of text */
2953 if (KdTermSize.cy > 0 &&
2954 (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdTermSize.cy)
2955 {
2956 PCSTR Prompt;
2957
2958 /* Disable the repetition of previous command with long many-page output */
2959 KdbRepeatLastCommand = FALSE;
2960
2961 if (KdbNumberOfColsPrinted > 0)
2962 KdbPuts("\n");
2963
2964 if (DoPage)
2965 Prompt = "--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---";
2966 else
2967 Prompt = "--- Press q to abort, any other key to continue ---";
2968
2969 KdbPuts(Prompt);
2970 c = KdpReadTermKey(&ScanCode);
2971 if (DoPage) // Show pressed key
2972 KdbPrintf(" '%c'/scan=%04x\n", c, ScanCode);
2973 else
2974 KdbPuts("\n");
2975
2976 RowsPrintedByTerminal++;
2977
2978 if (c == 'q')
2979 {
2980 KdbOutputAborted = TRUE;
2981 return;
2982 }
2983
2984 if (DoPage)
2985 {
2986 if (ScanCode == KEYSC_END || c == 'e')
2987 {
2988 PCHAR pBufEnd = Buffer + BufLength;
2989 p = CountOnePageUp(Buffer, BufLength, pBufEnd, &KdTermSize);
2990 i = strcspn(p, "\n");
2991 }
2992 else if (ScanCode == KEYSC_PAGEUP ||
2993 ScanCode == KEYSC_ARROWUP || c == 'u')
2994 {
2995 p = CountOnePageUp(Buffer, BufLength, p, &KdTermSize);
2996 i = strcspn(p, "\n");
2997 }
2998 else if (ScanCode == KEYSC_HOME || c == 'h')
2999 {
3000 p = Buffer;
3001 i = strcspn(p, "\n");
3002 }
3003 }
3004
3005 KdbNumberOfRowsPrinted = 0;
3006 KdbNumberOfColsPrinted = 0;
3007 }
3008
3009 /* Insert a NUL after the line and print only the current line */
3010 if (p[i] == '\n' && p[i + 1] != '\0')
3011 {
3012 c = p[i + 1];
3013 p[i + 1] = '\0';
3014 }
3015 else
3016 {
3017 c = '\0';
3018 }
3019
3020 /* Remove escape sequences from the line if there is no terminal connected */
3021 // FIXME: Dangerous operation since we modify the source string!!
3022 if (!KdTermConnected)
3023 KdpFilterEscapes(p);
3024
3025 /* Print the current line */
3026 KdbPuts(p);
3027
3028 /* Restore not null char with saved */
3029 if (c != '\0')
3030 p[i + 1] = c;
3031
3032 /* Set p to the start of the next line and
3033 * remember the number of printed rows/cols */
3034 p += i;
3035 if (p[0] == '\n')
3036 {
3037 p++;
3038 KdbNumberOfColsPrinted = 0;
3039 }
3040 else
3041 {
3042 ASSERT(p[0] == '\0');
3043 KdbNumberOfColsPrinted += i;
3044 }
3045
3046 KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
3047 }
3048 }
3049
3050 /*!\brief Prints the given string with, page by page.
3051 *
3052 * \param Buffer Characters buffer to print.
3053 * \param BufferLen Buffer size.
3054 *
3055 * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
3056 * number of lines required to print a single line from the Buffer in the terminal.
3057 * Maximum length of buffer is limited only by memory size.
3058 * Uses KdbPrintf internally.
3059 *
3060 * Note: BufLength should be greater than (KdTermSize.cx * KdTermSize.cy).
3061 */
3062 VOID
KdbpPager(_In_ PCHAR Buffer,_In_ ULONG BufLength)3063 KdbpPager(
3064 _In_ PCHAR Buffer,
3065 _In_ ULONG BufLength)
3066 {
3067 /* Call the internal function */
3068 KdbpPagerInternal(Buffer, BufLength, TRUE);
3069 }
3070
3071 /*!\brief Prints the given string with printf-like formatting.
3072 *
3073 * \param Format Format of the string/arguments.
3074 * \param ... Variable number of arguments matching the format specified in \a Format.
3075 *
3076 * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
3077 * number of lines required to print a single line from the Buffer in the terminal.
3078 * Prints maximum 4096 chars, because of its buffer size.
3079 */
3080 VOID
KdbpPrint(_In_ PSTR Format,_In_...)3081 KdbpPrint(
3082 _In_ PSTR Format,
3083 _In_ ...)
3084 {
3085 static CHAR Buffer[4096];
3086 ULONG Length;
3087 va_list ap;
3088
3089 /* Check if the user has aborted output of the current command */
3090 if (KdbOutputAborted)
3091 return;
3092
3093 /* Build the string */
3094 va_start(ap, Format);
3095 Length = _vsnprintf(Buffer, sizeof(Buffer) - 1, Format, ap);
3096 Buffer[Length] = '\0';
3097 va_end(ap);
3098
3099 /* Actually print it */
3100 KdbpPagerInternal(Buffer, Length, FALSE);
3101 }
3102
3103 VOID
KdbpPrintUnicodeString(_In_ PCUNICODE_STRING String)3104 KdbpPrintUnicodeString(
3105 _In_ PCUNICODE_STRING String)
3106 {
3107 ULONG i;
3108
3109 if ((String == NULL) || (String->Buffer == NULL))
3110 {
3111 KdbpPrint("<NULL>");
3112 return;
3113 }
3114
3115 for (i = 0; i < String->Length / sizeof(WCHAR); i++)
3116 {
3117 KdbpPrint("%c", (CHAR)String->Buffer[i]);
3118 }
3119 }
3120
3121
3122 BOOLEAN
3123 NTAPI
KdbRegisterCliCallback(PVOID Callback,BOOLEAN Deregister)3124 KdbRegisterCliCallback(
3125 PVOID Callback,
3126 BOOLEAN Deregister)
3127 {
3128 ULONG i;
3129
3130 /* Loop all entries */
3131 for (i = 0; i < _countof(KdbCliCallbacks); i++)
3132 {
3133 /* Check if deregistering was requested */
3134 if (Deregister)
3135 {
3136 /* Check if this entry is the one that was registered */
3137 if (KdbCliCallbacks[i] == Callback)
3138 {
3139 /* Delete it and report success */
3140 KdbCliCallbacks[i] = NULL;
3141 return TRUE;
3142 }
3143 }
3144 else
3145 {
3146 /* Check if this entry is free */
3147 if (KdbCliCallbacks[i] == NULL)
3148 {
3149 /* Set it and and report success */
3150 KdbCliCallbacks[i] = Callback;
3151 return TRUE;
3152 }
3153 }
3154 }
3155
3156 /* Unsuccessful */
3157 return FALSE;
3158 }
3159
3160 /*! \brief Invokes registered CLI callbacks until one of them handled the
3161 * Command.
3162 *
3163 * \param Command - Command line to parse and execute if possible.
3164 * \param Argc - Number of arguments in Argv
3165 * \param Argv - Array of strings, each of them containing one argument.
3166 *
3167 * \return TRUE, if the command was handled, FALSE if it was not handled.
3168 */
3169 static
3170 BOOLEAN
KdbpInvokeCliCallbacks(IN PCHAR Command,IN ULONG Argc,IN PCHAR Argv[])3171 KdbpInvokeCliCallbacks(
3172 IN PCHAR Command,
3173 IN ULONG Argc,
3174 IN PCHAR Argv[])
3175 {
3176 ULONG i;
3177
3178 /* Loop all entries */
3179 for (i = 0; i < _countof(KdbCliCallbacks); i++)
3180 {
3181 /* Check if this entry is registered */
3182 if (KdbCliCallbacks[i])
3183 {
3184 /* Invoke the callback and check if it handled the command */
3185 if (KdbCliCallbacks[i](Command, Argc, Argv))
3186 {
3187 return TRUE;
3188 }
3189 }
3190 }
3191
3192 /* None of the callbacks handled the command */
3193 return FALSE;
3194 }
3195
3196
3197 /*!\brief Parses command line and executes command if found
3198 *
3199 * \param Command Command line to parse and execute if possible.
3200 *
3201 * \retval TRUE Don't continue execution.
3202 * \retval FALSE Continue execution (leave KDB)
3203 */
3204 static BOOLEAN
KdbpDoCommand(IN PCHAR Command)3205 KdbpDoCommand(
3206 IN PCHAR Command)
3207 {
3208 BOOLEAN Continue = TRUE;
3209 SIZE_T i;
3210 PCHAR p;
3211 ULONG Argc;
3212 // FIXME: for what do we need a 1024 characters command line and 256 tokens?
3213 static PCHAR Argv[256];
3214 static CHAR OrigCommand[1024];
3215
3216 RtlStringCbCopyA(OrigCommand, sizeof(OrigCommand), Command);
3217
3218 Argc = 0;
3219 p = Command;
3220
3221 for (;;)
3222 {
3223 while (*p == '\t' || *p == ' ')
3224 p++;
3225
3226 if (*p == '\0')
3227 break;
3228
3229 i = strcspn(p, "\t ");
3230 Argv[Argc++] = p;
3231 p += i;
3232 if (*p == '\0')
3233 break;
3234
3235 *p = '\0';
3236 p++;
3237 }
3238
3239 if (Argc < 1)
3240 return TRUE;
3241
3242 /* Reset the pager state: number of printed rows/cols and aborted output flag */
3243 KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
3244 KdbOutputAborted = FALSE;
3245
3246 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
3247 {
3248 if (!KdbDebuggerCommands[i].Name)
3249 continue;
3250
3251 if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0)
3252 {
3253 Continue = KdbDebuggerCommands[i].Fn(Argc, Argv);
3254 goto Done;
3255 }
3256 }
3257
3258 /* Now invoke the registered callbacks */
3259 if (KdbpInvokeCliCallbacks(Command, Argc, Argv))
3260 goto Done;
3261
3262 KdbPrintf("Command '%s' is unknown.\n", OrigCommand);
3263
3264 Done:
3265 KdbOutputAborted = FALSE;
3266 return Continue;
3267 }
3268
3269 /*!\brief KDB Main Loop.
3270 *
3271 * \param EnteredOnSingleStep TRUE if KDB was entered on single step.
3272 */
3273 VOID
KdbpCliMainLoop(IN BOOLEAN EnteredOnSingleStep)3274 KdbpCliMainLoop(
3275 IN BOOLEAN EnteredOnSingleStep)
3276 {
3277 BOOLEAN Continue = TRUE;
3278 static CHAR Command[1024];
3279 static CHAR LastCommand[1024] = "";
3280
3281 // FIXME HACK: SYSREG SUPPORT CORE-19807 -- Emit a backtrace.
3282 // TODO: Remove once SYSREG "bt" command emission is fixed!
3283 #if 1
3284 KdbpDoCommand("bt");
3285 #endif
3286
3287 if (EnteredOnSingleStep)
3288 {
3289 if (!KdbSymPrintAddress((PVOID)KeGetContextPc(KdbCurrentTrapFrame), KdbCurrentTrapFrame))
3290 KdbPrintf("<%p>", KeGetContextPc(KdbCurrentTrapFrame));
3291
3292 KdbPuts(": ");
3293 if (KdbpDisassemble(KeGetContextPc(KdbCurrentTrapFrame), KdbUseIntelSyntax) < 0)
3294 KdbPuts("<INVALID>");
3295 KdbPuts("\n");
3296 }
3297 else
3298 {
3299 /* Preceding this message is one of the "Entered debugger..." banners */
3300 // KdbPuts("\nEntered debugger\n");
3301 KdbPuts("\nType \"help\" for a list of commands.\n");
3302 }
3303
3304 /* Main loop */
3305 while (Continue)
3306 {
3307 /*
3308 * Print the prompt and read a command.
3309 * Repeat the last one if the user pressed Enter.
3310 * This reduces the risk of RSI when single-stepping!
3311 */
3312 // TEMP HACK! Issue an empty string instead of duplicating "kdb:>"
3313 SIZE_T CmdLen = KdbPrompt(/*KdbPromptStr.Buffer*/"", Command, sizeof(Command));
3314 if (CmdLen == 0)
3315 {
3316 /* Nothing received but the user didn't press Enter, retry */
3317 continue;
3318 }
3319 else if (CmdLen > 1) // i.e. (*Command != ANSI_NULL)
3320 {
3321 /* Save this new last command */
3322 KdbRepeatLastCommand = TRUE;
3323 RtlStringCbCopyA(LastCommand, sizeof(LastCommand), Command);
3324
3325 /* Remember it */
3326 KdbpCommandHistoryAppend(Command);
3327 }
3328 else if (KdbRepeatLastCommand)
3329 {
3330 /* The user directly pressed Enter */
3331 RtlStringCbCopyA(Command, sizeof(Command), LastCommand);
3332 }
3333
3334 /* Invoke the command */
3335 Continue = KdbpDoCommand(Command);
3336 }
3337 }
3338
3339 /**
3340 * @brief
3341 * Interprets the KDBinit file from the \SystemRoot\System32\drivers\etc
3342 * directory, that has been loaded by KdbpCliInit().
3343 *
3344 * This function is used to interpret the init file in the debugger context
3345 * with a trap frame set up. KdbpCliInit() enters the debugger by calling
3346 * DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C). In turn, this will call
3347 * KdbEnterDebuggerException() which will finally call this function if
3348 * KdbInitFileBuffer is not NULL.
3349 **/
3350 VOID
KdbpCliInterpretInitFile(VOID)3351 KdbpCliInterpretInitFile(VOID)
3352 {
3353 PCHAR p1, p2;
3354
3355 p1 = InterlockedExchangePointer((PVOID*)&KdbInitFileBuffer, NULL);
3356 if (!p1)
3357 return;
3358
3359 /* Execute the commands in the init file */
3360 KdbPuts("KDB: Executing KDBinit file...\n");
3361 while (p1[0] != '\0')
3362 {
3363 size_t i = strcspn(p1, "\r\n");
3364 if (i > 0)
3365 {
3366 CHAR c = p1[i];
3367 p1[i] = '\0';
3368
3369 /* Look for "break" command and comments */
3370 p2 = p1;
3371 while (isspace(p2[0]))
3372 p2++;
3373
3374 if (strncmp(p2, "break", sizeof("break")-1) == 0 &&
3375 (p2[sizeof("break")-1] == '\0' || isspace(p2[sizeof("break")-1])))
3376 {
3377 /* Run the interactive debugger loop */
3378 KdbpCliMainLoop(FALSE);
3379 }
3380 else if (p2[0] != '#' && p2[0] != '\0') /* Ignore empty lines and comments */
3381 {
3382 /* Invoke the command */
3383 KdbpDoCommand(p1);
3384 }
3385
3386 p1[i] = c;
3387 }
3388
3389 p1 += i;
3390 while (p1[0] == '\r' || p1[0] == '\n')
3391 p1++;
3392 }
3393 KdbPuts("KDB: KDBinit executed\n");
3394 }
3395
3396 /**
3397 * @brief Called when KDB is initialized.
3398 *
3399 * Loads the KDBinit file from the \SystemRoot\System32\drivers\etc
3400 * directory and interprets it, by calling back into the debugger.
3401 **/
3402 NTSTATUS
KdbpCliInit(VOID)3403 KdbpCliInit(VOID)
3404 {
3405 NTSTATUS Status;
3406 OBJECT_ATTRIBUTES ObjectAttributes;
3407 UNICODE_STRING FileName;
3408 IO_STATUS_BLOCK Iosb;
3409 FILE_STANDARD_INFORMATION FileStdInfo;
3410 HANDLE hFile = NULL;
3411 ULONG FileSize;
3412 PCHAR FileBuffer;
3413
3414 /* Don't load the KDBinit file if its buffer is already lying around */
3415 if (KdbInitFileBuffer)
3416 return STATUS_SUCCESS;
3417
3418 /* Initialize the object attributes */
3419 RtlInitUnicodeString(&FileName, L"\\SystemRoot\\System32\\drivers\\etc\\KDBinit");
3420 InitializeObjectAttributes(&ObjectAttributes,
3421 &FileName,
3422 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3423 NULL,
3424 NULL);
3425
3426 /* Open the file */
3427 Status = ZwOpenFile(&hFile, FILE_READ_DATA | SYNCHRONIZE,
3428 &ObjectAttributes, &Iosb, 0,
3429 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
3430 FILE_NO_INTERMEDIATE_BUFFERING);
3431 if (!NT_SUCCESS(Status))
3432 {
3433 DPRINT("Could not open %wZ (Status 0x%lx)\n", &FileName, Status);
3434 return Status;
3435 }
3436
3437 /* Get the size of the file */
3438 Status = ZwQueryInformationFile(hFile, &Iosb,
3439 &FileStdInfo, sizeof(FileStdInfo),
3440 FileStandardInformation);
3441 if (!NT_SUCCESS(Status))
3442 {
3443 ZwClose(hFile);
3444 DPRINT1("Could not query size of %wZ (Status 0x%lx)\n", &FileName, Status);
3445 return Status;
3446 }
3447 FileSize = FileStdInfo.EndOfFile.u.LowPart;
3448
3449 /* Allocate memory for the file (add 1 byte for terminating NUL) */
3450 FileBuffer = ExAllocatePool(NonPagedPool, FileSize + 1);
3451 if (!FileBuffer)
3452 {
3453 ZwClose(hFile);
3454 DPRINT1("Could not allocate %lu bytes for KDBinit file\n", FileSize);
3455 return Status;
3456 }
3457
3458 /* Load file into memory */
3459 Status = ZwReadFile(hFile, NULL, NULL, NULL, &Iosb,
3460 FileBuffer, FileSize, NULL, NULL);
3461 ZwClose(hFile);
3462
3463 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
3464 {
3465 ExFreePool(FileBuffer);
3466 DPRINT1("Could not read KDBinit file into memory (Status 0x%lx)\n", Status);
3467 return Status;
3468 }
3469
3470 FileSize = min(FileSize, (ULONG)Iosb.Information);
3471 FileBuffer[FileSize] = ANSI_NULL;
3472
3473 /* Interpret the KDBinit file by calling back into the debugger */
3474 InterlockedExchangePointer((PVOID*)&KdbInitFileBuffer, FileBuffer);
3475 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
3476 InterlockedExchangePointer((PVOID*)&KdbInitFileBuffer, NULL);
3477
3478 ExFreePool(FileBuffer);
3479
3480 return STATUS_SUCCESS;
3481 }
3482
3483
3484 /**
3485 * @brief Debug logger function.
3486 *
3487 * This function writes text strings into KdpDmesgBuffer, using it as
3488 * a circular buffer. KdpDmesgBuffer contents can be later (re)viewed
3489 * using the dmesg command. KdbDebugPrint() protects KdpDmesgBuffer
3490 * from simultaneous writes by use of KdpDmesgLogSpinLock.
3491 **/
3492 static VOID
3493 NTAPI
KdbDebugPrint(_In_ PCCH String,_In_ ULONG Length)3494 KdbDebugPrint(
3495 _In_ PCCH String,
3496 _In_ ULONG Length)
3497 {
3498 KIRQL OldIrql;
3499 ULONG beg, end, num;
3500
3501 /* Avoid recursive calling if we already are in Dmesg mode */
3502 if (KdbpIsInDmesgMode)
3503 return;
3504
3505 if (KdpDmesgBuffer == NULL)
3506 return;
3507
3508 /* Acquire the printing spinlock without waiting at raised IRQL */
3509 OldIrql = KdbpAcquireLock(&KdpDmesgLogSpinLock);
3510
3511 beg = KdpDmesgCurrentPosition;
3512 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize); */
3513 num = min(Length, KdpDmesgFreeBytes);
3514 if (num != 0)
3515 {
3516 end = (beg + num) % KdpDmesgBufferSize;
3517 if (end > beg)
3518 {
3519 RtlCopyMemory(KdpDmesgBuffer + beg, String, Length);
3520 }
3521 else
3522 {
3523 RtlCopyMemory(KdpDmesgBuffer + beg, String, KdpDmesgBufferSize - beg);
3524 RtlCopyMemory(KdpDmesgBuffer, String + (KdpDmesgBufferSize - beg), end);
3525 }
3526 KdpDmesgCurrentPosition = end;
3527
3528 /* Counting the total bytes written */
3529 KdbDmesgTotalWritten += num;
3530 }
3531
3532 /* Release the spinlock */
3533 KdbpReleaseLock(&KdpDmesgLogSpinLock, OldIrql);
3534
3535 /* Optional step(?): find out a way to notify about buffer exhaustion,
3536 * and possibly fall into kbd to use dmesg command: user will read
3537 * debug strings before they will be wiped over by next writes. */
3538 }
3539
3540 /**
3541 * @brief Initializes the KDBG debugger.
3542 *
3543 * @param[in] DispatchTable
3544 * Pointer to the KD dispatch table.
3545 *
3546 * @param[in] BootPhase
3547 * Phase of initialization.
3548 *
3549 * @return A status value.
3550 * @note Also known as "KdpKdbgInit".
3551 **/
3552 NTSTATUS
3553 NTAPI
KdbInitialize(_In_ PKD_DISPATCH_TABLE DispatchTable,_In_ ULONG BootPhase)3554 KdbInitialize(
3555 _In_ PKD_DISPATCH_TABLE DispatchTable,
3556 _In_ ULONG BootPhase)
3557 {
3558 /* Saves the different symbol-loading status across boot phases */
3559 static ULONG LoadSymbols = 0;
3560
3561 if (BootPhase == 0)
3562 {
3563 /* Write out the functions that we support for now */
3564 DispatchTable->KdpPrintRoutine = KdbDebugPrint;
3565
3566 /* Check if we have a command line */
3567 if (KeLoaderBlock && KeLoaderBlock->LoadOptions)
3568 {
3569 /* Get the KDBG Settings */
3570 KdbpGetCommandLineSettings(KeLoaderBlock->LoadOptions);
3571 }
3572
3573 /* Register for BootPhase 1 initialization and as a Provider */
3574 DispatchTable->KdpInitRoutine = KdbInitialize;
3575 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
3576 }
3577 else if (BootPhase == 1)
3578 {
3579 /* Register for later BootPhase 2 reinitialization */
3580 DispatchTable->KdpInitRoutine = KdbInitialize;
3581
3582 /* Initialize Dmesg support */
3583
3584 /* Allocate a buffer for Dmesg log buffer. +1 for terminating null,
3585 * see kdbp_cli.c:KdbpCmdDmesg()/2 */
3586 KdpDmesgBuffer = ExAllocatePoolZero(NonPagedPool,
3587 KdpDmesgBufferSize + 1,
3588 TAG_KDBG);
3589 /* Ignore failure if KdpDmesgBuffer is NULL */
3590 KdpDmesgFreeBytes = KdpDmesgBufferSize;
3591 KdbDmesgTotalWritten = 0;
3592
3593 /* Initialize spinlock */
3594 KeInitializeSpinLock(&KdpDmesgLogSpinLock);
3595 }
3596
3597 /* Initialize symbols support in BootPhase 0 and 1 */
3598 if (BootPhase <= 1)
3599 {
3600 LoadSymbols <<= 1;
3601 LoadSymbols |= KdbSymInit(BootPhase);
3602 }
3603
3604 if (BootPhase == 1)
3605 {
3606 /* Announce ourselves */
3607 CHAR buffer[60];
3608 RtlStringCbPrintfA(buffer, sizeof(buffer),
3609 " KDBG debugger enabled - %s\r\n",
3610 !(LoadSymbols & 0x2) ? "No symbols loaded" :
3611 !(LoadSymbols & 0x1) ? "Kernel symbols loaded"
3612 : "Loading symbols");
3613 HalDisplayString(buffer);
3614 }
3615
3616 if (BootPhase >= 2)
3617 {
3618 /* I/O is now set up for disk access: load the KDBinit file */
3619 NTSTATUS Status = KdbpCliInit();
3620
3621 /* Schedule an I/O reinitialization if needed */
3622 if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
3623 Status == STATUS_OBJECT_PATH_NOT_FOUND)
3624 {
3625 DispatchTable->KdpInitRoutine = KdbInitialize;
3626 }
3627 }
3628
3629 return STATUS_SUCCESS;
3630 }
3631
3632 /* EOF */
3633