Lazy port of kdbg to AMD64. Kdbg is very x86 specific but thankfully amd64 isn't...
[reactos.git] / reactos / ntoskrnl / kdbg / kdb_cli.c
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/dbg/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 #define NDEBUG
34 #include <debug.h>
35
36 /* DEFINES *******************************************************************/
37 //hack for amd64
38 #define NPX_STATE_NOT_LOADED 0xA
39 #define NPX_STATE_LOADED 0x0
40
41 #define KEY_BS 8
42 #define KEY_ESC 27
43 #define KEY_DEL 127
44
45 #define KEY_SCAN_UP 72
46 #define KEY_SCAN_DOWN 80
47
48 #define KDB_ENTER_CONDITION_TO_STRING(cond) \
49 ((cond) == KdbDoNotEnter ? "never" : \
50 ((cond) == KdbEnterAlways ? "always" : \
51 ((cond) == KdbEnterFromKmode ? "kmode" : "umode")))
52
53 #define KDB_ACCESS_TYPE_TO_STRING(type) \
54 ((type) == KdbAccessRead ? "read" : \
55 ((type) == KdbAccessWrite ? "write" : \
56 ((type) == KdbAccessReadWrite ? "rdwr" : "exec")))
57
58 #define NPX_STATE_TO_STRING(state) \
59 ((state) == NPX_STATE_LOADED ? "Loaded" : \
60 ((state) == NPX_STATE_NOT_LOADED ? "Not loaded" : "Unknown"))
61
62 /* PROTOTYPES ****************************************************************/
63
64 static BOOLEAN KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]);
65 static BOOLEAN KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[]);
66 static BOOLEAN KdbpCmdRegs(ULONG Argc, PCHAR Argv[]);
67 static BOOLEAN KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]);
68
69 static BOOLEAN KdbpCmdContinue(ULONG Argc, PCHAR Argv[]);
70 static BOOLEAN KdbpCmdStep(ULONG Argc, PCHAR Argv[]);
71 static BOOLEAN KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[]);
72 static BOOLEAN KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[]);
73 static BOOLEAN KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]);
74
75 static BOOLEAN KdbpCmdThread(ULONG Argc, PCHAR Argv[]);
76 static BOOLEAN KdbpCmdProc(ULONG Argc, PCHAR Argv[]);
77
78 static BOOLEAN KdbpCmdMod(ULONG Argc, PCHAR Argv[]);
79 static BOOLEAN KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]);
80 static BOOLEAN KdbpCmdPcr(ULONG Argc, PCHAR Argv[]);
81 static BOOLEAN KdbpCmdTss(ULONG Argc, PCHAR Argv[]);
82
83 static BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]);
84 static BOOLEAN KdbpCmdFilter(ULONG Argc, PCHAR Argv[]);
85 static BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
86 static BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
87
88 /* GLOBALS *******************************************************************/
89
90 static BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */
91 static BOOLEAN KdbBreakOnModuleLoad = FALSE; /* Set to TRUE to break into KDB when a module is loaded */
92
93 static CHAR KdbCommandHistoryBuffer[2048]; /* Command history string ringbuffer */
94 static PCHAR KdbCommandHistory[sizeof(KdbCommandHistoryBuffer) / 8] = { NULL }; /* Command history ringbuffer */
95 static LONG KdbCommandHistoryBufferIndex = 0;
96 static LONG KdbCommandHistoryIndex = 0;
97
98 static ULONG KdbNumberOfRowsPrinted = 0;
99 static ULONG KdbNumberOfColsPrinted = 0;
100 static BOOLEAN KdbOutputAborted = FALSE;
101 static LONG KdbNumberOfRowsTerminal = -1;
102 static LONG KdbNumberOfColsTerminal = -1;
103
104 PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */
105 BOOLEAN KdbpBugCheckRequested = FALSE;
106
107 static const struct
108 {
109 PCHAR Name;
110 PCHAR Syntax;
111 PCHAR Help;
112 BOOLEAN (*Fn)(ULONG Argc, PCHAR Argv[]);
113 } KdbDebuggerCommands[] = {
114 /* Data */
115 { NULL, NULL, "Data", NULL },
116 { "?", "? expression", "Evaluate expression.", KdbpCmdEvalExpression },
117 { "disasm", "disasm [address] [L count]", "Disassemble count instructions at address.", KdbpCmdDisassembleX },
118 { "x", "x [address] [L count]", "Display count dwords, starting at addr.", KdbpCmdDisassembleX },
119 { "regs", "regs", "Display general purpose registers.", KdbpCmdRegs },
120 { "cregs", "cregs", "Display control registers.", KdbpCmdRegs },
121 { "sregs", "sregs", "Display status registers.", KdbpCmdRegs },
122 { "dregs", "dregs", "Display debug registers.", KdbpCmdRegs },
123 { "bt", "bt [*frameaddr|thread id]", "Prints current backtrace or from given frame addr", KdbpCmdBackTrace },
124
125 /* Flow control */
126 { NULL, NULL, "Flow control", NULL },
127 { "cont", "cont", "Continue execution (leave debugger)", KdbpCmdContinue },
128 { "step", "step [count]", "Execute single instructions, stepping into interrupts.", KdbpCmdStep },
129 { "next", "next [count]", "Execute single instructions, skipping calls and reps.", KdbpCmdStep },
130 { "bl", "bl", "List breakpoints.", KdbpCmdBreakPointList },
131 { "be", "be [breakpoint]", "Enable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
132 { "bd", "bd [breakpoint]", "Disable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
133 { "bc", "bc [breakpoint]", "Clear breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
134 { "bpx", "bpx [address] [IF condition]", "Set software execution breakpoint at address.", KdbpCmdBreakPoint },
135 { "bpm", "bpm [r|w|rw|x] [byte|word|dword] [address] [IF condition]", "Set memory breakpoint at address.", KdbpCmdBreakPoint },
136
137 /* Process/Thread */
138 { NULL, NULL, "Process/Thread", NULL },
139 { "thread", "thread [list[ pid]|[attach ]tid]", "List threads in current or specified process, display thread with given id or attach to thread.", KdbpCmdThread },
140 { "proc", "proc [list|[attach ]pid]", "List processes, display process with given id or attach to process.", KdbpCmdProc },
141
142 /* System information */
143 { NULL, NULL, "System info", NULL },
144 { "mod", "mod [address]", "List all modules or the one containing address.", KdbpCmdMod },
145 { "gdt", "gdt", "Display global descriptor table.", KdbpCmdGdtLdtIdt },
146 { "ldt", "ldt", "Display local descriptor table.", KdbpCmdGdtLdtIdt },
147 { "idt", "idt", "Display interrupt descriptor table.", KdbpCmdGdtLdtIdt },
148 { "pcr", "pcr", "Display processor control region.", KdbpCmdPcr },
149 { "tss", "tss", "Display task state segment.", KdbpCmdTss },
150
151 /* Others */
152 { NULL, NULL, "Others", NULL },
153 { "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck },
154 { "filter", "filter [error|warning|trace|info|level]+|-[componentname|default]", "Enable/disable debug channels", KdbpCmdFilter },
155 { "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet },
156 { "help", "help", "Display help screen.", KdbpCmdHelp }
157 };
158
159 /* FUNCTIONS *****************************************************************/
160
161 /*!\brief Transform a component name to an integer
162 *
163 * \param ComponentName The name of the component.
164 * \param ComponentId Receives the component id on success.
165 *
166 * \retval TRUE Success.
167 * \retval FALSE Failure.
168 */
169 static BOOLEAN
170 KdbpGetComponentId(
171 IN PCCH ComponentName,
172 OUT PULONG ComponentId)
173 {
174 ULONG i;
175
176 static struct
177 {
178 PCCH Name;
179 ULONG Id;
180 }
181 ComponentTable[] =
182 {
183 { "DEFAULT", DPFLTR_DEFAULT_ID },
184 { "SYSTEM", DPFLTR_SYSTEM_ID },
185 { "SMSS", DPFLTR_SMSS_ID },
186 { "SETUP", DPFLTR_SETUP_ID },
187 { "NTFS", DPFLTR_NTFS_ID },
188 { "FSTUB", DPFLTR_FSTUB_ID },
189 { "CRASHDUMP", DPFLTR_CRASHDUMP_ID },
190 { "CDAUDIO", DPFLTR_CDAUDIO_ID },
191 { "CDROM", DPFLTR_CDROM_ID },
192 { "CLASSPNP", DPFLTR_CLASSPNP_ID },
193 { "DISK", DPFLTR_DISK_ID },
194 { "REDBOOK", DPFLTR_REDBOOK_ID },
195 { "STORPROP", DPFLTR_STORPROP_ID },
196 { "SCSIPORT", DPFLTR_SCSIPORT_ID },
197 { "SCSIMINIPORT", DPFLTR_SCSIMINIPORT_ID },
198 { "CONFIG", DPFLTR_CONFIG_ID },
199 { "I8042PRT", DPFLTR_I8042PRT_ID },
200 { "SERMOUSE", DPFLTR_SERMOUSE_ID },
201 { "LSERMOUS", DPFLTR_LSERMOUS_ID },
202 { "KBDHID", DPFLTR_KBDHID_ID },
203 { "MOUHID", DPFLTR_MOUHID_ID },
204 { "KBDCLASS", DPFLTR_KBDCLASS_ID },
205 { "MOUCLASS", DPFLTR_MOUCLASS_ID },
206 { "TWOTRACK", DPFLTR_TWOTRACK_ID },
207 { "WMILIB", DPFLTR_WMILIB_ID },
208 { "ACPI", DPFLTR_ACPI_ID },
209 { "AMLI", DPFLTR_AMLI_ID },
210 { "HALIA64", DPFLTR_HALIA64_ID },
211 { "VIDEO", DPFLTR_VIDEO_ID },
212 { "SVCHOST", DPFLTR_SVCHOST_ID },
213 { "VIDEOPRT", DPFLTR_VIDEOPRT_ID },
214 { "TCPIP", DPFLTR_TCPIP_ID },
215 { "DMSYNTH", DPFLTR_DMSYNTH_ID },
216 { "NTOSPNP", DPFLTR_NTOSPNP_ID },
217 { "FASTFAT", DPFLTR_FASTFAT_ID },
218 { "SAMSS", DPFLTR_SAMSS_ID },
219 { "PNPMGR", DPFLTR_PNPMGR_ID },
220 { "NETAPI", DPFLTR_NETAPI_ID },
221 { "SCSERVER", DPFLTR_SCSERVER_ID },
222 { "SCCLIENT", DPFLTR_SCCLIENT_ID },
223 { "SERIAL", DPFLTR_SERIAL_ID },
224 { "SERENUM", DPFLTR_SERENUM_ID },
225 { "UHCD", DPFLTR_UHCD_ID },
226 { "BOOTOK", DPFLTR_BOOTOK_ID },
227 { "BOOTVRFY", DPFLTR_BOOTVRFY_ID },
228 { "RPCPROXY", DPFLTR_RPCPROXY_ID },
229 { "AUTOCHK", DPFLTR_AUTOCHK_ID },
230 { "DCOMSS", DPFLTR_DCOMSS_ID },
231 { "UNIMODEM", DPFLTR_UNIMODEM_ID },
232 { "SIS", DPFLTR_SIS_ID },
233 { "FLTMGR", DPFLTR_FLTMGR_ID },
234 { "WMICORE", DPFLTR_WMICORE_ID },
235 { "BURNENG", DPFLTR_BURNENG_ID },
236 { "IMAPI", DPFLTR_IMAPI_ID },
237 { "SXS", DPFLTR_SXS_ID },
238 { "FUSION", DPFLTR_FUSION_ID },
239 { "IDLETASK", DPFLTR_IDLETASK_ID },
240 { "SOFTPCI", DPFLTR_SOFTPCI_ID },
241 { "TAPE", DPFLTR_TAPE_ID },
242 { "MCHGR", DPFLTR_MCHGR_ID },
243 { "IDEP", DPFLTR_IDEP_ID },
244 { "PCIIDE", DPFLTR_PCIIDE_ID },
245 { "FLOPPY", DPFLTR_FLOPPY_ID },
246 { "FDC", DPFLTR_FDC_ID },
247 { "TERMSRV", DPFLTR_TERMSRV_ID },
248 { "W32TIME", DPFLTR_W32TIME_ID },
249 { "PREFETCHER", DPFLTR_PREFETCHER_ID },
250 { "RSFILTER", DPFLTR_RSFILTER_ID },
251 { "FCPORT", DPFLTR_FCPORT_ID },
252 { "PCI", DPFLTR_PCI_ID },
253 { "DMIO", DPFLTR_DMIO_ID },
254 { "DMCONFIG", DPFLTR_DMCONFIG_ID },
255 { "DMADMIN", DPFLTR_DMADMIN_ID },
256 { "WSOCKTRANSPORT", DPFLTR_WSOCKTRANSPORT_ID },
257 { "VSS", DPFLTR_VSS_ID },
258 { "PNPMEM", DPFLTR_PNPMEM_ID },
259 { "PROCESSOR", DPFLTR_PROCESSOR_ID },
260 { "DMSERVER", DPFLTR_DMSERVER_ID },
261 { "SR", DPFLTR_SR_ID },
262 { "INFINIBAND", DPFLTR_INFINIBAND_ID },
263 { "IHVDRIVER", DPFLTR_IHVDRIVER_ID },
264 { "IHVVIDEO", DPFLTR_IHVVIDEO_ID },
265 { "IHVAUDIO", DPFLTR_IHVAUDIO_ID },
266 { "IHVNETWORK", DPFLTR_IHVNETWORK_ID },
267 { "IHVSTREAMING", DPFLTR_IHVSTREAMING_ID },
268 { "IHVBUS", DPFLTR_IHVBUS_ID },
269 { "HPS", DPFLTR_HPS_ID },
270 { "RTLTHREADPOOL", DPFLTR_RTLTHREADPOOL_ID },
271 { "LDR", DPFLTR_LDR_ID },
272 { "TCPIP6", DPFLTR_TCPIP6_ID },
273 { "ISAPNP", DPFLTR_ISAPNP_ID },
274 { "SHPC", DPFLTR_SHPC_ID },
275 { "STORPORT", DPFLTR_STORPORT_ID },
276 { "STORMINIPORT", DPFLTR_STORMINIPORT_ID },
277 { "PRINTSPOOLER", DPFLTR_PRINTSPOOLER_ID },
278 };
279
280 for (i = 0; i < sizeof(ComponentTable) / sizeof(ComponentTable[0]); i++)
281 {
282 if (_stricmp(ComponentName, ComponentTable[i].Name) == 0)
283 {
284 *ComponentId = ComponentTable[i].Id;
285 return TRUE;
286 }
287 }
288
289 return FALSE;
290 }
291
292 /*!\brief Evaluates an expression...
293 *
294 * Much like KdbpRpnEvaluateExpression, but prints the error message (if any)
295 * at the given offset.
296 *
297 * \param Expression Expression to evaluate.
298 * \param ErrOffset Offset (in characters) to print the error message at.
299 * \param Result Receives the result on success.
300 *
301 * \retval TRUE Success.
302 * \retval FALSE Failure.
303 */
304 static BOOLEAN
305 KdbpEvaluateExpression(
306 IN PCHAR Expression,
307 IN LONG ErrOffset,
308 OUT PULONGLONG Result)
309 {
310 static CHAR ErrMsgBuffer[130] = "^ ";
311 LONG ExpressionErrOffset = -1;
312 PCHAR ErrMsg = ErrMsgBuffer;
313 BOOLEAN Ok;
314
315 Ok = KdbpRpnEvaluateExpression(Expression, KdbCurrentTrapFrame, Result,
316 &ExpressionErrOffset, ErrMsgBuffer + 2);
317 if (!Ok)
318 {
319 if (ExpressionErrOffset >= 0)
320 ExpressionErrOffset += ErrOffset;
321 else
322 ErrMsg += 2;
323
324 KdbpPrint("%*s%s\n", ExpressionErrOffset, "", ErrMsg);
325 }
326
327 return Ok;
328 }
329
330 /*!\brief Evaluates an expression and displays the result.
331 */
332 static BOOLEAN
333 KdbpCmdEvalExpression(
334 ULONG Argc,
335 PCHAR Argv[])
336 {
337 ULONG i, len;
338 ULONGLONG Result = 0;
339 ULONG ul;
340 LONG l = 0;
341 BOOLEAN Ok;
342
343 if (Argc < 2)
344 {
345 KdbpPrint("?: Argument required\n");
346 return TRUE;
347 }
348
349 /* Put the arguments back together */
350 Argc--;
351 for (i = 1; i < Argc; i++)
352 {
353 len = strlen(Argv[i]);
354 Argv[i][len] = ' ';
355 }
356
357 /* Evaluate the expression */
358 Ok = KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result);
359 if (Ok)
360 {
361 if (Result > 0x00000000ffffffffLL)
362 {
363 if (Result & 0x8000000000000000LL)
364 KdbpPrint("0x%016I64x %20I64u %20I64d\n", Result, Result, Result);
365 else
366 KdbpPrint("0x%016I64x %20I64u\n", Result, Result);
367 }
368 else
369 {
370 ul = (ULONG)Result;
371
372 if (ul <= 0xff && ul >= 0x80)
373 l = (LONG)((CHAR)ul);
374 else if (ul <= 0xffff && ul >= 0x8000)
375 l = (LONG)((SHORT)ul);
376 else
377 l = (LONG)ul;
378
379 if (l < 0)
380 KdbpPrint("0x%08lx %10lu %10ld\n", ul, ul, l);
381 else
382 KdbpPrint("0x%08lx %10lu\n", ul, ul);
383 }
384 }
385
386 return TRUE;
387 }
388
389 /*!\brief Display list of active debug channels
390 */
391 static BOOLEAN
392 KdbpCmdFilter(
393 ULONG Argc,
394 PCHAR Argv[])
395 {
396 ULONG i, j, ComponentId, Level;
397 ULONG set = DPFLTR_MASK, clear = DPFLTR_MASK;
398 PCHAR pend;
399 LPCSTR opt, p;
400
401 static struct
402 {
403 LPCSTR Name;
404 ULONG Level;
405 }
406 debug_classes[] =
407 {
408 { "error", 1 << DPFLTR_ERROR_LEVEL },
409 { "warning", 1 << DPFLTR_WARNING_LEVEL },
410 { "trace", 1 << DPFLTR_TRACE_LEVEL },
411 { "info", 1 << DPFLTR_INFO_LEVEL },
412 };
413
414 for (i = 1; i < Argc; i++)
415 {
416 opt = Argv[i];
417 p = opt + strcspn(opt, "+-");
418 if (!p[0]) p = opt; /* assume it's a debug channel name */
419
420 if (p > opt)
421 {
422 for (j = 0; j < sizeof(debug_classes) / sizeof(debug_classes[0]); j++)
423 {
424 SIZE_T len = strlen(debug_classes[j].Name);
425 if (len != (p - opt))
426 continue;
427 if (_strnicmp(opt, debug_classes[j].Name, len) == 0) /* found it */
428 {
429 if (*p == '+')
430 set |= debug_classes[j].Level;
431 else
432 clear |= debug_classes[j].Level;
433 break;
434 }
435 }
436 if (j == sizeof(debug_classes) / sizeof(debug_classes[0]))
437 {
438 Level = strtoul(opt, &pend, 0);
439 if (pend != p)
440 {
441 KdbpPrint("filter: bad class name '%.*s'\n", p - opt, opt);
442 continue;
443 }
444 if (*p == '+')
445 set |= Level;
446 else
447 clear |= Level;
448 }
449 }
450 else
451 {
452 if (*p == '-')
453 clear = MAXULONG;
454 else
455 set = MAXULONG;
456 }
457 if (*p == '+' || *p == '-')
458 p++;
459
460 if (!KdbpGetComponentId(p, &ComponentId))
461 {
462 KdbpPrint("filter: '%s' is not a valid component name!\n", p);
463 return TRUE;
464 }
465
466 /* Get current mask value */
467 NtSetDebugFilterState(ComponentId, set, TRUE);
468 NtSetDebugFilterState(ComponentId, clear, FALSE);
469 }
470
471 return TRUE;
472 }
473
474 /*!\brief Disassembles 10 instructions at eip or given address or
475 * displays 16 dwords from memory at given address.
476 */
477 static BOOLEAN
478 KdbpCmdDisassembleX(
479 ULONG Argc,
480 PCHAR Argv[])
481 {
482 ULONG Count;
483 ULONG ul;
484 INT i;
485 ULONGLONG Result = 0;
486 ULONG_PTR Address = KdbCurrentTrapFrame->Tf.Eip;
487 LONG InstLen;
488
489 if (Argv[0][0] == 'x') /* display memory */
490 Count = 16;
491 else /* disassemble */
492 Count = 10;
493
494 if (Argc >= 2)
495 {
496 /* Check for [L count] part */
497 ul = 0;
498 if (strcmp(Argv[Argc-2], "L") == 0)
499 {
500 ul = strtoul(Argv[Argc-1], NULL, 0);
501 if (ul > 0)
502 {
503 Count = ul;
504 Argc -= 2;
505 }
506 }
507 else if (Argv[Argc-1][0] == 'L')
508 {
509 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
510 if (ul > 0)
511 {
512 Count = ul;
513 Argc--;
514 }
515 }
516
517 /* Put the remaining arguments back together */
518 Argc--;
519 for (ul = 1; ul < Argc; ul++)
520 {
521 Argv[ul][strlen(Argv[ul])] = ' ';
522 }
523 Argc++;
524 }
525
526 /* Evaluate the expression */
527 if (Argc > 1)
528 {
529 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
530 return TRUE;
531
532 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
533 KdbpPrint("Warning: Address %I64x is beeing truncated\n");
534
535 Address = (ULONG_PTR)Result;
536 }
537 else if (Argv[0][0] == 'x')
538 {
539 KdbpPrint("x: Address argument required.\n");
540 return TRUE;
541 }
542
543 if (Argv[0][0] == 'x')
544 {
545 /* Display dwords */
546 ul = 0;
547
548 while (Count > 0)
549 {
550 if (!KdbSymPrintAddress((PVOID)Address))
551 KdbpPrint("<%x>:", Address);
552 else
553 KdbpPrint(":");
554
555 i = min(4, Count);
556 Count -= i;
557
558 while (--i >= 0)
559 {
560 if (!NT_SUCCESS(KdbpSafeReadMemory(&ul, (PVOID)Address, sizeof(ul))))
561 KdbpPrint(" ????????");
562 else
563 KdbpPrint(" %x", ul);
564
565 Address += sizeof(ul);
566 }
567
568 KdbpPrint("\n");
569 }
570 }
571 else
572 {
573 /* Disassemble */
574 while (Count-- > 0)
575 {
576 if (!KdbSymPrintAddress((PVOID)Address))
577 KdbpPrint("<%x>: ", Address);
578 else
579 KdbpPrint(": ");
580
581 InstLen = KdbpDisassemble(Address, KdbUseIntelSyntax);
582 if (InstLen < 0)
583 {
584 KdbpPrint("<INVALID>\n");
585 return TRUE;
586 }
587
588 KdbpPrint("\n");
589 Address += InstLen;
590 }
591 }
592
593 return TRUE;
594 }
595
596 /*!\brief Displays CPU registers.
597 */
598 static BOOLEAN
599 KdbpCmdRegs(
600 ULONG Argc,
601 PCHAR Argv[])
602 {
603 PKTRAP_FRAME Tf = &KdbCurrentTrapFrame->Tf;
604 INT i;
605 const PCHAR EflagsBits[64] = { " CF", NULL, " PF", " BIT3", " AF", " BIT5",
606 " ZF", " SF", " TF", " IF", " DF", " OF",
607 NULL, NULL, " NT", " BIT15", " RF", " VF",
608 " AC", " VIF", " VIP", " ID", " BIT22",
609 " BIT23", " BIT24", " BIT25", " BIT26",
610 " BIT27", " BIT28", " BIT29", " BIT30",
611 " BIT31", " BIT32", " BIT33", " BIT34",
612 " BIT35", " BIT36", " BIT37", " BIT38",
613 " BIT39", " BIT40", " BIT41", " BIT42",
614 " BIT43", " BIT44", " BIT45", " BIT46",
615 " BIT47", " BIT48", " BIT49", " BIT50",
616 " BIT51", " BIT52", " BIT53", " BIT54",
617 " BIT55", " BIT56", " BIT57", " BIT58",
618 " BIT59", " BIT60", " BIT61", " BIT62",
619 " BIT63",
620 };
621
622 if (Argv[0][0] == 'r') /* regs */
623 {
624 #ifdef _M_IX86
625 KdbpPrint("CS:EIP 0x%04x:0x%08x\n"
626 "SS:ESP 0x%04x:0x%08x\n"
627 " EAX 0x%08x EBX 0x%08x\n"
628 " ECX 0x%08x EDX 0x%08x\n"
629 " ESI 0x%08x EDI 0x%08x\n"
630 " EBP 0x%08x\n",
631 Tf->SegCs & 0xFFFF, Tf->Eip,
632 Tf->HardwareSegSs, Tf->HardwareEsp,
633 Tf->Eax, Tf->Ebx,
634 Tf->Ecx, Tf->Edx,
635 Tf->Esi, Tf->Edi,
636 Tf->Ebp);
637 #elif defined(_M_AMD64)
638 KdbpPrint("CS:RIP 0x%04x:0x%p\n"
639 "SS:RSP 0x%04x:0x%p\n"
640 " RAX 0x%p RBX 0x%p\n"
641 " RCX 0x%p RDX 0x%p\n"
642 " RSI 0x%p RDI 0x%p\n"
643 " RBP 0x%p R8 0x%p\n"
644 " R9 0x%p R10 0x%p\n"
645 " R11 0x%p\n",
646 Tf->SegCs & 0xFFFF, Tf->Rip,
647 Tf->SegSs, Tf->Rsp,
648 Tf->Rax, Tf->Rbx,
649 Tf->Rcx, Tf->Rdx,
650 Tf->Rsi, Tf->Rdi,
651 Tf->Rbp, Tf->R8,
652 Tf->R9, Tf->R10,
653 Tf->R11);
654 #endif
655 KdbpPrint("EFLAGS 0x%08x ", Tf->EFlags);
656
657 #ifdef _M_IX86
658 for (i = 0; i < 32; i++)
659 #elif defined(_M_AMD64)
660 for (i = 0; i < 64; i++)
661 #endif
662 {
663 if (i == 1)
664 {
665 if ((Tf->EFlags & (1 << 1)) == 0)
666 KdbpPrint(" !BIT1");
667 }
668 else if (i == 12)
669 {
670 KdbpPrint(" IOPL%d", (Tf->EFlags >> 12) & 3);
671 }
672 else if (i == 13)
673 {
674 }
675 else if ((Tf->EFlags & (1 << i)) != 0)
676 {
677 KdbpPrint(EflagsBits[i]);
678 }
679 }
680
681 KdbpPrint("\n");
682 }
683 else if (Argv[0][0] == 'c') /* cregs */
684 {
685 ULONG_PTR Cr0, Cr2, Cr3, Cr4;
686 KDESCRIPTOR Gdtr, Idtr;
687 #if defined(_M_IX86)
688 USHORT Ldtr;
689 Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
690 Ldtr = Ke386GetLocalDescriptorTable();
691 #elif defined(_M_AMD64)
692 __sgdt(&Gdtr.Limit);
693 #endif
694 static const PCHAR Cr0Bits[32] = { " PE", " MP", " EM", " TS", " ET", " NE", NULL, NULL,
695 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
696 " WP", NULL, " AM", NULL, NULL, NULL, NULL, NULL,
697 NULL, NULL, NULL, NULL, NULL, " NW", " CD", " PG" };
698 static const PCHAR Cr4Bits[32] = { " VME", " PVI", " TSD", " DE", " PSE", " PAE", " MCE", " PGE",
699 " PCE", " OSFXSR", " OSXMMEXCPT", NULL, NULL, NULL, NULL, NULL,
700 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
701 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
702
703 Cr0 = KdbCurrentTrapFrame->Cr0;
704 Cr2 = KdbCurrentTrapFrame->Cr2;
705 Cr3 = KdbCurrentTrapFrame->Cr3;
706 Cr4 = KdbCurrentTrapFrame->Cr4;
707
708 /* Get interrupt descriptor table regs */
709 __sidt(&Idtr.Limit);
710
711 /* Display the control registers */
712 KdbpPrint("CR0 0x%p ", Cr0);
713
714 for (i = 0; i < 32; i++)
715 {
716 if (!Cr0Bits[i])
717 continue;
718
719 if ((Cr0 & (1 << i)) != 0)
720 KdbpPrint(Cr0Bits[i]);
721 }
722
723 KdbpPrint("\nCR2 0x%p\n", Cr2);
724 KdbpPrint("CR3 0x%p Pagedir-Base 0x%p %s%s\n", Cr3, (Cr3 & 0xfffff000),
725 (Cr3 & (1 << 3)) ? " PWT" : "", (Cr3 & (1 << 4)) ? " PCD" : "" );
726 KdbpPrint("CR4 0x%p ", Cr4);
727
728 for (i = 0; i < 32; i++)
729 {
730 if (!Cr4Bits[i])
731 continue;
732
733 if ((Cr4 & (1 << i)) != 0)
734 KdbpPrint(Cr4Bits[i]);
735 }
736
737 /* Display the descriptor table regs */
738 KdbpPrint("\nGDTR Base 0x%p Size 0x%04x\n", Gdtr.Base, Gdtr.Limit);
739 #ifdef _M_IX86
740 KdbpPrint("LDTR 0x%p\n", Ldtr);
741 #endif
742 KdbpPrint("IDTR Base 0x%p Size 0x%04x\n", Idtr.Base, Idtr.Limit);
743 }
744 else if (Argv[0][0] == 's') /* sregs */
745 {
746 KdbpPrint("CS 0x%04x Index 0x%04x %cDT RPL%d\n",
747 Tf->SegCs & 0xffff, (Tf->SegCs & 0xffff) >> 3,
748 (Tf->SegCs & (1 << 2)) ? 'L' : 'G', Tf->SegCs & 3);
749 KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n",
750 Tf->SegDs, Tf->SegDs >> 3, (Tf->SegDs & (1 << 2)) ? 'L' : 'G', Tf->SegDs & 3);
751 KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n",
752 Tf->SegEs, Tf->SegEs >> 3, (Tf->SegEs & (1 << 2)) ? 'L' : 'G', Tf->SegEs & 3);
753 KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n",
754 Tf->SegFs, Tf->SegFs >> 3, (Tf->SegFs & (1 << 2)) ? 'L' : 'G', Tf->SegFs & 3);
755 KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n",
756 Tf->SegGs, Tf->SegGs >> 3, (Tf->SegGs & (1 << 2)) ? 'L' : 'G', Tf->SegGs & 3);
757 #ifdef _M_IX86
758 KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
759 Tf->HardwareSegSs, Tf->HardwareSegSs >> 3, (Tf->HardwareSegSs & (1 << 2)) ? 'L' : 'G', Tf->HardwareSegSs & 3);
760 #else
761 KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
762 Tf->SegSs, Tf->SegSs >> 3, (Tf->SegSs & (1 << 2)) ? 'L' : 'G', Tf->SegSs & 3);
763 #endif
764
765 }
766 else /* dregs */
767 {
768 ASSERT(Argv[0][0] == 'd');
769 KdbpPrint("DR0 0x%p\n"
770 "DR1 0x%p\n"
771 "DR2 0x%p\n"
772 "DR3 0x%p\n"
773 "DR6 0x%p\n"
774 "DR7 0x%p\n",
775 Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3,
776 Tf->Dr6, Tf->Dr7);
777 }
778
779 return TRUE;
780 }
781
782 /*!\brief Displays a backtrace.
783 */
784 static BOOLEAN
785 KdbpCmdBackTrace(
786 ULONG Argc,
787 PCHAR Argv[])
788 {
789 ULONG Count;
790 ULONG ul;
791 ULONGLONG Result = 0;
792 #ifdef _M_IX86
793 ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp;
794 #elif defined(_M_AMD64)
795 ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Rbp;
796 #endif
797 ULONG_PTR Address;
798
799 if (Argc >= 2)
800 {
801 /* Check for [L count] part */
802 ul = 0;
803
804 if (strcmp(Argv[Argc-2], "L") == 0)
805 {
806 ul = strtoul(Argv[Argc-1], NULL, 0);
807 if (ul > 0)
808 {
809 Count = ul;
810 Argc -= 2;
811 }
812 }
813 else if (Argv[Argc-1][0] == 'L')
814 {
815 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
816 if (ul > 0)
817 {
818 Count = ul;
819 Argc--;
820 }
821 }
822
823 /* Put the remaining arguments back together */
824 Argc--;
825 for (ul = 1; ul < Argc; ul++)
826 {
827 Argv[ul][strlen(Argv[ul])] = ' ';
828 }
829 Argc++;
830 }
831
832 /* Check if frame addr or thread id is given. */
833 if (Argc > 1)
834 {
835 if (Argv[1][0] == '*')
836 {
837 Argv[1]++;
838
839 /* Evaluate the expression */
840 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
841 return TRUE;
842
843 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
844 KdbpPrint("Warning: Address %I64x is beeing truncated\n");
845
846 Frame = (ULONG_PTR)Result;
847 }
848 else
849 {
850 KdbpPrint("Thread backtrace not supported yet!\n");
851 return TRUE;
852 }
853 }
854 else
855 {
856 KdbpPrint("Eip:\n");
857
858 /* Try printing the function at EIP */
859 if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
860 KdbpPrint("<%x>\n", KdbCurrentTrapFrame->Tf.Eip);
861 else
862 KdbpPrint("\n");
863 }
864
865 KdbpPrint("Frames:\n");
866 for (;;)
867 {
868 if (Frame == 0)
869 break;
870
871 if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR))))
872 {
873 KdbpPrint("Couldn't access memory at 0x%p!\n", Frame + sizeof(ULONG_PTR));
874 break;
875 }
876
877 if (!KdbSymPrintAddress((PVOID)Address))
878 KdbpPrint("<%x>\n", Address);
879 else
880 KdbpPrint("\n");
881
882 if (Address == 0)
883 break;
884
885 if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR))))
886 {
887 KdbpPrint("Couldn't access memory at 0x%p!\n", Frame);
888 break;
889 }
890 }
891
892 return TRUE;
893 }
894
895 /*!\brief Continues execution of the system/leaves KDB.
896 */
897 static BOOLEAN
898 KdbpCmdContinue(
899 ULONG Argc,
900 PCHAR Argv[])
901 {
902 /* Exit the main loop */
903 return FALSE;
904 }
905
906 /*!\brief Continues execution of the system/leaves KDB.
907 */
908 static BOOLEAN
909 KdbpCmdStep(
910 ULONG Argc,
911 PCHAR Argv[])
912 {
913 ULONG Count = 1;
914
915 if (Argc > 1)
916 {
917 Count = strtoul(Argv[1], NULL, 0);
918 if (Count == 0)
919 {
920 KdbpPrint("%s: Integer argument required\n", Argv[0]);
921 return TRUE;
922 }
923 }
924
925 if (Argv[0][0] == 'n')
926 KdbSingleStepOver = TRUE;
927 else
928 KdbSingleStepOver = FALSE;
929
930 /* Set the number of single steps and return to the interrupted code. */
931 KdbNumSingleSteps = Count;
932
933 return FALSE;
934 }
935
936 /*!\brief Lists breakpoints.
937 */
938 static BOOLEAN
939 KdbpCmdBreakPointList(
940 ULONG Argc,
941 PCHAR Argv[])
942 {
943 LONG l;
944 ULONG_PTR Address = 0;
945 KDB_BREAKPOINT_TYPE Type = 0;
946 KDB_ACCESS_TYPE AccessType = 0;
947 UCHAR Size = 0;
948 UCHAR DebugReg = 0;
949 BOOLEAN Enabled = FALSE;
950 BOOLEAN Global = FALSE;
951 PEPROCESS Process = NULL;
952 PCHAR str1, str2, ConditionExpr, GlobalOrLocal;
953 CHAR Buffer[20];
954
955 l = KdbpGetNextBreakPointNr(0);
956 if (l < 0)
957 {
958 KdbpPrint("No breakpoints.\n");
959 return TRUE;
960 }
961
962 KdbpPrint("Breakpoints:\n");
963 do
964 {
965 if (!KdbpGetBreakPointInfo(l, &Address, &Type, &Size, &AccessType, &DebugReg,
966 &Enabled, &Global, &Process, &ConditionExpr))
967 {
968 continue;
969 }
970
971 if (l == KdbLastBreakPointNr)
972 {
973 str1 = "\x1b[1m*";
974 str2 = "\x1b[0m";
975 }
976 else
977 {
978 str1 = " ";
979 str2 = "";
980 }
981
982 if (Global)
983 {
984 GlobalOrLocal = " global";
985 }
986 else
987 {
988 GlobalOrLocal = Buffer;
989 sprintf(Buffer, " PID 0x%08lx",
990 (ULONG_PTR)(Process ? Process->UniqueProcessId : INVALID_HANDLE_VALUE));
991 }
992
993 if (Type == KdbBreakPointSoftware || Type == KdbBreakPointTemporary)
994 {
995 KdbpPrint(" %s%03d BPX 0x%08x%s%s%s%s%s\n",
996 str1, l, Address,
997 Enabled ? "" : " disabled",
998 GlobalOrLocal,
999 ConditionExpr ? " IF " : "",
1000 ConditionExpr ? ConditionExpr : "",
1001 str2);
1002 }
1003 else if (Type == KdbBreakPointHardware)
1004 {
1005 if (!Enabled)
1006 {
1007 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s disabled%s%s%s%s\n", str1, l, Address,
1008 KDB_ACCESS_TYPE_TO_STRING(AccessType),
1009 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
1010 GlobalOrLocal,
1011 ConditionExpr ? " IF " : "",
1012 ConditionExpr ? ConditionExpr : "",
1013 str2);
1014 }
1015 else
1016 {
1017 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s DR%d%s%s%s%s\n", str1, l, Address,
1018 KDB_ACCESS_TYPE_TO_STRING(AccessType),
1019 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
1020 DebugReg,
1021 GlobalOrLocal,
1022 ConditionExpr ? " IF " : "",
1023 ConditionExpr ? ConditionExpr : "",
1024 str2);
1025 }
1026 }
1027 }
1028 while ((l = KdbpGetNextBreakPointNr(l+1)) >= 0);
1029
1030 return TRUE;
1031 }
1032
1033 /*!\brief Enables, disables or clears a breakpoint.
1034 */
1035 static BOOLEAN
1036 KdbpCmdEnableDisableClearBreakPoint(
1037 ULONG Argc,
1038 PCHAR Argv[])
1039 {
1040 PCHAR pend;
1041 ULONG BreakPointNr;
1042
1043 if (Argc < 2)
1044 {
1045 KdbpPrint("%s: argument required\n", Argv[0]);
1046 return TRUE;
1047 }
1048
1049 pend = Argv[1];
1050 BreakPointNr = strtoul(Argv[1], &pend, 0);
1051 if (pend == Argv[1] || *pend != '\0')
1052 {
1053 KdbpPrint("%s: integer argument required\n", Argv[0]);
1054 return TRUE;
1055 }
1056
1057 if (Argv[0][1] == 'e') /* enable */
1058 {
1059 KdbpEnableBreakPoint(BreakPointNr, NULL);
1060 }
1061 else if (Argv [0][1] == 'd') /* disable */
1062 {
1063 KdbpDisableBreakPoint(BreakPointNr, NULL);
1064 }
1065 else /* clear */
1066 {
1067 ASSERT(Argv[0][1] == 'c');
1068 KdbpDeleteBreakPoint(BreakPointNr, NULL);
1069 }
1070
1071 return TRUE;
1072 }
1073
1074 /*!\brief Sets a software or hardware (memory) breakpoint at the given address.
1075 */
1076 static BOOLEAN
1077 KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[])
1078 {
1079 ULONGLONG Result = 0;
1080 ULONG_PTR Address;
1081 KDB_BREAKPOINT_TYPE Type;
1082 UCHAR Size = 0;
1083 KDB_ACCESS_TYPE AccessType = 0;
1084 ULONG AddressArgIndex, i;
1085 LONG ConditionArgIndex;
1086 BOOLEAN Global = TRUE;
1087
1088 if (Argv[0][2] == 'x') /* software breakpoint */
1089 {
1090 if (Argc < 2)
1091 {
1092 KdbpPrint("bpx: Address argument required.\n");
1093 return TRUE;
1094 }
1095
1096 AddressArgIndex = 1;
1097 Type = KdbBreakPointSoftware;
1098 }
1099 else /* memory breakpoint */
1100 {
1101 ASSERT(Argv[0][2] == 'm');
1102
1103 if (Argc < 2)
1104 {
1105 KdbpPrint("bpm: Access type argument required (one of r, w, rw, x)\n");
1106 return TRUE;
1107 }
1108
1109 if (_stricmp(Argv[1], "x") == 0)
1110 AccessType = KdbAccessExec;
1111 else if (_stricmp(Argv[1], "r") == 0)
1112 AccessType = KdbAccessRead;
1113 else if (_stricmp(Argv[1], "w") == 0)
1114 AccessType = KdbAccessWrite;
1115 else if (_stricmp(Argv[1], "rw") == 0)
1116 AccessType = KdbAccessReadWrite;
1117 else
1118 {
1119 KdbpPrint("bpm: Unknown access type '%s'\n", Argv[1]);
1120 return TRUE;
1121 }
1122
1123 if (Argc < 3)
1124 {
1125 KdbpPrint("bpm: %s argument required.\n", AccessType == KdbAccessExec ? "Address" : "Memory size");
1126 return TRUE;
1127 }
1128
1129 AddressArgIndex = 3;
1130 if (_stricmp(Argv[2], "byte") == 0)
1131 Size = 1;
1132 else if (_stricmp(Argv[2], "word") == 0)
1133 Size = 2;
1134 else if (_stricmp(Argv[2], "dword") == 0)
1135 Size = 4;
1136 else if (_stricmp(Argv[2], "qword") == 0)
1137 Size = 8;
1138 else if (AccessType == KdbAccessExec)
1139 {
1140 Size = 1;
1141 AddressArgIndex--;
1142 }
1143 else
1144 {
1145 KdbpPrint("bpm: Unknown memory size '%s'\n", Argv[2]);
1146 return TRUE;
1147 }
1148
1149 if (Argc <= AddressArgIndex)
1150 {
1151 KdbpPrint("bpm: Address argument required.\n");
1152 return TRUE;
1153 }
1154
1155 Type = KdbBreakPointHardware;
1156 }
1157
1158 /* Put the arguments back together */
1159 ConditionArgIndex = -1;
1160 for (i = AddressArgIndex; i < (Argc-1); i++)
1161 {
1162 if (strcmp(Argv[i+1], "IF") == 0) /* IF found */
1163 {
1164 ConditionArgIndex = i + 2;
1165 if (ConditionArgIndex >= Argc)
1166 {
1167 KdbpPrint("%s: IF requires condition expression.\n", Argv[0]);
1168 return TRUE;
1169 }
1170
1171 for (i = ConditionArgIndex; i < (Argc-1); i++)
1172 Argv[i][strlen(Argv[i])] = ' ';
1173
1174 break;
1175 }
1176
1177 Argv[i][strlen(Argv[i])] = ' ';
1178 }
1179
1180 /* Evaluate the address expression */
1181 if (!KdbpEvaluateExpression(Argv[AddressArgIndex],
1182 sizeof("kdb:> ")-1 + (Argv[AddressArgIndex]-Argv[0]),
1183 &Result))
1184 {
1185 return TRUE;
1186 }
1187
1188 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1189 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0]);
1190
1191 Address = (ULONG_PTR)Result;
1192
1193 KdbpInsertBreakPoint(Address, Type, Size, AccessType,
1194 (ConditionArgIndex < 0) ? NULL : Argv[ConditionArgIndex],
1195 Global, NULL);
1196
1197 return TRUE;
1198 }
1199
1200 /*!\brief Lists threads or switches to another thread context.
1201 */
1202 static BOOLEAN
1203 KdbpCmdThread(
1204 ULONG Argc,
1205 PCHAR Argv[])
1206 {
1207 PLIST_ENTRY Entry;
1208 PETHREAD Thread = NULL;
1209 PEPROCESS Process = NULL;
1210 BOOLEAN ReferencedThread = FALSE, ReferencedProcess = FALSE;
1211 PULONG_PTR Esp;
1212 PULONG_PTR Ebp;
1213 ULONG_PTR Eip;
1214 ULONG_PTR ul = 0;
1215 PCHAR State, pend, str1, str2;
1216 static const PCHAR ThreadStateToString[DeferredReady+1] =
1217 {
1218 "Initialized", "Ready", "Running",
1219 "Standby", "Terminated", "Waiting",
1220 "Transition", "DeferredReady"
1221 };
1222
1223 ASSERT(KdbCurrentProcess);
1224
1225 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1226 {
1227 Process = KdbCurrentProcess;
1228
1229 if (Argc >= 3)
1230 {
1231 ul = strtoul(Argv[2], &pend, 0);
1232 if (Argv[2] == pend)
1233 {
1234 KdbpPrint("thread: '%s' is not a valid process id!\n", Argv[2]);
1235 return TRUE;
1236 }
1237
1238 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1239 {
1240 KdbpPrint("thread: Invalid process id!\n");
1241 return TRUE;
1242 }
1243
1244 /* Remember our reference */
1245 ReferencedProcess = TRUE;
1246 }
1247
1248 Entry = Process->ThreadListHead.Flink;
1249 if (Entry == &Process->ThreadListHead)
1250 {
1251 if (Argc >= 3)
1252 KdbpPrint("No threads in process 0x%08x!\n", ul);
1253 else
1254 KdbpPrint("No threads in current process!\n");
1255
1256 if (ReferencedProcess)
1257 ObDereferenceObject(Process);
1258
1259 return TRUE;
1260 }
1261
1262 KdbpPrint(" TID State Prior. Affinity EBP EIP\n");
1263 do
1264 {
1265 Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
1266
1267 if (Thread == KdbCurrentThread)
1268 {
1269 str1 = "\x1b[1m*";
1270 str2 = "\x1b[0m";
1271 }
1272 else
1273 {
1274 str1 = " ";
1275 str2 = "";
1276 }
1277
1278 if (Thread->Tcb.TrapFrame)
1279 {
1280 #ifdef _M_IX86
1281 if (Thread->Tcb.TrapFrame->PreviousPreviousMode == KernelMode)
1282 Esp = (PULONG)Thread->Tcb.TrapFrame->TempEsp;
1283 else
1284 Esp = (PULONG)Thread->Tcb.TrapFrame->HardwareEsp;
1285
1286 Ebp = (PULONG)Thread->Tcb.TrapFrame->Ebp;
1287 Eip = Thread->Tcb.TrapFrame->Eip;
1288
1289 #elif defined(_M_AMD64)
1290 Esp = (PULONG_PTR)Thread->Tcb.TrapFrame->Rsp;
1291
1292 Ebp = (PULONG_PTR)Thread->Tcb.TrapFrame->Rbp;
1293 Eip = Thread->Tcb.TrapFrame->Eip;
1294 #endif
1295 }
1296 else
1297 {
1298 Esp = (PULONG_PTR)Thread->Tcb.KernelStack;
1299 Ebp = (PULONG_PTR)Esp[4];
1300 Eip = 0;
1301
1302 if (Ebp) /* FIXME: Should we attach to the process to read Ebp[1]? */
1303 KdbpSafeReadMemory(&Eip, Ebp + 1, sizeof (Eip));
1304 }
1305
1306 if (Thread->Tcb.State < (DeferredReady + 1))
1307 State = ThreadStateToString[Thread->Tcb.State];
1308 else
1309 State = "Unknown";
1310
1311 KdbpPrint(" %s0x%p %-11s %3d 0x%p 0x%p 0x%p%s\n",
1312 str1,
1313 Thread->Cid.UniqueThread,
1314 State,
1315 Thread->Tcb.Priority,
1316 Thread->Tcb.Affinity,
1317 Ebp,
1318 Eip,
1319 str2);
1320
1321 Entry = Entry->Flink;
1322 }
1323 while (Entry != &Process->ThreadListHead);
1324
1325 /* Release our reference, if any */
1326 if (ReferencedProcess)
1327 ObDereferenceObject(Process);
1328 }
1329 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1330 {
1331 if (Argc < 3)
1332 {
1333 KdbpPrint("thread attach: thread id argument required!\n");
1334 return TRUE;
1335 }
1336
1337 ul = strtoul(Argv[2], &pend, 0);
1338 if (Argv[2] == pend)
1339 {
1340 KdbpPrint("thread attach: '%s' is not a valid thread id!\n", Argv[2]);
1341 return TRUE;
1342 }
1343
1344 if (!KdbpAttachToThread((PVOID)ul))
1345 {
1346 return TRUE;
1347 }
1348
1349 KdbpPrint("Attached to thread 0x%08x.\n", ul);
1350 }
1351 else
1352 {
1353 Thread = KdbCurrentThread;
1354
1355 if (Argc >= 2)
1356 {
1357 ul = strtoul(Argv[1], &pend, 0);
1358 if (Argv[1] == pend)
1359 {
1360 KdbpPrint("thread: '%s' is not a valid thread id!\n", Argv[1]);
1361 return TRUE;
1362 }
1363
1364 if (!NT_SUCCESS(PsLookupThreadByThreadId((PVOID)ul, &Thread)))
1365 {
1366 KdbpPrint("thread: Invalid thread id!\n");
1367 return TRUE;
1368 }
1369
1370 /* Remember our reference */
1371 ReferencedThread = TRUE;
1372 }
1373
1374 if (Thread->Tcb.State < (DeferredReady + 1))
1375 State = ThreadStateToString[Thread->Tcb.State];
1376 else
1377 State = "Unknown";
1378
1379 KdbpPrint("%s"
1380 " TID: 0x%08x\n"
1381 " State: %s (0x%x)\n"
1382 " Priority: %d\n"
1383 " Affinity: 0x%08x\n"
1384 " Initial Stack: 0x%p\n"
1385 " Stack Limit: 0x%p\n"
1386 " Stack Base: 0x%p\n"
1387 " Kernel Stack: 0x%p\n"
1388 " Trap Frame: 0x%p\n"
1389 " NPX State: %s (0x%x)\n",
1390 (Argc < 2) ? "Current Thread:\n" : "",
1391 Thread->Cid.UniqueThread,
1392 State, Thread->Tcb.State,
1393 Thread->Tcb.Priority,
1394 Thread->Tcb.Affinity,
1395 Thread->Tcb.InitialStack,
1396 Thread->Tcb.StackLimit,
1397 Thread->Tcb.StackBase,
1398 Thread->Tcb.KernelStack,
1399 Thread->Tcb.TrapFrame,
1400 NPX_STATE_TO_STRING(Thread->Tcb.NpxState), Thread->Tcb.NpxState);
1401
1402 /* Release our reference if we had one */
1403 if (ReferencedThread)
1404 ObDereferenceObject(Thread);
1405 }
1406
1407 return TRUE;
1408 }
1409
1410 /*!\brief Lists processes or switches to another process context.
1411 */
1412 static BOOLEAN
1413 KdbpCmdProc(
1414 ULONG Argc,
1415 PCHAR Argv[])
1416 {
1417 PLIST_ENTRY Entry;
1418 PEPROCESS Process;
1419 BOOLEAN ReferencedProcess = FALSE;
1420 PCHAR State, pend, str1, str2;
1421 ULONG_PTR ul;
1422 extern LIST_ENTRY PsActiveProcessHead;
1423
1424 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1425 {
1426 Entry = PsActiveProcessHead.Flink;
1427 if (!Entry || Entry == &PsActiveProcessHead)
1428 {
1429 KdbpPrint("No processes in the system!\n");
1430 return TRUE;
1431 }
1432
1433 KdbpPrint(" PID State Filename\n");
1434 do
1435 {
1436 Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
1437
1438 if (Process == KdbCurrentProcess)
1439 {
1440 str1 = "\x1b[1m*";
1441 str2 = "\x1b[0m";
1442 }
1443 else
1444 {
1445 str1 = " ";
1446 str2 = "";
1447 }
1448
1449 State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
1450 ((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
1451
1452 KdbpPrint(" %s0x%08x %-10s %s%s\n",
1453 str1,
1454 Process->UniqueProcessId,
1455 State,
1456 Process->ImageFileName,
1457 str2);
1458
1459 Entry = Entry->Flink;
1460 }
1461 while(Entry != &PsActiveProcessHead);
1462 }
1463 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1464 {
1465 if (Argc < 3)
1466 {
1467 KdbpPrint("process attach: process id argument required!\n");
1468 return TRUE;
1469 }
1470
1471 ul = strtoul(Argv[2], &pend, 0);
1472 if (Argv[2] == pend)
1473 {
1474 KdbpPrint("process attach: '%s' is not a valid process id!\n", Argv[2]);
1475 return TRUE;
1476 }
1477
1478 if (!KdbpAttachToProcess((PVOID)ul))
1479 {
1480 return TRUE;
1481 }
1482
1483 KdbpPrint("Attached to process 0x%x, thread 0x%x.\n", ul,
1484 (ULONG_PTR)KdbCurrentThread->Cid.UniqueThread);
1485 }
1486 else
1487 {
1488 Process = KdbCurrentProcess;
1489
1490 if (Argc >= 2)
1491 {
1492 ul = strtoul(Argv[1], &pend, 0);
1493 if (Argv[1] == pend)
1494 {
1495 KdbpPrint("proc: '%s' is not a valid process id!\n", Argv[1]);
1496 return TRUE;
1497 }
1498
1499 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1500 {
1501 KdbpPrint("proc: Invalid process id!\n");
1502 return TRUE;
1503 }
1504
1505 /* Remember our reference */
1506 ReferencedProcess = TRUE;
1507 }
1508
1509 State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
1510 ((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
1511 KdbpPrint("%s"
1512 " PID: 0x%08x\n"
1513 " State: %s (0x%x)\n"
1514 " Image Filename: %s\n",
1515 (Argc < 2) ? "Current process:\n" : "",
1516 Process->UniqueProcessId,
1517 State, Process->Pcb.State,
1518 Process->ImageFileName);
1519
1520 /* Release our reference, if any */
1521 if (ReferencedProcess)
1522 ObDereferenceObject(Process);
1523 }
1524
1525 return TRUE;
1526 }
1527
1528 /*!\brief Lists loaded modules or the one containing the specified address.
1529 */
1530 static BOOLEAN
1531 KdbpCmdMod(
1532 ULONG Argc,
1533 PCHAR Argv[])
1534 {
1535 ULONGLONG Result = 0;
1536 ULONG_PTR Address;
1537 PLDR_DATA_TABLE_ENTRY LdrEntry;
1538 BOOLEAN DisplayOnlyOneModule = FALSE;
1539 INT i = 0;
1540
1541 if (Argc >= 2)
1542 {
1543 /* Put the arguments back together */
1544 Argc--;
1545 while (--Argc >= 1)
1546 Argv[Argc][strlen(Argv[Argc])] = ' ';
1547
1548 /* Evaluate the expression */
1549 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
1550 {
1551 return TRUE;
1552 }
1553
1554 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1555 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0]);
1556
1557 Address = (ULONG_PTR)Result;
1558
1559 if (!KdbpSymFindModule((PVOID)Address, NULL, -1, &LdrEntry))
1560 {
1561 KdbpPrint("No module containing address 0x%p found!\n", Address);
1562 return TRUE;
1563 }
1564
1565 DisplayOnlyOneModule = TRUE;
1566 }
1567 else
1568 {
1569 if (!KdbpSymFindModule(NULL, NULL, 0, &LdrEntry))
1570 {
1571 ULONG_PTR ntoskrnlBase = ((ULONG_PTR)KdbpCmdMod) & 0xfff00000;
1572 KdbpPrint(" Base Size Name\n");
1573 KdbpPrint(" %p %x %s\n", ntoskrnlBase, 0, "ntoskrnl.exe");
1574 return TRUE;
1575 }
1576
1577 i = 1;
1578 }
1579
1580 KdbpPrint(" Base Size Name\n");
1581 for (;;)
1582 {
1583 KdbpPrint(" %p %x %wZ\n", LdrEntry->DllBase, LdrEntry->SizeOfImage, &LdrEntry->BaseDllName);
1584
1585 if(DisplayOnlyOneModule || !KdbpSymFindModule(NULL, NULL, i++, &LdrEntry))
1586 break;
1587 }
1588
1589 return TRUE;
1590 }
1591
1592 /*!\brief Displays GDT, LDT or IDTd.
1593 */
1594 static BOOLEAN
1595 KdbpCmdGdtLdtIdt(
1596 ULONG Argc,
1597 PCHAR Argv[])
1598 {
1599 KDESCRIPTOR Reg;
1600 ULONG SegDesc[2];
1601 ULONG SegBase;
1602 ULONG SegLimit;
1603 PCHAR SegType;
1604 USHORT SegSel;
1605 UCHAR Type, Dpl;
1606 INT i;
1607 ULONG ul;
1608
1609 if (Argv[0][0] == 'i')
1610 {
1611 /* Read IDTR */
1612 __sidt(&Reg.Limit);
1613
1614 if (Reg.Limit < 7)
1615 {
1616 KdbpPrint("Interrupt descriptor table is empty.\n");
1617 return TRUE;
1618 }
1619
1620 KdbpPrint("IDT Base: 0x%08x Limit: 0x%04x\n", Reg.Base, Reg.Limit);
1621 KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n");
1622
1623 for (i = 0; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
1624 {
1625 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)((ULONG_PTR)Reg.Base + i), sizeof(SegDesc))))
1626 {
1627 KdbpPrint("Couldn't access memory at 0x%08x!\n", (ULONG_PTR)Reg.Base + i);
1628 return TRUE;
1629 }
1630
1631 Dpl = ((SegDesc[1] >> 13) & 3);
1632 if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
1633 SegType = "TASKGATE";
1634 else if ((SegDesc[1] & 0x1fe0) == 0x0e00) /* 32 bit Interrupt gate */
1635 SegType = "INTGATE32";
1636 else if ((SegDesc[1] & 0x1fe0) == 0x0600) /* 16 bit Interrupt gate */
1637 SegType = "INTGATE16";
1638 else if ((SegDesc[1] & 0x1fe0) == 0x0f00) /* 32 bit Trap gate */
1639 SegType = "TRAPGATE32";
1640 else if ((SegDesc[1] & 0x1fe0) == 0x0700) /* 16 bit Trap gate */
1641 SegType = "TRAPGATE16";
1642 else
1643 SegType = "UNKNOWN";
1644
1645 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
1646 {
1647 KdbpPrint(" %03d %-10s [NP] [NP] %02d\n",
1648 i / 8, SegType, Dpl);
1649 }
1650 else if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
1651 {
1652 SegSel = SegDesc[0] >> 16;
1653 KdbpPrint(" %03d %-10s 0x%04x %02d\n",
1654 i / 8, SegType, SegSel, Dpl);
1655 }
1656 else
1657 {
1658 SegSel = SegDesc[0] >> 16;
1659 SegBase = (SegDesc[1] & 0xffff0000) | (SegDesc[0] & 0x0000ffff);
1660 KdbpPrint(" %03d %-10s 0x%04x 0x%08x %02d\n",
1661 i / 8, SegType, SegSel, SegBase, Dpl);
1662 }
1663 }
1664 }
1665 else
1666 {
1667 ul = 0;
1668
1669 if (Argv[0][0] == 'g')
1670 {
1671 /* Read GDTR */
1672 #ifdef _M_IX86
1673 Ke386GetGlobalDescriptorTable(&Reg.Limit);
1674 #elif defined(_M_AMD64)
1675 __sgdt(&Reg.Limit);
1676 #endif
1677 i = 8;
1678 }
1679 else
1680 {
1681 ASSERT(Argv[0][0] == 'l');
1682
1683 /* Read LDTR */
1684 #ifdef _M_IX86
1685 Reg.Limit = Ke386GetLocalDescriptorTable();
1686 #elif defined(_M_AMD64)
1687 __sldt(&Reg.Limit);
1688 #endif
1689 Reg.Base = 0;
1690 i = 0;
1691 ul = 1 << 2;
1692 }
1693
1694 if (Reg.Limit < 7)
1695 {
1696 KdbpPrint("%s descriptor table is empty.\n",
1697 Argv[0][0] == 'g' ? "Global" : "Local");
1698 return TRUE;
1699 }
1700
1701 KdbpPrint("%cDT Base: 0x%08x Limit: 0x%04x\n",
1702 Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit);
1703 KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n");
1704
1705 for (; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
1706 {
1707 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)((ULONG_PTR)Reg.Base + i), sizeof(SegDesc))))
1708 {
1709 KdbpPrint("Couldn't access memory at 0x%08x!\n", (ULONG_PTR)Reg.Base + i);
1710 return TRUE;
1711 }
1712
1713 Dpl = ((SegDesc[1] >> 13) & 3);
1714 Type = ((SegDesc[1] >> 8) & 0xf);
1715
1716 SegBase = SegDesc[0] >> 16;
1717 SegBase |= (SegDesc[1] & 0xff) << 16;
1718 SegBase |= SegDesc[1] & 0xff000000;
1719 SegLimit = SegDesc[0] & 0x0000ffff;
1720 SegLimit |= (SegDesc[1] >> 16) & 0xf;
1721
1722 if ((SegDesc[1] & (1 << 23)) != 0)
1723 {
1724 SegLimit *= 4096;
1725 SegLimit += 4095;
1726 }
1727 else
1728 {
1729 SegLimit++;
1730 }
1731
1732 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
1733 {
1734 switch (Type)
1735 {
1736 case 1: SegType = "TSS16(Avl)"; break;
1737 case 2: SegType = "LDT"; break;
1738 case 3: SegType = "TSS16(Busy)"; break;
1739 case 4: SegType = "CALLGATE16"; break;
1740 case 5: SegType = "TASKGATE"; break;
1741 case 6: SegType = "INTGATE16"; break;
1742 case 7: SegType = "TRAPGATE16"; break;
1743 case 9: SegType = "TSS32(Avl)"; break;
1744 case 11: SegType = "TSS32(Busy)"; break;
1745 case 12: SegType = "CALLGATE32"; break;
1746 case 14: SegType = "INTGATE32"; break;
1747 case 15: SegType = "INTGATE32"; break;
1748 default: SegType = "UNKNOWN"; break;
1749 }
1750
1751 if (!(Type >= 1 && Type <= 3) &&
1752 Type != 9 && Type != 11)
1753 {
1754 SegBase = 0;
1755 SegLimit = 0;
1756 }
1757 }
1758 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
1759 {
1760 if ((SegDesc[1] & (1 << 22)) != 0)
1761 SegType = "DATA32";
1762 else
1763 SegType = "DATA16";
1764 }
1765 else /* Code segment */
1766 {
1767 if ((SegDesc[1] & (1 << 22)) != 0)
1768 SegType = "CODE32";
1769 else
1770 SegType = "CODE16";
1771 }
1772
1773 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
1774 {
1775 KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n",
1776 i / 8, i | Dpl | ul, SegType, Dpl);
1777 }
1778 else
1779 {
1780 KdbpPrint(" %03d 0x%04x %-11s 0x%08x 0x%08x %02d ",
1781 i / 8, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl);
1782
1783 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
1784 {
1785 /* FIXME: Display system segment */
1786 }
1787 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
1788 {
1789 if ((SegDesc[1] & (1 << 10)) != 0) /* Expand-down */
1790 KdbpPrint(" E");
1791
1792 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/W" : " R");
1793
1794 if ((SegDesc[1] & (1 << 8)) != 0)
1795 KdbpPrint(" A");
1796 }
1797 else /* Code segment */
1798 {
1799 if ((SegDesc[1] & (1 << 10)) != 0) /* Conforming */
1800 KdbpPrint(" C");
1801
1802 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/X" : " X");
1803
1804 if ((SegDesc[1] & (1 << 8)) != 0)
1805 KdbpPrint(" A");
1806 }
1807
1808 if ((SegDesc[1] & (1 << 20)) != 0)
1809 KdbpPrint(" AVL");
1810
1811 KdbpPrint("\n");
1812 }
1813 }
1814 }
1815
1816 return TRUE;
1817 }
1818
1819 /*!\brief Displays the KPCR
1820 */
1821 static BOOLEAN
1822 KdbpCmdPcr(
1823 ULONG Argc,
1824 PCHAR Argv[])
1825 {
1826 PKIPCR Pcr = (PKIPCR)KeGetPcr();
1827
1828 #ifdef _M_IX86
1829 KdbpPrint("Current PCR is at 0x%08x.\n", (INT)Pcr);
1830 KdbpPrint(" Tib.ExceptionList: 0x%08x\n"
1831 " Tib.StackBase: 0x%08x\n"
1832 " Tib.StackLimit: 0x%08x\n"
1833 " Tib.SubSystemTib: 0x%08x\n"
1834 " Tib.FiberData/Version: 0x%08x\n"
1835 " Tib.ArbitraryUserPointer: 0x%08x\n"
1836 " Tib.Self: 0x%08x\n"
1837 " Self: 0x%08x\n"
1838 " PCRCB: 0x%08x\n"
1839 " Irql: 0x%02x\n"
1840 " IRR: 0x%08x\n"
1841 " IrrActive: 0x%08x\n"
1842 " IDR: 0x%08x\n"
1843 " KdVersionBlock: 0x%08x\n"
1844 " IDT: 0x%08x\n"
1845 " GDT: 0x%08x\n"
1846 " TSS: 0x%08x\n"
1847 " MajorVersion: 0x%04x\n"
1848 " MinorVersion: 0x%04x\n"
1849 " SetMember: 0x%08x\n"
1850 " StallScaleFactor: 0x%08x\n"
1851 " Number: 0x%02x\n"
1852 " L2CacheAssociativity: 0x%02x\n"
1853 " VdmAlert: 0x%08x\n"
1854 " L2CacheSize: 0x%08x\n"
1855 " InterruptMode: 0x%08x\n",
1856 Pcr->NtTib.ExceptionList, Pcr->NtTib.StackBase, Pcr->NtTib.StackLimit,
1857 Pcr->NtTib.SubSystemTib, Pcr->NtTib.FiberData, Pcr->NtTib.ArbitraryUserPointer,
1858 Pcr->NtTib.Self, Pcr->Self, Pcr->Prcb, Pcr->Irql, Pcr->IRR, Pcr->IrrActive,
1859 Pcr->IDR, Pcr->KdVersionBlock, Pcr->IDT, Pcr->GDT, Pcr->TSS,
1860 Pcr->MajorVersion, Pcr->MinorVersion, Pcr->SetMember, Pcr->StallScaleFactor,
1861 Pcr->Number, Pcr->L2CacheAssociativity,
1862 Pcr->VdmAlert, Pcr->SecondLevelCacheSize, Pcr->InterruptMode);
1863 #elif defined(_M_AMD64)
1864 KdbpPrint("Current PCR is at 0x%x.\n", (INT_PTR)Pcr);
1865 KdbpPrint(" Tib.ExceptionList: 0x%x\n"
1866 " Tib.StackBase: 0x%x\n"
1867 " Tib.StackLimit: 0x%x\n"
1868 " Tib.SubSystemTib: 0x%x\n"
1869 " Tib.FiberData/Version: 0x%x\n"
1870 " Tib.ArbitraryUserPointer: 0x%x\n"
1871 " Tib.Self: 0x%x\n"
1872 " Self: 0x%x\n"
1873 " PCRCB: 0x%x\n"
1874 " Irql: 0x%x\n"
1875 " KdVersionBlock: 0x%08x\n"
1876 " IDT: 0x%08x\n"
1877 " GDT: 0x%08x\n"
1878 " TSS: 0x%08x\n"
1879 " UserRsp: 0x%08x\n"
1880 " MajorVersion: 0x%04x\n"
1881 " MinorVersion: 0x%04x\n"
1882 " StallScaleFactor: 0x%08x\n"
1883 " L2CacheAssociativity: 0x%02x\n"
1884 " L2CacheSize: 0x%08x\n",
1885 Pcr->NtTib.ExceptionList, Pcr->NtTib.StackBase, Pcr->NtTib.StackLimit,
1886 Pcr->NtTib.SubSystemTib, Pcr->NtTib.FiberData, Pcr->NtTib.ArbitraryUserPointer,
1887 Pcr->NtTib.Self, Pcr->Self, Pcr->Prcb, Pcr->Irql,
1888 Pcr->KdVersionBlock, Pcr->IdtBase, Pcr->GdtBase, Pcr->TssBase,Pcr->UserRsp,
1889 Pcr->MajorVersion, Pcr->MinorVersion, Pcr->StallScaleFactor,
1890 Pcr->SecondLevelCacheAssociativity, Pcr->SecondLevelCacheSize);
1891 #endif
1892 return TRUE;
1893 }
1894
1895 /*!\brief Displays the TSS
1896 */
1897 static BOOLEAN
1898 KdbpCmdTss(
1899 ULONG Argc,
1900 PCHAR Argv[])
1901 {
1902 #ifdef _M_IX86
1903 KTSS *Tss = KeGetPcr()->TSS;
1904
1905 KdbpPrint("Current TSS is at 0x%08x.\n", (INT)Tss);
1906 KdbpPrint(" Eip: 0x%08x\n"
1907 " Es: 0x%04x\n"
1908 " Cs: 0x%04x\n"
1909 " Ss: 0x%04x\n"
1910 " Ds: 0x%04x\n"
1911 " Fs: 0x%04x\n"
1912 " Gs: 0x%04x\n"
1913 " IoMapBase: 0x%04x\n",
1914 Tss->Eip, Tss->Es, Tss->Cs, Tss->Ds, Tss->Fs, Tss->Gs, Tss->IoMapBase);
1915
1916 return TRUE;
1917 #endif
1918 return FALSE;
1919 }
1920
1921 /*!\brief Bugchecks the system.
1922 */
1923 static BOOLEAN
1924 KdbpCmdBugCheck(
1925 ULONG Argc,
1926 PCHAR Argv[])
1927 {
1928 /* Set the flag and quit looping */
1929 KdbpBugCheckRequested = TRUE;
1930
1931 return FALSE;
1932 }
1933
1934 /*!\brief Sets or displays a config variables value.
1935 */
1936 static BOOLEAN
1937 KdbpCmdSet(
1938 ULONG Argc,
1939 PCHAR Argv[])
1940 {
1941 LONG l;
1942 BOOLEAN First;
1943 PCHAR pend = 0;
1944 KDB_ENTER_CONDITION ConditionFirst = KdbDoNotEnter;
1945 KDB_ENTER_CONDITION ConditionLast = KdbDoNotEnter;
1946
1947 static const PCHAR ExceptionNames[21] =
1948 {
1949 "ZERODEVIDE", "DEBUGTRAP", "NMI", "INT3", "OVERFLOW", "BOUND", "INVALIDOP",
1950 "NOMATHCOP", "DOUBLEFAULT", "RESERVED(9)", "INVALIDTSS", "SEGMENTNOTPRESENT",
1951 "STACKFAULT", "GPF", "PAGEFAULT", "RESERVED(15)", "MATHFAULT", "ALIGNMENTCHECK",
1952 "MACHINECHECK", "SIMDFAULT", "OTHERS"
1953 };
1954
1955 if (Argc == 1)
1956 {
1957 KdbpPrint("Available settings:\n");
1958 KdbpPrint(" syntax [intel|at&t]\n");
1959 KdbpPrint(" condition [exception|*] [first|last] [never|always|kmode|umode]\n");
1960 KdbpPrint(" break_on_module_load [true|false]\n");
1961 }
1962 else if (strcmp(Argv[1], "syntax") == 0)
1963 {
1964 if (Argc == 2)
1965 {
1966 KdbpPrint("syntax = %s\n", KdbUseIntelSyntax ? "intel" : "at&t");
1967 }
1968 else if (Argc >= 3)
1969 {
1970 if (_stricmp(Argv[2], "intel") == 0)
1971 KdbUseIntelSyntax = TRUE;
1972 else if (_stricmp(Argv[2], "at&t") == 0)
1973 KdbUseIntelSyntax = FALSE;
1974 else
1975 KdbpPrint("Unknown syntax '%s'.\n", Argv[2]);
1976 }
1977 }
1978 else if (strcmp(Argv[1], "condition") == 0)
1979 {
1980 if (Argc == 2)
1981 {
1982 KdbpPrint("Conditions: (First) (Last)\n");
1983 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames) - 1; l++)
1984 {
1985 if (!ExceptionNames[l])
1986 continue;
1987
1988 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
1989 ASSERT(0);
1990
1991 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
1992 ASSERT(0);
1993
1994 KdbpPrint(" #%02d %-20s %-8s %-8s\n", l, ExceptionNames[l],
1995 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
1996 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
1997 }
1998
1999 ASSERT(l == (RTL_NUMBER_OF(ExceptionNames) - 1));
2000 KdbpPrint(" %-20s %-8s %-8s\n", ExceptionNames[l],
2001 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2002 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2003 }
2004 else
2005 {
2006 if (Argc >= 5 && strcmp(Argv[2], "*") == 0) /* Allow * only when setting condition */
2007 {
2008 l = -1;
2009 }
2010 else
2011 {
2012 l = strtoul(Argv[2], &pend, 0);
2013
2014 if (Argv[2] == pend)
2015 {
2016 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++)
2017 {
2018 if (!ExceptionNames[l])
2019 continue;
2020
2021 if (_stricmp(ExceptionNames[l], Argv[2]) == 0)
2022 break;
2023 }
2024 }
2025
2026 if (l >= RTL_NUMBER_OF(ExceptionNames))
2027 {
2028 KdbpPrint("Unknown exception '%s'.\n", Argv[2]);
2029 return TRUE;
2030 }
2031 }
2032
2033 if (Argc > 4)
2034 {
2035 if (_stricmp(Argv[3], "first") == 0)
2036 First = TRUE;
2037 else if (_stricmp(Argv[3], "last") == 0)
2038 First = FALSE;
2039 else
2040 {
2041 KdbpPrint("set condition: second argument must be 'first' or 'last'\n");
2042 return TRUE;
2043 }
2044
2045 if (_stricmp(Argv[4], "never") == 0)
2046 ConditionFirst = KdbDoNotEnter;
2047 else if (_stricmp(Argv[4], "always") == 0)
2048 ConditionFirst = KdbEnterAlways;
2049 else if (_stricmp(Argv[4], "umode") == 0)
2050 ConditionFirst = KdbEnterFromUmode;
2051 else if (_stricmp(Argv[4], "kmode") == 0)
2052 ConditionFirst = KdbEnterFromKmode;
2053 else
2054 {
2055 KdbpPrint("set condition: third argument must be 'never', 'always', 'umode' or 'kmode'\n");
2056 return TRUE;
2057 }
2058
2059 if (!KdbpSetEnterCondition(l, First, ConditionFirst))
2060 {
2061 if (l >= 0)
2062 KdbpPrint("Couldn't change condition for exception #%02d\n", l);
2063 else
2064 KdbpPrint("Couldn't change condition for all exceptions\n", l);
2065 }
2066 }
2067 else /* Argc >= 3 */
2068 {
2069 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
2070 ASSERT(0);
2071
2072 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
2073 ASSERT(0);
2074
2075 if (l < (RTL_NUMBER_OF(ExceptionNames) - 1))
2076 {
2077 KdbpPrint("Condition for exception #%02d (%s): FirstChance %s LastChance %s\n",
2078 l, ExceptionNames[l],
2079 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2080 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2081 }
2082 else
2083 {
2084 KdbpPrint("Condition for all other exceptions: FirstChance %s LastChance %s\n",
2085 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2086 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2087 }
2088 }
2089 }
2090 }
2091 else if (strcmp(Argv[1], "break_on_module_load") == 0)
2092 {
2093 if (Argc == 2)
2094 KdbpPrint("break_on_module_load = %s\n", KdbBreakOnModuleLoad ? "enabled" : "disabled");
2095 else if (Argc >= 3)
2096 {
2097 if (_stricmp(Argv[2], "enable") == 0 || _stricmp(Argv[2], "enabled") == 0 || _stricmp(Argv[2], "true") == 0)
2098 KdbBreakOnModuleLoad = TRUE;
2099 else if (_stricmp(Argv[2], "disable") == 0 || _stricmp(Argv[2], "disabled") == 0 || _stricmp(Argv[2], "false") == 0)
2100 KdbBreakOnModuleLoad = FALSE;
2101 else
2102 KdbpPrint("Unknown setting '%s'.\n", Argv[2]);
2103 }
2104 }
2105 else
2106 {
2107 KdbpPrint("Unknown setting '%s'.\n", Argv[1]);
2108 }
2109
2110 return TRUE;
2111 }
2112
2113 /*!\brief Displays help screen.
2114 */
2115 static BOOLEAN
2116 KdbpCmdHelp(
2117 ULONG Argc,
2118 PCHAR Argv[])
2119 {
2120 ULONG i;
2121
2122 KdbpPrint("Kernel debugger commands:\n");
2123 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
2124 {
2125 if (!KdbDebuggerCommands[i].Syntax) /* Command group */
2126 {
2127 if (i > 0)
2128 KdbpPrint("\n");
2129
2130 KdbpPrint("\x1b[7m* %s:\x1b[0m\n", KdbDebuggerCommands[i].Help);
2131 continue;
2132 }
2133
2134 KdbpPrint(" %-20s - %s\n",
2135 KdbDebuggerCommands[i].Syntax,
2136 KdbDebuggerCommands[i].Help);
2137 }
2138
2139 return TRUE;
2140 }
2141
2142 /*!\brief Prints the given string with printf-like formatting.
2143 *
2144 * \param Format Format of the string/arguments.
2145 * \param ... Variable number of arguments matching the format specified in \a Format.
2146 *
2147 * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
2148 * number of lines required to print a single line from the Buffer in the terminal.
2149 */
2150 VOID
2151 KdbpPrint(
2152 IN PCHAR Format,
2153 IN ... OPTIONAL)
2154 {
2155 static CHAR Buffer[4096];
2156 static BOOLEAN TerminalInitialized = FALSE;
2157 static BOOLEAN TerminalConnected = FALSE;
2158 static BOOLEAN TerminalReportsSize = TRUE;
2159 CHAR c = '\0';
2160 PCHAR p, p2;
2161 ULONG Length;
2162 ULONG i, j;
2163 LONG RowsPrintedByTerminal;
2164 ULONG ScanCode;
2165 va_list ap;
2166
2167 /* Check if the user has aborted output of the current command */
2168 if (KdbOutputAborted)
2169 return;
2170
2171 /* Initialize the terminal */
2172 if (!TerminalInitialized)
2173 {
2174 DbgPrint("\x1b[7h"); /* Enable linewrap */
2175
2176 /* Query terminal type */
2177 /*DbgPrint("\x1b[Z");*/
2178 DbgPrint("\x05");
2179
2180 TerminalInitialized = TRUE;
2181 Length = 0;
2182 KeStallExecutionProcessor(100000);
2183
2184 for (;;)
2185 {
2186 c = KdbpTryGetCharSerial(5000);
2187 if (c == -1)
2188 break;
2189
2190 Buffer[Length++] = c;
2191 if (Length >= (sizeof (Buffer) - 1))
2192 break;
2193 }
2194
2195 Buffer[Length] = '\0';
2196 if (Length > 0)
2197 TerminalConnected = TRUE;
2198 }
2199
2200 /* Get number of rows and columns in terminal */
2201 if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
2202 (KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
2203 {
2204 if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
2205 {
2206 /* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
2207 TerminalReportsSize = FALSE;
2208 KeStallExecutionProcessor(100000);
2209 DbgPrint("\x1b[18t");
2210 c = KdbpTryGetCharSerial(5000);
2211
2212 if (c == KEY_ESC)
2213 {
2214 c = KdbpTryGetCharSerial(5000);
2215 if (c == '[')
2216 {
2217 Length = 0;
2218
2219 for (;;)
2220 {
2221 c = KdbpTryGetCharSerial(5000);
2222 if (c == -1)
2223 break;
2224
2225 Buffer[Length++] = c;
2226 if (isalpha(c) || Length >= (sizeof (Buffer) - 1))
2227 break;
2228 }
2229
2230 Buffer[Length] = '\0';
2231 if (Buffer[0] == '8' && Buffer[1] == ';')
2232 {
2233 for (i = 2; (i < Length) && (Buffer[i] != ';'); i++);
2234
2235 if (Buffer[i] == ';')
2236 {
2237 Buffer[i++] = '\0';
2238
2239 /* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
2240 KdbNumberOfRowsTerminal = strtoul(Buffer + 2, NULL, 0);
2241 KdbNumberOfColsTerminal = strtoul(Buffer + i, NULL, 0);
2242 TerminalReportsSize = TRUE;
2243 }
2244 }
2245 }
2246 /* Clear further characters */
2247 while ((c = KdbpTryGetCharSerial(5000)) != -1);
2248 }
2249 }
2250
2251 if (KdbNumberOfRowsTerminal <= 0)
2252 {
2253 /* Set number of rows to the default. */
2254 KdbNumberOfRowsTerminal = 24;
2255 }
2256 else if (KdbNumberOfColsTerminal <= 0)
2257 {
2258 /* Set number of cols to the default. */
2259 KdbNumberOfColsTerminal = 80;
2260 }
2261 }
2262
2263 /* Get the string */
2264 va_start(ap, Format);
2265 Length = _vsnprintf(Buffer, sizeof (Buffer) - 1, Format, ap);
2266 Buffer[Length] = '\0';
2267 va_end(ap);
2268
2269 p = Buffer;
2270 while (p[0] != '\0')
2271 {
2272 i = strcspn(p, "\n");
2273
2274 /* Calculate the number of lines which will be printed in the terminal
2275 * when outputting the current line
2276 */
2277 if (i > 0)
2278 RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
2279 else
2280 RowsPrintedByTerminal = 0;
2281
2282 if (p[i] == '\n')
2283 RowsPrintedByTerminal++;
2284
2285 /*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
2286
2287 /* Display a prompt if we printed one screen full of text */
2288 if (KdbNumberOfRowsTerminal > 0 &&
2289 (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
2290 {
2291 if (KdbNumberOfColsPrinted > 0)
2292 DbgPrint("\n");
2293
2294 DbgPrint("--- Press q to abort, any other key to continue ---");
2295
2296 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2297 c = KdbpGetCharSerial();
2298 else
2299 c = KdbpGetCharKeyboard(&ScanCode);
2300
2301 if (c == '\r')
2302 {
2303 /* Try to read '\n' which might follow '\r' - if \n is not received here
2304 * it will be interpreted as "return" when the next command should be read.
2305 */
2306 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2307 c = KdbpTryGetCharSerial(5);
2308 else
2309 c = KdbpTryGetCharKeyboard(&ScanCode, 5);
2310 }
2311
2312 DbgPrint("\n");
2313 if (c == 'q')
2314 {
2315 KdbOutputAborted = TRUE;
2316 return;
2317 }
2318
2319 KdbNumberOfRowsPrinted = 0;
2320 KdbNumberOfColsPrinted = 0;
2321 }
2322
2323 /* Insert a NUL after the line and print only the current line. */
2324 if (p[i] == '\n' && p[i + 1] != '\0')
2325 {
2326 c = p[i + 1];
2327 p[i + 1] = '\0';
2328 }
2329 else
2330 {
2331 c = '\0';
2332 }
2333
2334 /* Remove escape sequences from the line if there's no terminal connected */
2335 if (!TerminalConnected)
2336 {
2337 while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
2338 {
2339 if (p2[1] == '[')
2340 {
2341 j = 2;
2342 while (!isalpha(p2[j++]));
2343 strcpy(p2, p2 + j);
2344 }
2345 else
2346 {
2347 strcpy(p2, p2 + 1);
2348 }
2349 }
2350 }
2351
2352 DbgPrint("%s", p);
2353
2354 if (c != '\0')
2355 p[i + 1] = c;
2356
2357 /* Set p to the start of the next line and
2358 * remember the number of rows/cols printed
2359 */
2360 p += i;
2361 if (p[0] == '\n')
2362 {
2363 p++;
2364 KdbNumberOfColsPrinted = 0;
2365 }
2366 else
2367 {
2368 ASSERT(p[0] == '\0');
2369 KdbNumberOfColsPrinted += i;
2370 }
2371
2372 KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
2373 }
2374 }
2375
2376 /*!\brief Appends a command to the command history
2377 *
2378 * \param Command Pointer to the command to append to the history.
2379 */
2380 static VOID
2381 KdbpCommandHistoryAppend(
2382 IN PCHAR Command)
2383 {
2384 ULONG Length1 = strlen(Command) + 1;
2385 ULONG Length2 = 0;
2386 INT i;
2387 PCHAR Buffer;
2388
2389 ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer));
2390
2391 if (Length1 <= 1 ||
2392 (KdbCommandHistory[KdbCommandHistoryIndex] &&
2393 strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0))
2394 {
2395 return;
2396 }
2397
2398 /* Calculate Length1 and Length2 */
2399 Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex;
2400 KdbCommandHistoryBufferIndex += Length1;
2401 if (KdbCommandHistoryBufferIndex >= (LONG)RTL_NUMBER_OF(KdbCommandHistoryBuffer))
2402 {
2403 KdbCommandHistoryBufferIndex -= RTL_NUMBER_OF(KdbCommandHistoryBuffer);
2404 Length2 = KdbCommandHistoryBufferIndex;
2405 Length1 -= Length2;
2406 }
2407
2408 /* Remove previous commands until there is enough space to append the new command */
2409 for (i = KdbCommandHistoryIndex; KdbCommandHistory[i];)
2410 {
2411 if ((Length2 > 0 &&
2412 (KdbCommandHistory[i] >= Buffer ||
2413 KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))) ||
2414 (Length2 <= 0 &&
2415 (KdbCommandHistory[i] >= Buffer &&
2416 KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))))
2417 {
2418 KdbCommandHistory[i] = NULL;
2419 }
2420
2421 i--;
2422 if (i < 0)
2423 i = RTL_NUMBER_OF(KdbCommandHistory) - 1;
2424
2425 if (i == KdbCommandHistoryIndex)
2426 break;
2427 }
2428
2429 /* Make sure the new command history entry is free */
2430 KdbCommandHistoryIndex++;
2431 KdbCommandHistoryIndex %= RTL_NUMBER_OF(KdbCommandHistory);
2432 if (KdbCommandHistory[KdbCommandHistoryIndex])
2433 {
2434 KdbCommandHistory[KdbCommandHistoryIndex] = NULL;
2435 }
2436
2437 /* Append command */
2438 KdbCommandHistory[KdbCommandHistoryIndex] = Buffer;
2439 ASSERT((KdbCommandHistory[KdbCommandHistoryIndex] + Length1) <= KdbCommandHistoryBuffer + RTL_NUMBER_OF(KdbCommandHistoryBuffer));
2440 memcpy(KdbCommandHistory[KdbCommandHistoryIndex], Command, Length1);
2441 if (Length2 > 0)
2442 {
2443 memcpy(KdbCommandHistoryBuffer, Command + Length1, Length2);
2444 }
2445 }
2446
2447 /*!\brief Reads a line of user-input.
2448 *
2449 * \param Buffer Buffer to store the input into. Trailing newlines are removed.
2450 * \param Size Size of \a Buffer.
2451 *
2452 * \note Accepts only \n newlines, \r is ignored.
2453 */
2454 static VOID
2455 KdbpReadCommand(
2456 OUT PCHAR Buffer,
2457 IN ULONG Size)
2458 {
2459 CHAR Key;
2460 PCHAR Orig = Buffer;
2461 ULONG ScanCode = 0;
2462 BOOLEAN EchoOn;
2463 static CHAR LastCommand[1024] = "";
2464 static CHAR NextKey = '\0';
2465 INT CmdHistIndex = -1;
2466 INT i;
2467
2468 EchoOn = !((KdbDebugState & KD_DEBUG_KDNOECHO) != 0);
2469
2470 for (;;)
2471 {
2472 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2473 {
2474 Key = (NextKey == '\0') ? KdbpGetCharSerial() : NextKey;
2475 NextKey = '\0';
2476 ScanCode = 0;
2477 if (Key == KEY_ESC) /* ESC */
2478 {
2479 Key = KdbpGetCharSerial();
2480 if (Key == '[')
2481 {
2482 Key = KdbpGetCharSerial();
2483
2484 switch (Key)
2485 {
2486 case 'A':
2487 ScanCode = KEY_SCAN_UP;
2488 break;
2489 case 'B':
2490 ScanCode = KEY_SCAN_DOWN;
2491 break;
2492 case 'C':
2493 break;
2494 case 'D':
2495 break;
2496 }
2497 }
2498 }
2499 }
2500 else
2501 {
2502 ScanCode = 0;
2503 Key = (NextKey == '\0') ? KdbpGetCharKeyboard(&ScanCode) : NextKey;
2504 NextKey = '\0';
2505 }
2506
2507 if ((ULONG)(Buffer - Orig) >= (Size - 1))
2508 {
2509 /* Buffer is full, accept only newlines */
2510 if (Key != '\n')
2511 continue;
2512 }
2513
2514 if (Key == '\r')
2515 {
2516 /* Read the next char - this is to throw away a \n which most clients should
2517 * send after \r.
2518 */
2519 KeStallExecutionProcessor(100000);
2520
2521 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2522 NextKey = KdbpTryGetCharSerial(5);
2523 else
2524 NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5);
2525
2526 if (NextKey == '\n' || NextKey == -1) /* \n or no response at all */
2527 NextKey = '\0';
2528
2529 KdbpPrint("\n");
2530
2531 /*
2532 * Repeat the last command if the user presses enter. Reduces the
2533 * risk of RSI when single-stepping.
2534 */
2535 if (Buffer == Orig)
2536 {
2537 strncpy(Buffer, LastCommand, Size);
2538 Buffer[Size - 1] = '\0';
2539 }
2540 else
2541 {
2542 *Buffer = '\0';
2543 strncpy(LastCommand, Orig, sizeof (LastCommand));
2544 LastCommand[sizeof (LastCommand) - 1] = '\0';
2545 }
2546
2547 return;
2548 }
2549 else if (Key == KEY_BS || Key == KEY_DEL)
2550 {
2551 if (Buffer > Orig)
2552 {
2553 Buffer--;
2554 *Buffer = 0;
2555
2556 if (EchoOn)
2557 KdbpPrint("%c %c", KEY_BS, KEY_BS);
2558 else
2559 KdbpPrint(" %c", KEY_BS);
2560 }
2561 }
2562 else if (ScanCode == KEY_SCAN_UP)
2563 {
2564 BOOLEAN Print = TRUE;
2565
2566 if (CmdHistIndex < 0)
2567 {
2568 CmdHistIndex = KdbCommandHistoryIndex;
2569 }
2570 else
2571 {
2572 i = CmdHistIndex - 1;
2573
2574 if (i < 0)
2575 CmdHistIndex = RTL_NUMBER_OF(KdbCommandHistory) - 1;
2576
2577 if (KdbCommandHistory[i] && i != KdbCommandHistoryIndex)
2578 CmdHistIndex = i;
2579 else
2580 Print = FALSE;
2581 }
2582
2583 if (Print && KdbCommandHistory[CmdHistIndex])
2584 {
2585 while (Buffer > Orig)
2586 {
2587 Buffer--;
2588 *Buffer = 0;
2589
2590 if (EchoOn)
2591 KdbpPrint("%c %c", KEY_BS, KEY_BS);
2592 else
2593 KdbpPrint(" %c", KEY_BS);
2594 }
2595
2596 i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
2597 memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
2598 Orig[i] = '\0';
2599 Buffer = Orig + i;
2600 KdbpPrint("%s", Orig);
2601 }
2602 }
2603 else if (ScanCode == KEY_SCAN_DOWN)
2604 {
2605 if (CmdHistIndex > 0 && CmdHistIndex != KdbCommandHistoryIndex)
2606 {
2607 i = CmdHistIndex + 1;
2608 if (i >= (INT)RTL_NUMBER_OF(KdbCommandHistory))
2609 i = 0;
2610
2611 if (KdbCommandHistory[i])
2612 {
2613 CmdHistIndex = i;
2614 while (Buffer > Orig)
2615 {
2616 Buffer--;
2617 *Buffer = 0;
2618
2619 if (EchoOn)
2620 KdbpPrint("%c %c", KEY_BS, KEY_BS);
2621 else
2622 KdbpPrint(" %c", KEY_BS);
2623 }
2624
2625 i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
2626 memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
2627 Orig[i] = '\0';
2628 Buffer = Orig + i;
2629 KdbpPrint("%s", Orig);
2630 }
2631 }
2632 }
2633 else
2634 {
2635 if (EchoOn)
2636 KdbpPrint("%c", Key);
2637
2638 *Buffer = Key;
2639 Buffer++;
2640 }
2641 }
2642 }
2643
2644 /*!\brief Parses command line and executes command if found
2645 *
2646 * \param Command Command line to parse and execute if possible.
2647 *
2648 * \retval TRUE Don't continue execution.
2649 * \retval FALSE Continue execution (leave KDB)
2650 */
2651 static BOOLEAN
2652 KdbpDoCommand(
2653 IN PCHAR Command)
2654 {
2655 ULONG i;
2656 PCHAR p;
2657 ULONG Argc;
2658 static PCH Argv[256];
2659 static CHAR OrigCommand[1024];
2660
2661 strncpy(OrigCommand, Command, sizeof(OrigCommand) - 1);
2662 OrigCommand[sizeof(OrigCommand) - 1] = '\0';
2663
2664 Argc = 0;
2665 p = Command;
2666
2667 for (;;)
2668 {
2669 while (*p == '\t' || *p == ' ')
2670 p++;
2671
2672 if (*p == '\0')
2673 break;
2674
2675 i = strcspn(p, "\t ");
2676 Argv[Argc++] = p;
2677 p += i;
2678 if (*p == '\0')
2679 break;
2680
2681 *p = '\0';
2682 p++;
2683 }
2684
2685 if (Argc < 1)
2686 return TRUE;
2687
2688 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
2689 {
2690 if (!KdbDebuggerCommands[i].Name)
2691 continue;
2692
2693 if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0)
2694 {
2695 return KdbDebuggerCommands[i].Fn(Argc, Argv);
2696 }
2697 }
2698
2699 KdbpPrint("Command '%s' is unknown.\n", OrigCommand);
2700 return TRUE;
2701 }
2702
2703 /*!\brief KDB Main Loop.
2704 *
2705 * \param EnteredOnSingleStep TRUE if KDB was entered on single step.
2706 */
2707 VOID
2708 KdbpCliMainLoop(
2709 IN BOOLEAN EnteredOnSingleStep)
2710 {
2711 static CHAR Command[1024];
2712 BOOLEAN Continue;
2713
2714 if (EnteredOnSingleStep)
2715 {
2716 if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
2717 {
2718 KdbpPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip);
2719 }
2720
2721 KdbpPrint(": ");
2722 if (KdbpDisassemble(KdbCurrentTrapFrame->Tf.Eip, KdbUseIntelSyntax) < 0)
2723 {
2724 KdbpPrint("<INVALID>");
2725 }
2726 KdbpPrint("\n");
2727 }
2728
2729 /* Flush the input buffer */
2730 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2731 {
2732 while (KdbpTryGetCharSerial(1) != -1);
2733 }
2734 else
2735 {
2736 ULONG ScanCode;
2737 while (KdbpTryGetCharKeyboard(&ScanCode, 1) != -1);
2738 }
2739
2740 /* Main loop */
2741 do
2742 {
2743 /* Print the prompt */
2744 KdbpPrint("kdb:> ");
2745
2746 /* Read a command and remember it */
2747 KdbpReadCommand(Command, sizeof (Command));
2748 KdbpCommandHistoryAppend(Command);
2749
2750 /* Reset the number of rows/cols printed and output aborted state */
2751 KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
2752 KdbOutputAborted = FALSE;
2753
2754 /* Call the command */
2755 Continue = KdbpDoCommand(Command);
2756 }
2757 while (Continue);
2758 }
2759
2760 /*!\brief Called when a module is loaded.
2761 *
2762 * \param Name Filename of the module which was loaded.
2763 */
2764 VOID
2765 KdbpCliModuleLoaded(
2766 IN PUNICODE_STRING Name)
2767 {
2768 if (!KdbBreakOnModuleLoad)
2769 return;
2770
2771 KdbpPrint("Module %wZ loaded.\n", Name);
2772 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
2773 }
2774
2775 /*!\brief This function is called by KdbEnterDebuggerException...
2776 *
2777 * Used to interpret the init file in a context with a trapframe setup
2778 * (KdbpCliInit call KdbEnter which will call KdbEnterDebuggerException which will
2779 * call this function if KdbInitFileBuffer is not NULL.
2780 */
2781 VOID
2782 KdbpCliInterpretInitFile()
2783 {
2784 PCHAR p1, p2;
2785 INT i;
2786 CHAR c;
2787
2788 /* Execute the commands in the init file */
2789 DPRINT("KDB: Executing KDBinit file...\n");
2790 p1 = KdbInitFileBuffer;
2791 while (p1[0] != '\0')
2792 {
2793 i = strcspn(p1, "\r\n");
2794 if (i > 0)
2795 {
2796 c = p1[i];
2797 p1[i] = '\0';
2798
2799 /* Look for "break" command and comments */
2800 p2 = p1;
2801
2802 while (isspace(p2[0]))
2803 p2++;
2804
2805 if (strncmp(p2, "break", sizeof("break")-1) == 0 &&
2806 (p2[sizeof("break")-1] == '\0' || isspace(p2[sizeof("break")-1])))
2807 {
2808 /* break into the debugger */
2809 KdbpCliMainLoop(FALSE);
2810 }
2811 else if (p2[0] != '#' && p2[0] != '\0') /* Ignore empty lines and comments */
2812 {
2813 KdbpDoCommand(p1);
2814 }
2815
2816 p1[i] = c;
2817 }
2818
2819 p1 += i;
2820 while (p1[0] == '\r' || p1[0] == '\n')
2821 p1++;
2822 }
2823 DPRINT("KDB: KDBinit executed\n");
2824 }
2825
2826 /*!\brief Called when KDB is initialized
2827 *
2828 * Reads the KDBinit file from the SystemRoot\system32\drivers\etc directory and executes it.
2829 */
2830 VOID
2831 KdbpCliInit()
2832 {
2833 NTSTATUS Status;
2834 OBJECT_ATTRIBUTES ObjectAttributes;
2835 UNICODE_STRING FileName;
2836 IO_STATUS_BLOCK Iosb;
2837 FILE_STANDARD_INFORMATION FileStdInfo;
2838 HANDLE hFile = NULL;
2839 INT FileSize;
2840 PCHAR FileBuffer;
2841 ULONG OldEflags;
2842
2843 /* Initialize the object attributes */
2844 RtlInitUnicodeString(&FileName, L"\\SystemRoot\\system32\\drivers\\etc\\KDBinit");
2845 InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL);
2846
2847 /* Open the file */
2848 Status = ZwOpenFile(&hFile, FILE_READ_DATA, &ObjectAttributes, &Iosb, 0,
2849 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
2850 FILE_NO_INTERMEDIATE_BUFFERING);
2851 if (!NT_SUCCESS(Status))
2852 {
2853 DPRINT("Could not open \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
2854 return;
2855 }
2856
2857 /* Get the size of the file */
2858 Status = ZwQueryInformationFile(hFile, &Iosb, &FileStdInfo, sizeof (FileStdInfo),
2859 FileStandardInformation);
2860 if (!NT_SUCCESS(Status))
2861 {
2862 ZwClose(hFile);
2863 DPRINT("Could not query size of \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
2864 return;
2865 }
2866 FileSize = FileStdInfo.EndOfFile.u.LowPart;
2867
2868 /* Allocate memory for the file */
2869 FileBuffer = ExAllocatePool(PagedPool, FileSize + 1); /* add 1 byte for terminating '\0' */
2870 if (!FileBuffer)
2871 {
2872 ZwClose(hFile);
2873 DPRINT("Could not allocate %d bytes for KDBinit file\n", FileSize);
2874 return;
2875 }
2876
2877 /* Load file into memory */
2878 Status = ZwReadFile(hFile, 0, 0, 0, &Iosb, FileBuffer, FileSize, 0, 0);
2879 ZwClose(hFile);
2880
2881 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
2882 {
2883 ExFreePool(FileBuffer);
2884 DPRINT("Could not read KDBinit file into memory (Status 0x%lx)\n", Status);
2885 return;
2886 }
2887
2888 FileSize = min(FileSize, (INT)Iosb.Information);
2889 FileBuffer[FileSize] = '\0';
2890
2891 /* Enter critical section */
2892 OldEflags = __readeflags();
2893 _disable();
2894
2895 /* Interpret the init file... */
2896 KdbInitFileBuffer = FileBuffer;
2897 KdbEnter();
2898 KdbInitFileBuffer = NULL;
2899
2900 /* Leave critical section */
2901 __writeeflags(OldEflags);
2902
2903 ExFreePool(FileBuffer);
2904 }
2905
2906 VOID
2907 NTAPI
2908 KdpSerialDebugPrint(
2909 LPSTR Message,
2910 ULONG Length
2911 );
2912
2913 STRING KdpPromptString = RTL_CONSTANT_STRING("kdb:> ");
2914 extern KSPIN_LOCK KdpSerialSpinLock;
2915
2916 ULONG
2917 NTAPI
2918 KdpPrompt(IN LPSTR InString,
2919 IN USHORT InStringLength,
2920 OUT LPSTR OutString,
2921 IN USHORT OutStringLength)
2922 {
2923 USHORT i;
2924 CHAR Response;
2925 ULONG DummyScanCode;
2926 KIRQL OldIrql;
2927
2928 /* Acquire the printing spinlock without waiting at raised IRQL */
2929 while (TRUE)
2930 {
2931 /* Wait when the spinlock becomes available */
2932 while (!KeTestSpinLock(&KdpSerialSpinLock));
2933
2934 /* Spinlock was free, raise IRQL */
2935 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
2936
2937 /* Try to get the spinlock */
2938 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock))
2939 break;
2940
2941 /* Someone else got the spinlock, lower IRQL back */
2942 KeLowerIrql(OldIrql);
2943 }
2944
2945 /* Loop the string to send */
2946 for (i = 0; i < InStringLength; i++)
2947 {
2948 /* Print it to serial */
2949 KdPortPutByteEx(&SerialPortInfo, *(PCHAR)(InString + i));
2950 }
2951
2952 /* Print a new line for log neatness */
2953 KdPortPutByteEx(&SerialPortInfo, '\r');
2954 KdPortPutByteEx(&SerialPortInfo, '\n');
2955
2956 /* Print the kdb prompt */
2957 for (i = 0; i < KdpPromptString.Length; i++)
2958 {
2959 /* Print it to serial */
2960 KdPortPutByteEx(&SerialPortInfo,
2961 *(KdpPromptString.Buffer + i));
2962 }
2963
2964 /* Loop the whole string */
2965 for (i = 0; i < OutStringLength; i++)
2966 {
2967 /* Check if this is serial debugging mode */
2968 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2969 {
2970 /* Get the character from serial */
2971 do
2972 {
2973 Response = KdbpTryGetCharSerial(MAXULONG);
2974 } while (Response == -1);
2975 }
2976 else
2977 {
2978 /* Get the response from the keyboard */
2979 do
2980 {
2981 Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG);
2982 } while (Response == -1);
2983 }
2984
2985 /* Check for return */
2986 if (Response == '\r')
2987 {
2988 /*
2989 * We might need to discard the next '\n'.
2990 * Wait a bit to make sure we receive it.
2991 */
2992 KeStallExecutionProcessor(100000);
2993
2994 /* Check the mode */
2995 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2996 {
2997 /* Read and discard the next character, if any */
2998 KdbpTryGetCharSerial(5);
2999 }
3000 else
3001 {
3002 /* Read and discard the next character, if any */
3003 KdbpTryGetCharKeyboard(&DummyScanCode, 5);
3004 }
3005
3006 /*
3007 * Null terminate the output string -- documentation states that
3008 * DbgPrompt does not null terminate, but it does
3009 */
3010 *(PCHAR)(OutString + i) = 0;
3011
3012 /* Print a new line */
3013 KdPortPutByteEx(&SerialPortInfo, '\r');
3014 KdPortPutByteEx(&SerialPortInfo, '\n');
3015
3016 /* Release spinlock */
3017 KiReleaseSpinLock(&KdpSerialSpinLock);
3018
3019 /* Lower IRQL back */
3020 KeLowerIrql(OldIrql);
3021
3022 /* Return the length */
3023 return OutStringLength + 1;
3024 }
3025
3026 /* Write it back and print it to the log */
3027 *(PCHAR)(OutString + i) = Response;
3028 KdPortPutByteEx(&SerialPortInfo, Response);
3029 }
3030
3031 /* Print a new line */
3032 KdPortPutByteEx(&SerialPortInfo, '\r');
3033 KdPortPutByteEx(&SerialPortInfo, '\n');
3034
3035 /* Release spinlock */
3036 KiReleaseSpinLock(&KdpSerialSpinLock);
3037
3038 /* Lower IRQL back */
3039 KeLowerIrql(OldIrql);
3040
3041 /* Return the length */
3042 return OutStringLength;
3043 }