Sync tools to 45592
[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",Result);
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("<%p>:", 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("<%p>: ", 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[32] = { " 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
612 };
613
614 if (Argv[0][0] == 'r') /* regs */
615 {
616 #ifdef _M_IX86
617 KdbpPrint("CS:EIP 0x%04x:0x%08x\n"
618 "SS:ESP 0x%04x:0x%08x\n"
619 " EAX 0x%08x EBX 0x%08x\n"
620 " ECX 0x%08x EDX 0x%08x\n"
621 " ESI 0x%08x EDI 0x%08x\n"
622 " EBP 0x%08x\n",
623 Tf->SegCs & 0xFFFF, Tf->Eip,
624 Tf->HardwareSegSs, Tf->HardwareEsp,
625 Tf->Eax, Tf->Ebx,
626 Tf->Ecx, Tf->Edx,
627 Tf->Esi, Tf->Edi,
628 Tf->Ebp);
629 #elif defined(_M_AMD64)
630 KdbpPrint("CS:RIP 0x%04x:0x%p\n"
631 "SS:RSP 0x%04x:0x%p\n"
632 " RAX 0x%p RBX 0x%p\n"
633 " RCX 0x%p RDX 0x%p\n"
634 " RSI 0x%p RDI 0x%p\n"
635 " RBP 0x%p R8 0x%p\n"
636 " R9 0x%p R10 0x%p\n"
637 " R11 0x%p\n",
638 Tf->SegCs & 0xFFFF, Tf->Rip,
639 Tf->SegSs, Tf->Rsp,
640 Tf->Rax, Tf->Rbx,
641 Tf->Rcx, Tf->Rdx,
642 Tf->Rsi, Tf->Rdi,
643 Tf->Rbp, Tf->R8,
644 Tf->R9, Tf->R10,
645 Tf->R11);
646 #endif
647 KdbpPrint("EFLAGS 0x%08x ", Tf->EFlags);
648
649 for (i = 0; i < 32; i++)
650 {
651 if (i == 1)
652 {
653 if ((Tf->EFlags & (1 << 1)) == 0)
654 KdbpPrint(" !BIT1");
655 }
656 else if (i == 12)
657 {
658 KdbpPrint(" IOPL%d", (Tf->EFlags >> 12) & 3);
659 }
660 else if (i == 13)
661 {
662 }
663 else if ((Tf->EFlags & (1 << i)) != 0)
664 {
665 KdbpPrint(EflagsBits[i]);
666 }
667 }
668
669 KdbpPrint("\n");
670 }
671 else if (Argv[0][0] == 'c') /* cregs */
672 {
673 ULONG_PTR Cr0, Cr2, Cr3, Cr4;
674 KDESCRIPTOR Gdtr, Idtr;
675 #if defined(_M_IX86)
676 USHORT Ldtr;
677 Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
678 Ldtr = Ke386GetLocalDescriptorTable();
679 #elif defined(_M_AMD64)
680 __sgdt(&Gdtr.Limit);
681 #endif
682 static const PCHAR Cr0Bits[32] = { " PE", " MP", " EM", " TS", " ET", " NE", NULL, NULL,
683 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
684 " WP", NULL, " AM", NULL, NULL, NULL, NULL, NULL,
685 NULL, NULL, NULL, NULL, NULL, " NW", " CD", " PG" };
686 static const PCHAR Cr4Bits[32] = { " VME", " PVI", " TSD", " DE", " PSE", " PAE", " MCE", " PGE",
687 " PCE", " OSFXSR", " OSXMMEXCPT", NULL, NULL, NULL, NULL, NULL,
688 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
689 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
690
691 Cr0 = KdbCurrentTrapFrame->Cr0;
692 Cr2 = KdbCurrentTrapFrame->Cr2;
693 Cr3 = KdbCurrentTrapFrame->Cr3;
694 Cr4 = KdbCurrentTrapFrame->Cr4;
695
696 /* Get interrupt descriptor table regs */
697 __sidt(&Idtr.Limit);
698
699 /* Display the control registers */
700 KdbpPrint("CR0 0x%p ", Cr0);
701
702 for (i = 0; i < 32; i++)
703 {
704 if (!Cr0Bits[i])
705 continue;
706
707 if ((Cr0 & (1 << i)) != 0)
708 KdbpPrint(Cr0Bits[i]);
709 }
710
711 KdbpPrint("\nCR2 0x%p\n", Cr2);
712 KdbpPrint("CR3 0x%p Pagedir-Base 0x%p %s%s\n", Cr3, (Cr3 & 0xfffff000),
713 (Cr3 & (1 << 3)) ? " PWT" : "", (Cr3 & (1 << 4)) ? " PCD" : "" );
714 KdbpPrint("CR4 0x%p ", Cr4);
715
716 for (i = 0; i < 32; i++)
717 {
718 if (!Cr4Bits[i])
719 continue;
720
721 if ((Cr4 & (1 << i)) != 0)
722 KdbpPrint(Cr4Bits[i]);
723 }
724
725 /* Display the descriptor table regs */
726 KdbpPrint("\nGDTR Base 0x%p Size 0x%04x\n", Gdtr.Base, Gdtr.Limit);
727 #ifdef _M_IX86
728 KdbpPrint("LDTR 0x%p\n", Ldtr);
729 #endif
730 KdbpPrint("IDTR Base 0x%p Size 0x%04x\n", Idtr.Base, Idtr.Limit);
731 }
732 else if (Argv[0][0] == 's') /* sregs */
733 {
734 KdbpPrint("CS 0x%04x Index 0x%04x %cDT RPL%d\n",
735 Tf->SegCs & 0xffff, (Tf->SegCs & 0xffff) >> 3,
736 (Tf->SegCs & (1 << 2)) ? 'L' : 'G', Tf->SegCs & 3);
737 KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n",
738 Tf->SegDs, Tf->SegDs >> 3, (Tf->SegDs & (1 << 2)) ? 'L' : 'G', Tf->SegDs & 3);
739 KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n",
740 Tf->SegEs, Tf->SegEs >> 3, (Tf->SegEs & (1 << 2)) ? 'L' : 'G', Tf->SegEs & 3);
741 KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n",
742 Tf->SegFs, Tf->SegFs >> 3, (Tf->SegFs & (1 << 2)) ? 'L' : 'G', Tf->SegFs & 3);
743 KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n",
744 Tf->SegGs, Tf->SegGs >> 3, (Tf->SegGs & (1 << 2)) ? 'L' : 'G', Tf->SegGs & 3);
745 #ifdef _M_IX86
746 KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
747 Tf->HardwareSegSs, Tf->HardwareSegSs >> 3, (Tf->HardwareSegSs & (1 << 2)) ? 'L' : 'G', Tf->HardwareSegSs & 3);
748 #else
749 KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
750 Tf->SegSs, Tf->SegSs >> 3, (Tf->SegSs & (1 << 2)) ? 'L' : 'G', Tf->SegSs & 3);
751 #endif
752
753 }
754 else /* dregs */
755 {
756 ASSERT(Argv[0][0] == 'd');
757 KdbpPrint("DR0 0x%p\n"
758 "DR1 0x%p\n"
759 "DR2 0x%p\n"
760 "DR3 0x%p\n"
761 "DR6 0x%p\n"
762 "DR7 0x%p\n",
763 Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3,
764 Tf->Dr6, Tf->Dr7);
765 }
766
767 return TRUE;
768 }
769
770 /*!\brief Displays a backtrace.
771 */
772 static BOOLEAN
773 KdbpCmdBackTrace(
774 ULONG Argc,
775 PCHAR Argv[])
776 {
777 ULONG Count;
778 ULONG ul;
779 ULONGLONG Result = 0;
780 #ifdef _M_IX86
781 ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp;
782 #elif defined(_M_AMD64)
783 ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Rbp;
784 #endif
785 ULONG_PTR Address;
786
787 if (Argc >= 2)
788 {
789 /* Check for [L count] part */
790 ul = 0;
791
792 if (strcmp(Argv[Argc-2], "L") == 0)
793 {
794 ul = strtoul(Argv[Argc-1], NULL, 0);
795 if (ul > 0)
796 {
797 Count = ul;
798 Argc -= 2;
799 }
800 }
801 else if (Argv[Argc-1][0] == 'L')
802 {
803 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
804 if (ul > 0)
805 {
806 Count = ul;
807 Argc--;
808 }
809 }
810
811 /* Put the remaining arguments back together */
812 Argc--;
813 for (ul = 1; ul < Argc; ul++)
814 {
815 Argv[ul][strlen(Argv[ul])] = ' ';
816 }
817 Argc++;
818 }
819
820 /* Check if frame addr or thread id is given. */
821 if (Argc > 1)
822 {
823 if (Argv[1][0] == '*')
824 {
825 Argv[1]++;
826
827 /* Evaluate the expression */
828 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
829 return TRUE;
830
831 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
832 KdbpPrint("Warning: Address %I64x is beeing truncated\n",Result);
833
834 Frame = (ULONG_PTR)Result;
835 }
836 else
837 {
838 KdbpPrint("Thread backtrace not supported yet!\n");
839 return TRUE;
840 }
841 }
842 else
843 {
844 #ifdef _M_AMD64
845 KdbpPrint("Rip:\n");
846 #else
847 KdbpPrint("Eip:\n");
848 #endif
849
850 /* Try printing the function at EIP */
851 if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
852 KdbpPrint("<%p>\n", KdbCurrentTrapFrame->Tf.Eip);
853 else
854 KdbpPrint("\n");
855 }
856
857 KdbpPrint("Frames:\n");
858 for (;;)
859 {
860 if (Frame == 0)
861 break;
862
863 if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR))))
864 {
865 KdbpPrint("Couldn't access memory at 0x%p!\n", Frame + sizeof(ULONG_PTR));
866 break;
867 }
868
869 if (!KdbSymPrintAddress((PVOID)Address))
870 KdbpPrint("<%x>\n", Address);
871 else
872 KdbpPrint("\n");
873
874 if (Address == 0)
875 break;
876
877 if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR))))
878 {
879 KdbpPrint("Couldn't access memory at 0x%p!\n", Frame);
880 break;
881 }
882 }
883
884 return TRUE;
885 }
886
887 /*!\brief Continues execution of the system/leaves KDB.
888 */
889 static BOOLEAN
890 KdbpCmdContinue(
891 ULONG Argc,
892 PCHAR Argv[])
893 {
894 /* Exit the main loop */
895 return FALSE;
896 }
897
898 /*!\brief Continues execution of the system/leaves KDB.
899 */
900 static BOOLEAN
901 KdbpCmdStep(
902 ULONG Argc,
903 PCHAR Argv[])
904 {
905 ULONG Count = 1;
906
907 if (Argc > 1)
908 {
909 Count = strtoul(Argv[1], NULL, 0);
910 if (Count == 0)
911 {
912 KdbpPrint("%s: Integer argument required\n", Argv[0]);
913 return TRUE;
914 }
915 }
916
917 if (Argv[0][0] == 'n')
918 KdbSingleStepOver = TRUE;
919 else
920 KdbSingleStepOver = FALSE;
921
922 /* Set the number of single steps and return to the interrupted code. */
923 KdbNumSingleSteps = Count;
924
925 return FALSE;
926 }
927
928 /*!\brief Lists breakpoints.
929 */
930 static BOOLEAN
931 KdbpCmdBreakPointList(
932 ULONG Argc,
933 PCHAR Argv[])
934 {
935 LONG l;
936 ULONG_PTR Address = 0;
937 KDB_BREAKPOINT_TYPE Type = 0;
938 KDB_ACCESS_TYPE AccessType = 0;
939 UCHAR Size = 0;
940 UCHAR DebugReg = 0;
941 BOOLEAN Enabled = FALSE;
942 BOOLEAN Global = FALSE;
943 PEPROCESS Process = NULL;
944 PCHAR str1, str2, ConditionExpr, GlobalOrLocal;
945 CHAR Buffer[20];
946
947 l = KdbpGetNextBreakPointNr(0);
948 if (l < 0)
949 {
950 KdbpPrint("No breakpoints.\n");
951 return TRUE;
952 }
953
954 KdbpPrint("Breakpoints:\n");
955 do
956 {
957 if (!KdbpGetBreakPointInfo(l, &Address, &Type, &Size, &AccessType, &DebugReg,
958 &Enabled, &Global, &Process, &ConditionExpr))
959 {
960 continue;
961 }
962
963 if (l == KdbLastBreakPointNr)
964 {
965 str1 = "\x1b[1m*";
966 str2 = "\x1b[0m";
967 }
968 else
969 {
970 str1 = " ";
971 str2 = "";
972 }
973
974 if (Global)
975 {
976 GlobalOrLocal = " global";
977 }
978 else
979 {
980 GlobalOrLocal = Buffer;
981 sprintf(Buffer, " PID 0x%08lx",
982 (ULONG_PTR)(Process ? Process->UniqueProcessId : INVALID_HANDLE_VALUE));
983 }
984
985 if (Type == KdbBreakPointSoftware || Type == KdbBreakPointTemporary)
986 {
987 KdbpPrint(" %s%03d BPX 0x%08x%s%s%s%s%s\n",
988 str1, l, Address,
989 Enabled ? "" : " disabled",
990 GlobalOrLocal,
991 ConditionExpr ? " IF " : "",
992 ConditionExpr ? ConditionExpr : "",
993 str2);
994 }
995 else if (Type == KdbBreakPointHardware)
996 {
997 if (!Enabled)
998 {
999 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s disabled%s%s%s%s\n", str1, l, Address,
1000 KDB_ACCESS_TYPE_TO_STRING(AccessType),
1001 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
1002 GlobalOrLocal,
1003 ConditionExpr ? " IF " : "",
1004 ConditionExpr ? ConditionExpr : "",
1005 str2);
1006 }
1007 else
1008 {
1009 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s DR%d%s%s%s%s\n", str1, l, Address,
1010 KDB_ACCESS_TYPE_TO_STRING(AccessType),
1011 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
1012 DebugReg,
1013 GlobalOrLocal,
1014 ConditionExpr ? " IF " : "",
1015 ConditionExpr ? ConditionExpr : "",
1016 str2);
1017 }
1018 }
1019 }
1020 while ((l = KdbpGetNextBreakPointNr(l+1)) >= 0);
1021
1022 return TRUE;
1023 }
1024
1025 /*!\brief Enables, disables or clears a breakpoint.
1026 */
1027 static BOOLEAN
1028 KdbpCmdEnableDisableClearBreakPoint(
1029 ULONG Argc,
1030 PCHAR Argv[])
1031 {
1032 PCHAR pend;
1033 ULONG BreakPointNr;
1034
1035 if (Argc < 2)
1036 {
1037 KdbpPrint("%s: argument required\n", Argv[0]);
1038 return TRUE;
1039 }
1040
1041 pend = Argv[1];
1042 BreakPointNr = strtoul(Argv[1], &pend, 0);
1043 if (pend == Argv[1] || *pend != '\0')
1044 {
1045 KdbpPrint("%s: integer argument required\n", Argv[0]);
1046 return TRUE;
1047 }
1048
1049 if (Argv[0][1] == 'e') /* enable */
1050 {
1051 KdbpEnableBreakPoint(BreakPointNr, NULL);
1052 }
1053 else if (Argv [0][1] == 'd') /* disable */
1054 {
1055 KdbpDisableBreakPoint(BreakPointNr, NULL);
1056 }
1057 else /* clear */
1058 {
1059 ASSERT(Argv[0][1] == 'c');
1060 KdbpDeleteBreakPoint(BreakPointNr, NULL);
1061 }
1062
1063 return TRUE;
1064 }
1065
1066 /*!\brief Sets a software or hardware (memory) breakpoint at the given address.
1067 */
1068 static BOOLEAN
1069 KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[])
1070 {
1071 ULONGLONG Result = 0;
1072 ULONG_PTR Address;
1073 KDB_BREAKPOINT_TYPE Type;
1074 UCHAR Size = 0;
1075 KDB_ACCESS_TYPE AccessType = 0;
1076 ULONG AddressArgIndex, i;
1077 LONG ConditionArgIndex;
1078 BOOLEAN Global = TRUE;
1079
1080 if (Argv[0][2] == 'x') /* software breakpoint */
1081 {
1082 if (Argc < 2)
1083 {
1084 KdbpPrint("bpx: Address argument required.\n");
1085 return TRUE;
1086 }
1087
1088 AddressArgIndex = 1;
1089 Type = KdbBreakPointSoftware;
1090 }
1091 else /* memory breakpoint */
1092 {
1093 ASSERT(Argv[0][2] == 'm');
1094
1095 if (Argc < 2)
1096 {
1097 KdbpPrint("bpm: Access type argument required (one of r, w, rw, x)\n");
1098 return TRUE;
1099 }
1100
1101 if (_stricmp(Argv[1], "x") == 0)
1102 AccessType = KdbAccessExec;
1103 else if (_stricmp(Argv[1], "r") == 0)
1104 AccessType = KdbAccessRead;
1105 else if (_stricmp(Argv[1], "w") == 0)
1106 AccessType = KdbAccessWrite;
1107 else if (_stricmp(Argv[1], "rw") == 0)
1108 AccessType = KdbAccessReadWrite;
1109 else
1110 {
1111 KdbpPrint("bpm: Unknown access type '%s'\n", Argv[1]);
1112 return TRUE;
1113 }
1114
1115 if (Argc < 3)
1116 {
1117 KdbpPrint("bpm: %s argument required.\n", AccessType == KdbAccessExec ? "Address" : "Memory size");
1118 return TRUE;
1119 }
1120
1121 AddressArgIndex = 3;
1122 if (_stricmp(Argv[2], "byte") == 0)
1123 Size = 1;
1124 else if (_stricmp(Argv[2], "word") == 0)
1125 Size = 2;
1126 else if (_stricmp(Argv[2], "dword") == 0)
1127 Size = 4;
1128 else if (_stricmp(Argv[2], "qword") == 0)
1129 Size = 8;
1130 else if (AccessType == KdbAccessExec)
1131 {
1132 Size = 1;
1133 AddressArgIndex--;
1134 }
1135 else
1136 {
1137 KdbpPrint("bpm: Unknown memory size '%s'\n", Argv[2]);
1138 return TRUE;
1139 }
1140
1141 if (Argc <= AddressArgIndex)
1142 {
1143 KdbpPrint("bpm: Address argument required.\n");
1144 return TRUE;
1145 }
1146
1147 Type = KdbBreakPointHardware;
1148 }
1149
1150 /* Put the arguments back together */
1151 ConditionArgIndex = -1;
1152 for (i = AddressArgIndex; i < (Argc-1); i++)
1153 {
1154 if (strcmp(Argv[i+1], "IF") == 0) /* IF found */
1155 {
1156 ConditionArgIndex = i + 2;
1157 if (ConditionArgIndex >= Argc)
1158 {
1159 KdbpPrint("%s: IF requires condition expression.\n", Argv[0]);
1160 return TRUE;
1161 }
1162
1163 for (i = ConditionArgIndex; i < (Argc-1); i++)
1164 Argv[i][strlen(Argv[i])] = ' ';
1165
1166 break;
1167 }
1168
1169 Argv[i][strlen(Argv[i])] = ' ';
1170 }
1171
1172 /* Evaluate the address expression */
1173 if (!KdbpEvaluateExpression(Argv[AddressArgIndex],
1174 sizeof("kdb:> ")-1 + (Argv[AddressArgIndex]-Argv[0]),
1175 &Result))
1176 {
1177 return TRUE;
1178 }
1179
1180 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1181 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0],Result);
1182
1183 Address = (ULONG_PTR)Result;
1184
1185 KdbpInsertBreakPoint(Address, Type, Size, AccessType,
1186 (ConditionArgIndex < 0) ? NULL : Argv[ConditionArgIndex],
1187 Global, NULL);
1188
1189 return TRUE;
1190 }
1191
1192 /*!\brief Lists threads or switches to another thread context.
1193 */
1194 static BOOLEAN
1195 KdbpCmdThread(
1196 ULONG Argc,
1197 PCHAR Argv[])
1198 {
1199 PLIST_ENTRY Entry;
1200 PETHREAD Thread = NULL;
1201 PEPROCESS Process = NULL;
1202 BOOLEAN ReferencedThread = FALSE, ReferencedProcess = FALSE;
1203 PULONG_PTR Esp;
1204 PULONG_PTR Ebp;
1205 ULONG_PTR Eip;
1206 ULONG_PTR ul = 0;
1207 PCHAR State, pend, str1, str2;
1208 static const PCHAR ThreadStateToString[DeferredReady+1] =
1209 {
1210 "Initialized", "Ready", "Running",
1211 "Standby", "Terminated", "Waiting",
1212 "Transition", "DeferredReady"
1213 };
1214
1215 ASSERT(KdbCurrentProcess);
1216
1217 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1218 {
1219 Process = KdbCurrentProcess;
1220
1221 if (Argc >= 3)
1222 {
1223 ul = strtoul(Argv[2], &pend, 0);
1224 if (Argv[2] == pend)
1225 {
1226 KdbpPrint("thread: '%s' is not a valid process id!\n", Argv[2]);
1227 return TRUE;
1228 }
1229
1230 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1231 {
1232 KdbpPrint("thread: Invalid process id!\n");
1233 return TRUE;
1234 }
1235
1236 /* Remember our reference */
1237 ReferencedProcess = TRUE;
1238 }
1239
1240 Entry = Process->ThreadListHead.Flink;
1241 if (Entry == &Process->ThreadListHead)
1242 {
1243 if (Argc >= 3)
1244 KdbpPrint("No threads in process 0x%08x!\n", ul);
1245 else
1246 KdbpPrint("No threads in current process!\n");
1247
1248 if (ReferencedProcess)
1249 ObDereferenceObject(Process);
1250
1251 return TRUE;
1252 }
1253
1254 KdbpPrint(" TID State Prior. Affinity EBP EIP\n");
1255 do
1256 {
1257 Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
1258
1259 if (Thread == KdbCurrentThread)
1260 {
1261 str1 = "\x1b[1m*";
1262 str2 = "\x1b[0m";
1263 }
1264 else
1265 {
1266 str1 = " ";
1267 str2 = "";
1268 }
1269
1270 if (Thread->Tcb.TrapFrame)
1271 {
1272 #ifdef _M_IX86
1273 if (Thread->Tcb.TrapFrame->PreviousPreviousMode == KernelMode)
1274 Esp = (PULONG)Thread->Tcb.TrapFrame->TempEsp;
1275 else
1276 Esp = (PULONG)Thread->Tcb.TrapFrame->HardwareEsp;
1277
1278 Ebp = (PULONG)Thread->Tcb.TrapFrame->Ebp;
1279 Eip = Thread->Tcb.TrapFrame->Eip;
1280
1281 #elif defined(_M_AMD64)
1282 Esp = (PULONG_PTR)Thread->Tcb.TrapFrame->Rsp;
1283
1284 Ebp = (PULONG_PTR)Thread->Tcb.TrapFrame->Rbp;
1285 Eip = Thread->Tcb.TrapFrame->Eip;
1286 #endif
1287 }
1288 else
1289 {
1290 Esp = (PULONG_PTR)Thread->Tcb.KernelStack;
1291 Ebp = (PULONG_PTR)Esp[4];
1292 Eip = 0;
1293
1294 if (Ebp) /* FIXME: Should we attach to the process to read Ebp[1]? */
1295 KdbpSafeReadMemory(&Eip, Ebp + 1, sizeof (Eip));
1296 }
1297
1298 if (Thread->Tcb.State < (DeferredReady + 1))
1299 State = ThreadStateToString[Thread->Tcb.State];
1300 else
1301 State = "Unknown";
1302
1303 KdbpPrint(" %s0x%p %-11s %3d 0x%p 0x%p 0x%p%s\n",
1304 str1,
1305 Thread->Cid.UniqueThread,
1306 State,
1307 Thread->Tcb.Priority,
1308 Thread->Tcb.Affinity,
1309 Ebp,
1310 Eip,
1311 str2);
1312
1313 Entry = Entry->Flink;
1314 }
1315 while (Entry != &Process->ThreadListHead);
1316
1317 /* Release our reference, if any */
1318 if (ReferencedProcess)
1319 ObDereferenceObject(Process);
1320 }
1321 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1322 {
1323 if (Argc < 3)
1324 {
1325 KdbpPrint("thread attach: thread id argument required!\n");
1326 return TRUE;
1327 }
1328
1329 ul = strtoul(Argv[2], &pend, 0);
1330 if (Argv[2] == pend)
1331 {
1332 KdbpPrint("thread attach: '%s' is not a valid thread id!\n", Argv[2]);
1333 return TRUE;
1334 }
1335
1336 if (!KdbpAttachToThread((PVOID)ul))
1337 {
1338 return TRUE;
1339 }
1340
1341 KdbpPrint("Attached to thread 0x%08x.\n", ul);
1342 }
1343 else
1344 {
1345 Thread = KdbCurrentThread;
1346
1347 if (Argc >= 2)
1348 {
1349 ul = strtoul(Argv[1], &pend, 0);
1350 if (Argv[1] == pend)
1351 {
1352 KdbpPrint("thread: '%s' is not a valid thread id!\n", Argv[1]);
1353 return TRUE;
1354 }
1355
1356 if (!NT_SUCCESS(PsLookupThreadByThreadId((PVOID)ul, &Thread)))
1357 {
1358 KdbpPrint("thread: Invalid thread id!\n");
1359 return TRUE;
1360 }
1361
1362 /* Remember our reference */
1363 ReferencedThread = TRUE;
1364 }
1365
1366 if (Thread->Tcb.State < (DeferredReady + 1))
1367 State = ThreadStateToString[Thread->Tcb.State];
1368 else
1369 State = "Unknown";
1370
1371 KdbpPrint("%s"
1372 " TID: 0x%08x\n"
1373 " State: %s (0x%x)\n"
1374 " Priority: %d\n"
1375 " Affinity: 0x%08x\n"
1376 " Initial Stack: 0x%p\n"
1377 " Stack Limit: 0x%p\n"
1378 " Stack Base: 0x%p\n"
1379 " Kernel Stack: 0x%p\n"
1380 " Trap Frame: 0x%p\n"
1381 " NPX State: %s (0x%x)\n",
1382 (Argc < 2) ? "Current Thread:\n" : "",
1383 Thread->Cid.UniqueThread,
1384 State, Thread->Tcb.State,
1385 Thread->Tcb.Priority,
1386 Thread->Tcb.Affinity,
1387 Thread->Tcb.InitialStack,
1388 Thread->Tcb.StackLimit,
1389 Thread->Tcb.StackBase,
1390 Thread->Tcb.KernelStack,
1391 Thread->Tcb.TrapFrame,
1392 NPX_STATE_TO_STRING(Thread->Tcb.NpxState), Thread->Tcb.NpxState);
1393
1394 /* Release our reference if we had one */
1395 if (ReferencedThread)
1396 ObDereferenceObject(Thread);
1397 }
1398
1399 return TRUE;
1400 }
1401
1402 /*!\brief Lists processes or switches to another process context.
1403 */
1404 static BOOLEAN
1405 KdbpCmdProc(
1406 ULONG Argc,
1407 PCHAR Argv[])
1408 {
1409 PLIST_ENTRY Entry;
1410 PEPROCESS Process;
1411 BOOLEAN ReferencedProcess = FALSE;
1412 PCHAR State, pend, str1, str2;
1413 ULONG_PTR ul;
1414 extern LIST_ENTRY PsActiveProcessHead;
1415
1416 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1417 {
1418 Entry = PsActiveProcessHead.Flink;
1419 if (!Entry || Entry == &PsActiveProcessHead)
1420 {
1421 KdbpPrint("No processes in the system!\n");
1422 return TRUE;
1423 }
1424
1425 KdbpPrint(" PID State Filename\n");
1426 do
1427 {
1428 Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
1429
1430 if (Process == KdbCurrentProcess)
1431 {
1432 str1 = "\x1b[1m*";
1433 str2 = "\x1b[0m";
1434 }
1435 else
1436 {
1437 str1 = " ";
1438 str2 = "";
1439 }
1440
1441 State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
1442 ((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
1443
1444 KdbpPrint(" %s0x%08x %-10s %s%s\n",
1445 str1,
1446 Process->UniqueProcessId,
1447 State,
1448 Process->ImageFileName,
1449 str2);
1450
1451 Entry = Entry->Flink;
1452 }
1453 while(Entry != &PsActiveProcessHead);
1454 }
1455 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1456 {
1457 if (Argc < 3)
1458 {
1459 KdbpPrint("process attach: process id argument required!\n");
1460 return TRUE;
1461 }
1462
1463 ul = strtoul(Argv[2], &pend, 0);
1464 if (Argv[2] == pend)
1465 {
1466 KdbpPrint("process attach: '%s' is not a valid process id!\n", Argv[2]);
1467 return TRUE;
1468 }
1469
1470 if (!KdbpAttachToProcess((PVOID)ul))
1471 {
1472 return TRUE;
1473 }
1474
1475 KdbpPrint("Attached to process 0x%x, thread 0x%x.\n", ul,
1476 (ULONG_PTR)KdbCurrentThread->Cid.UniqueThread);
1477 }
1478 else
1479 {
1480 Process = KdbCurrentProcess;
1481
1482 if (Argc >= 2)
1483 {
1484 ul = strtoul(Argv[1], &pend, 0);
1485 if (Argv[1] == pend)
1486 {
1487 KdbpPrint("proc: '%s' is not a valid process id!\n", Argv[1]);
1488 return TRUE;
1489 }
1490
1491 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1492 {
1493 KdbpPrint("proc: Invalid process id!\n");
1494 return TRUE;
1495 }
1496
1497 /* Remember our reference */
1498 ReferencedProcess = TRUE;
1499 }
1500
1501 State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
1502 ((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
1503 KdbpPrint("%s"
1504 " PID: 0x%08x\n"
1505 " State: %s (0x%x)\n"
1506 " Image Filename: %s\n",
1507 (Argc < 2) ? "Current process:\n" : "",
1508 Process->UniqueProcessId,
1509 State, Process->Pcb.State,
1510 Process->ImageFileName);
1511
1512 /* Release our reference, if any */
1513 if (ReferencedProcess)
1514 ObDereferenceObject(Process);
1515 }
1516
1517 return TRUE;
1518 }
1519
1520 /*!\brief Lists loaded modules or the one containing the specified address.
1521 */
1522 static BOOLEAN
1523 KdbpCmdMod(
1524 ULONG Argc,
1525 PCHAR Argv[])
1526 {
1527 ULONGLONG Result = 0;
1528 ULONG_PTR Address;
1529 PLDR_DATA_TABLE_ENTRY LdrEntry;
1530 BOOLEAN DisplayOnlyOneModule = FALSE;
1531 INT i = 0;
1532
1533 if (Argc >= 2)
1534 {
1535 /* Put the arguments back together */
1536 Argc--;
1537 while (--Argc >= 1)
1538 Argv[Argc][strlen(Argv[Argc])] = ' ';
1539
1540 /* Evaluate the expression */
1541 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
1542 {
1543 return TRUE;
1544 }
1545
1546 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1547 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0],Result);
1548
1549 Address = (ULONG_PTR)Result;
1550
1551 if (!KdbpSymFindModule((PVOID)Address, NULL, -1, &LdrEntry))
1552 {
1553 KdbpPrint("No module containing address 0x%p found!\n", Address);
1554 return TRUE;
1555 }
1556
1557 DisplayOnlyOneModule = TRUE;
1558 }
1559 else
1560 {
1561 if (!KdbpSymFindModule(NULL, NULL, 0, &LdrEntry))
1562 {
1563 ULONG_PTR ntoskrnlBase = ((ULONG_PTR)KdbpCmdMod) & 0xfff00000;
1564 KdbpPrint(" Base Size Name\n");
1565 KdbpPrint(" %p %x %s\n", ntoskrnlBase, 0, "ntoskrnl.exe");
1566 return TRUE;
1567 }
1568
1569 i = 1;
1570 }
1571
1572 KdbpPrint(" Base Size Name\n");
1573 for (;;)
1574 {
1575 KdbpPrint(" %p %x %wZ\n", LdrEntry->DllBase, LdrEntry->SizeOfImage, &LdrEntry->BaseDllName);
1576
1577 if(DisplayOnlyOneModule || !KdbpSymFindModule(NULL, NULL, i++, &LdrEntry))
1578 break;
1579 }
1580
1581 return TRUE;
1582 }
1583
1584 /*!\brief Displays GDT, LDT or IDTd.
1585 */
1586 #ifdef _M_AMD64
1587 static BOOLEAN
1588 KdbpCmdGdtLdtIdt(
1589 ULONG Argc,
1590 PCHAR Argv[])
1591 {
1592 KDESCRIPTOR Reg;
1593 KIDTENTRY IdtEntry;
1594 KGDTENTRY GdtEntry;
1595 ULONG_PTR SegBase;
1596 ULONG SegLimit;
1597 PCHAR SegType;
1598 USHORT SegSel;
1599 UCHAR Type, Dpl;
1600 INT i = 0;
1601 ULONG ul;
1602
1603 if (Argv[0][0] == 'i')
1604 {
1605 /* Read IDTR */
1606 __sidt(&Reg.Limit);
1607
1608 if (Reg.Limit < 7)
1609 {
1610 KdbpPrint("Interrupt descriptor table is empty.\n");
1611 return TRUE;
1612 }
1613
1614 KdbpPrint("IDT Base: 0x%p Limit: 0x%04x\n", Reg.Base, Reg.Limit);
1615 KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n");
1616
1617 while (i < (Reg.Limit)/sizeof(IdtEntry))
1618 {
1619 if (!NT_SUCCESS(KdbpSafeReadMemory(&IdtEntry, (PVOID)((ULONG_PTR)Reg.Base +(i *sizeof(IdtEntry))), sizeof(IdtEntry))))
1620 {
1621 KdbpPrint("Couldn't access memory at 0x%x!\n", (ULONG_PTR)Reg.Base + sizeof(IdtEntry));
1622 return TRUE;
1623 }
1624
1625 Dpl = IdtEntry.Dpl;
1626 if (IdtEntry.Type == 0x5) /* Task gate */
1627 SegType = "TASKGATE";
1628 else if (IdtEntry.Type == 0xE) /* 32 bit Interrupt gate */
1629 SegType = "INTGATE32";
1630 else if (IdtEntry.Type == 0x6) /* 16 bit Interrupt gate */
1631 SegType = "INTGATE16";
1632 else if (IdtEntry.Type == 0xF) /* 32 bit Trap gate */
1633 SegType = "TRAPGATE32";
1634 else if (IdtEntry.Type == 0x7) /* 16 bit Trap gate */
1635 SegType = "TRAPGATE16";
1636 else
1637 SegType = "UNKNOWN";
1638
1639 if (IdtEntry.Present == 0) /* not present */
1640 {
1641 KdbpPrint(" %03d %-10s [NP] [NP] %02d\n",
1642 i, SegType, Dpl);
1643 }
1644 else if (IdtEntry.Type == 0x5) /* Task gate */
1645 {
1646 SegSel = IdtEntry.Selector;
1647 KdbpPrint(" %03d %-10s 0x%04x %02d\n",
1648 i, SegType, SegSel, Dpl);
1649 }
1650 else
1651 {
1652 SegSel = IdtEntry.Selector;
1653 SegBase = (ULONG64)IdtEntry.OffsetLow |
1654 (ULONG64)IdtEntry.OffsetMiddle << 16 |
1655 (ULONG64)IdtEntry.OffsetHigh << 32;
1656
1657 KdbpPrint(" %03d %-10s 0x%04x 0x%p %02d\n",
1658 i , SegType, SegSel, SegBase, Dpl);
1659 }
1660 i++;
1661 }
1662 }
1663 else
1664 {
1665 ul = 0;
1666
1667 if (Argv[0][0] == 'g')
1668 {
1669 /* Read GDTR */
1670 __sgdt(&Reg.Limit);
1671
1672 }
1673 else
1674 {
1675 ASSERT(Argv[0][0] == 'l');
1676
1677 /* Read LDTR */
1678 __sldt(&Reg.Limit);
1679
1680 Reg.Base = 0;
1681 ul = 1 << 2;
1682 }
1683
1684 if (Reg.Limit < 7)
1685 {
1686 KdbpPrint("%s descriptor table is empty.\n",
1687 Argv[0][0] == 'g' ? "Global" : "Local");
1688 return TRUE;
1689 }
1690
1691 KdbpPrint("%cDT Base: 0x%p Limit: 0x%04x\n",
1692 Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit);
1693 KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n");
1694
1695 while (i < (Reg.Limit)/sizeof(GdtEntry))
1696 {
1697 if (!NT_SUCCESS(KdbpSafeReadMemory(&GdtEntry, (PVOID)((ULONG_PTR)Reg.Base +(i * 8)), sizeof(GdtEntry))))
1698 {
1699 KdbpPrint("Couldn't access memory at 0x%p!\n", (ULONG_PTR)Reg.Base + i);
1700 return TRUE;
1701 }
1702
1703 Dpl = GdtEntry.Bits.Dpl;
1704 Type = GdtEntry.Bits.Type;
1705
1706 SegBase = (ULONG_PTR)KiGetGdtDescriptorBase(&GdtEntry);
1707
1708 SegLimit = GdtEntry.LimitLow;
1709 SegLimit |= (ULONG64)GdtEntry.Bits.LimitHigh << 32;
1710
1711 if (GdtEntry.Bits.DefaultBig != 0)
1712 {
1713 SegLimit *= 4096;
1714 SegLimit += 4095;
1715 }
1716 else
1717 {
1718 SegLimit++;
1719 }
1720
1721 if (GdtEntry.Bits.System == 1) /* System segment */
1722 {
1723 switch (Type)
1724 {
1725 case 1: SegType = "TSS16(Avl)"; break;
1726 case 2: SegType = "LDT"; break;
1727 case 3: SegType = "TSS16(Busy)"; break;
1728 case 4: SegType = "CALLGATE16"; break;
1729 case 5: SegType = "TASKGATE"; break;
1730 case 6: SegType = "INTGATE16"; break;
1731 case 7: SegType = "TRAPGATE16"; break;
1732 case 9: SegType = "TSS32(Avl)"; break;
1733 case 11: SegType = "TSS32(Busy)"; break;
1734 case 12: SegType = "CALLGATE32"; break;
1735 case 14: SegType = "INTGATE32"; break;
1736 case 15: SegType = "INTGATE32"; break;
1737 default: SegType = "UNKNOWN"; break;
1738 }
1739
1740 if (!(Type >= 1 && Type <= 3) &&
1741 Type != 9 && Type != 11)
1742 {
1743 SegBase = 0;
1744 SegLimit = 0;
1745 }
1746 }
1747 else if ((Type & (1 << 3)) == 0) /* Data segment */
1748 {
1749 if (GdtEntry.Bits.LongMode != 0)
1750 SegType = "DATA32";
1751 else
1752 SegType = "DATA16";
1753 }
1754 else /* Code segment */
1755 {
1756 if (GdtEntry.Bits.LongMode != 0)
1757 SegType = "CODE32";
1758 else
1759 SegType = "CODE16";
1760 }
1761
1762 if (GdtEntry.Bits.Present == 0) /* not present */
1763 {
1764 KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n",
1765 i, i | Dpl | ul, SegType, Dpl);
1766 }
1767 else
1768 {
1769 KdbpPrint(" %03d 0x%04x %-11s 0x%p 0x%08x %02d ",
1770 i, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl);
1771
1772 if (GdtEntry.Bits.System == 1) /* System segment */
1773 {
1774 /* FIXME: Display system segment */
1775 /* they are twice as big as regular segments */
1776 i++;
1777 }
1778 else if ((Type & (1 << 3)) == 0) /* Data segment */
1779 {
1780 if ((Type & (1 << 2)) != 0) /* Expand-down */
1781 KdbpPrint(" E");
1782
1783 KdbpPrint((Type & (1 << 1)) ? " R/W" : " R");
1784
1785 if ((Type & (1 >> 1)) != 0)
1786 KdbpPrint(" A");
1787 }
1788 else /* Code segment */
1789 {
1790 if ((Type & (1 << 2)) != 0) /* Conforming */
1791 KdbpPrint(" C");
1792
1793 KdbpPrint((Type & (1 << 1)) ? " R/X" : " X");
1794
1795 if ((Type & (1 << 1)) != 0)
1796 KdbpPrint(" A");
1797 }
1798
1799 if ((GdtEntry.Bits.LimitHigh & (1 << 3)) != 0)
1800 KdbpPrint(" AVL");
1801
1802 KdbpPrint("\n");
1803 }
1804 i++;
1805 }
1806 }
1807
1808 return TRUE;
1809 }
1810 #endif
1811
1812 /*!\brief Displays GDT, LDT or IDTd.
1813 */
1814 #ifdef _M_IX86
1815 static BOOLEAN
1816 KdbpCmdGdtLdtIdt(
1817 ULONG Argc,
1818 PCHAR Argv[])
1819 {
1820 KDESCRIPTOR Reg;
1821 ULONG SegDesc[2];
1822 ULONG SegBase;
1823 ULONG SegLimit;
1824 PCHAR SegType;
1825 USHORT SegSel;
1826 UCHAR Type, Dpl;
1827 INT i;
1828 ULONG ul;
1829
1830 if (Argv[0][0] == 'i')
1831 {
1832 /* Read IDTR */
1833 __sidt(&Reg.Limit);
1834
1835 if (Reg.Limit < 7)
1836 {
1837 KdbpPrint("Interrupt descriptor table is empty.\n");
1838 return TRUE;
1839 }
1840
1841 KdbpPrint("IDT Base: 0x%08x Limit: 0x%04x\n", Reg.Base, Reg.Limit);
1842 KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n");
1843
1844 for (i = 0; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
1845 {
1846 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)((ULONG_PTR)Reg.Base + i), sizeof(SegDesc))))
1847 {
1848 KdbpPrint("Couldn't access memory at 0x%08x!\n", (ULONG_PTR)Reg.Base + i);
1849 return TRUE;
1850 }
1851
1852 Dpl = ((SegDesc[1] >> 13) & 3);
1853 if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
1854 SegType = "TASKGATE";
1855 else if ((SegDesc[1] & 0x1fe0) == 0x0e00) /* 32 bit Interrupt gate */
1856 SegType = "INTGATE32";
1857 else if ((SegDesc[1] & 0x1fe0) == 0x0600) /* 16 bit Interrupt gate */
1858 SegType = "INTGATE16";
1859 else if ((SegDesc[1] & 0x1fe0) == 0x0f00) /* 32 bit Trap gate */
1860 SegType = "TRAPGATE32";
1861 else if ((SegDesc[1] & 0x1fe0) == 0x0700) /* 16 bit Trap gate */
1862 SegType = "TRAPGATE16";
1863 else
1864 SegType = "UNKNOWN";
1865
1866 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
1867 {
1868 KdbpPrint(" %03d %-10s [NP] [NP] %02d\n",
1869 i / 8, SegType, Dpl);
1870 }
1871 else if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
1872 {
1873 SegSel = SegDesc[0] >> 16;
1874 KdbpPrint(" %03d %-10s 0x%04x %02d\n",
1875 i / 8, SegType, SegSel, Dpl);
1876 }
1877 else
1878 {
1879 SegSel = SegDesc[0] >> 16;
1880 SegBase = (SegDesc[1] & 0xffff0000) | (SegDesc[0] & 0x0000ffff);
1881 KdbpPrint(" %03d %-10s 0x%04x 0x%08x %02d\n",
1882 i / 8, SegType, SegSel, SegBase, Dpl);
1883 }
1884 }
1885 }
1886 else
1887 {
1888 ul = 0;
1889
1890 if (Argv[0][0] == 'g')
1891 {
1892 /* Read GDTR */
1893 Ke386GetGlobalDescriptorTable(&Reg.Limit);
1894 i = 8;
1895 }
1896 else
1897 {
1898 ASSERT(Argv[0][0] == 'l');
1899
1900 /* Read LDTR */
1901 Reg.Limit = Ke386GetLocalDescriptorTable();
1902 Reg.Base = 0;
1903 i = 0;
1904 ul = 1 << 2;
1905 }
1906
1907 if (Reg.Limit < 7)
1908 {
1909 KdbpPrint("%s descriptor table is empty.\n",
1910 Argv[0][0] == 'g' ? "Global" : "Local");
1911 return TRUE;
1912 }
1913
1914 KdbpPrint("%cDT Base: 0x%08x Limit: 0x%04x\n",
1915 Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit);
1916 KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n");
1917
1918 for (; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
1919 {
1920 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)((ULONG_PTR)Reg.Base + i), sizeof(SegDesc))))
1921 {
1922 KdbpPrint("Couldn't access memory at 0x%08x!\n", (ULONG_PTR)Reg.Base + i);
1923 return TRUE;
1924 }
1925
1926 Dpl = ((SegDesc[1] >> 13) & 3);
1927 Type = ((SegDesc[1] >> 8) & 0xf);
1928
1929 SegBase = SegDesc[0] >> 16;
1930 SegBase |= (SegDesc[1] & 0xff) << 16;
1931 SegBase |= SegDesc[1] & 0xff000000;
1932 SegLimit = SegDesc[0] & 0x0000ffff;
1933 SegLimit |= (SegDesc[1] >> 16) & 0xf;
1934
1935 if ((SegDesc[1] & (1 << 23)) != 0)
1936 {
1937 SegLimit *= 4096;
1938 SegLimit += 4095;
1939 }
1940 else
1941 {
1942 SegLimit++;
1943 }
1944
1945 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
1946 {
1947 switch (Type)
1948 {
1949 case 1: SegType = "TSS16(Avl)"; break;
1950 case 2: SegType = "LDT"; break;
1951 case 3: SegType = "TSS16(Busy)"; break;
1952 case 4: SegType = "CALLGATE16"; break;
1953 case 5: SegType = "TASKGATE"; break;
1954 case 6: SegType = "INTGATE16"; break;
1955 case 7: SegType = "TRAPGATE16"; break;
1956 case 9: SegType = "TSS32(Avl)"; break;
1957 case 11: SegType = "TSS32(Busy)"; break;
1958 case 12: SegType = "CALLGATE32"; break;
1959 case 14: SegType = "INTGATE32"; break;
1960 case 15: SegType = "INTGATE32"; break;
1961 default: SegType = "UNKNOWN"; break;
1962 }
1963
1964 if (!(Type >= 1 && Type <= 3) &&
1965 Type != 9 && Type != 11)
1966 {
1967 SegBase = 0;
1968 SegLimit = 0;
1969 }
1970 }
1971 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
1972 {
1973 if ((SegDesc[1] & (1 << 22)) != 0)
1974 SegType = "DATA32";
1975 else
1976 SegType = "DATA16";
1977 }
1978 else /* Code segment */
1979 {
1980 if ((SegDesc[1] & (1 << 22)) != 0)
1981 SegType = "CODE32";
1982 else
1983 SegType = "CODE16";
1984 }
1985
1986 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
1987 {
1988 KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n",
1989 i / 8, i | Dpl | ul, SegType, Dpl);
1990 }
1991 else
1992 {
1993 KdbpPrint(" %03d 0x%04x %-11s 0x%08x 0x%08x %02d ",
1994 i / 8, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl);
1995
1996 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
1997 {
1998 /* FIXME: Display system segment */
1999 }
2000 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
2001 {
2002 if ((SegDesc[1] & (1 << 10)) != 0) /* Expand-down */
2003 KdbpPrint(" E");
2004
2005 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/W" : " R");
2006
2007 if ((SegDesc[1] & (1 << 8)) != 0)
2008 KdbpPrint(" A");
2009 }
2010 else /* Code segment */
2011 {
2012 if ((SegDesc[1] & (1 << 10)) != 0) /* Conforming */
2013 KdbpPrint(" C");
2014
2015 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/X" : " X");
2016
2017 if ((SegDesc[1] & (1 << 8)) != 0)
2018 KdbpPrint(" A");
2019 }
2020
2021 if ((SegDesc[1] & (1 << 20)) != 0)
2022 KdbpPrint(" AVL");
2023
2024 KdbpPrint("\n");
2025 }
2026 }
2027 }
2028
2029 return TRUE;
2030 }
2031 #endif
2032
2033 /*!\brief Displays the KPCR
2034 */
2035 static BOOLEAN
2036 KdbpCmdPcr(
2037 ULONG Argc,
2038 PCHAR Argv[])
2039 {
2040 PKIPCR Pcr = (PKIPCR)KeGetPcr();
2041
2042 #ifdef _M_IX86
2043 KdbpPrint("Current PCR is at 0x%08x.\n", (INT)Pcr);
2044 KdbpPrint(" Tib.ExceptionList: 0x%08x\n"
2045 " Tib.StackBase: 0x%08x\n"
2046 " Tib.StackLimit: 0x%08x\n"
2047 " Tib.SubSystemTib: 0x%08x\n"
2048 " Tib.FiberData/Version: 0x%08x\n"
2049 " Tib.ArbitraryUserPointer: 0x%08x\n"
2050 " Tib.Self: 0x%08x\n"
2051 " Self: 0x%08x\n"
2052 " PCRCB: 0x%08x\n"
2053 " Irql: 0x%02x\n"
2054 " IRR: 0x%08x\n"
2055 " IrrActive: 0x%08x\n"
2056 " IDR: 0x%08x\n"
2057 " KdVersionBlock: 0x%08x\n"
2058 " IDT: 0x%08x\n"
2059 " GDT: 0x%08x\n"
2060 " TSS: 0x%08x\n"
2061 " MajorVersion: 0x%04x\n"
2062 " MinorVersion: 0x%04x\n"
2063 " SetMember: 0x%08x\n"
2064 " StallScaleFactor: 0x%08x\n"
2065 " Number: 0x%02x\n"
2066 " L2CacheAssociativity: 0x%02x\n"
2067 " VdmAlert: 0x%08x\n"
2068 " L2CacheSize: 0x%08x\n"
2069 " InterruptMode: 0x%08x\n",
2070 Pcr->NtTib.ExceptionList, Pcr->NtTib.StackBase, Pcr->NtTib.StackLimit,
2071 Pcr->NtTib.SubSystemTib, Pcr->NtTib.FiberData, Pcr->NtTib.ArbitraryUserPointer,
2072 Pcr->NtTib.Self, Pcr->Self, Pcr->Prcb, Pcr->Irql, Pcr->IRR, Pcr->IrrActive,
2073 Pcr->IDR, Pcr->KdVersionBlock, Pcr->IDT, Pcr->GDT, Pcr->TSS,
2074 Pcr->MajorVersion, Pcr->MinorVersion, Pcr->SetMember, Pcr->StallScaleFactor,
2075 Pcr->Number, Pcr->SecondLevelCacheAssociativity,
2076 Pcr->VdmAlert, Pcr->SecondLevelCacheSize, Pcr->InterruptMode);
2077 #elif defined(_M_AMD64)
2078 KdbpPrint("Current PCR is at 0x%x.\n", (INT_PTR)Pcr);
2079 KdbpPrint(" Tib.ExceptionList: 0x%p\n"
2080 " Tib.StackBase: 0x%p\n"
2081 " Tib.StackLimit: 0x%x\n"
2082 " Tib.SubSystemTib: 0x%p\n"
2083 " Tib.FiberData/Version: 0x%x\n"
2084 " Tib.ArbitraryUserPointer: 0x%p\n"
2085 " Tib.Self: 0x%p\n"
2086 " Self: 0x%p\n"
2087 " PCRCB: 0x%p\n"
2088 " Irql: 0x%x\n"
2089 " KdVersionBlock: 0x%08x\n"
2090 " IDT: 0x%p\n"
2091 " GDT: 0x%p\n"
2092 " TSS: 0x%p\n"
2093 " UserRsp: 0x%p\n"
2094 " MajorVersion: 0x%04x\n"
2095 " MinorVersion: 0x%04x\n"
2096 " StallScaleFactor: 0x%08x\n"
2097 " L2CacheAssociativity: 0x%02x\n"
2098 " L2CacheSize: 0x%08x\n",
2099 Pcr->NtTib.ExceptionList, Pcr->NtTib.StackBase, Pcr->NtTib.StackLimit,
2100 Pcr->NtTib.SubSystemTib, Pcr->NtTib.FiberData, Pcr->NtTib.ArbitraryUserPointer,
2101 Pcr->NtTib.Self, Pcr->Self, Pcr->Prcb, Pcr->Irql,
2102 Pcr->KdVersionBlock, Pcr->IdtBase, Pcr->GdtBase, Pcr->TssBase,Pcr->UserRsp,
2103 Pcr->MajorVersion, Pcr->MinorVersion, Pcr->StallScaleFactor,
2104 Pcr->SecondLevelCacheAssociativity, Pcr->SecondLevelCacheSize);
2105 #endif
2106 return TRUE;
2107 }
2108
2109 /*!\brief Displays the TSS
2110 */
2111 static BOOLEAN
2112 KdbpCmdTss(
2113 ULONG Argc,
2114 PCHAR Argv[])
2115 {
2116 #ifdef _M_IX86
2117 KTSS *Tss = KeGetPcr()->TSS;
2118
2119 KdbpPrint("Current TSS is at 0x%p.\n", (INT_PTR)Tss);
2120 KdbpPrint(" Eip: 0x%p\n"
2121 " Es: 0x%04x\n"
2122 " Cs: 0x%04x\n"
2123 " Ss: 0x%04x\n"
2124 " Ds: 0x%04x\n"
2125 " Fs: 0x%04x\n"
2126 " Gs: 0x%04x\n"
2127 " IoMapBase: 0x%04x\n",
2128 Tss->Eip, Tss->Es, Tss->Cs, Tss->Ds, Tss->Fs, Tss->Gs, Tss->IoMapBase);
2129 #elif defined(_M_AMD64)
2130 KTSS *Tss = KeGetPcr()->TssBase;
2131
2132 KdbpPrint("Current TSS is at 0x%p.\n", (INT_PTR)Tss);
2133 KdbpPrint(" Rsp0: 0x%p\n"
2134 " Rsp1: 0x%p\n"
2135 " Rsp2: 0x%p\n"
2136 " Ist: 0x%p\n"
2137 " IoMapBase: 0x%04x\n",
2138 Tss->Rsp0, Tss->Rsp1, Tss->Rsp2, Tss->Ist, Tss->IoMapBase);
2139 #endif
2140 return TRUE;
2141 }
2142
2143 /*!\brief Bugchecks the system.
2144 */
2145 static BOOLEAN
2146 KdbpCmdBugCheck(
2147 ULONG Argc,
2148 PCHAR Argv[])
2149 {
2150 /* Set the flag and quit looping */
2151 KdbpBugCheckRequested = TRUE;
2152
2153 return FALSE;
2154 }
2155
2156 /*!\brief Sets or displays a config variables value.
2157 */
2158 static BOOLEAN
2159 KdbpCmdSet(
2160 ULONG Argc,
2161 PCHAR Argv[])
2162 {
2163 LONG l;
2164 BOOLEAN First;
2165 PCHAR pend = 0;
2166 KDB_ENTER_CONDITION ConditionFirst = KdbDoNotEnter;
2167 KDB_ENTER_CONDITION ConditionLast = KdbDoNotEnter;
2168
2169 static const PCHAR ExceptionNames[21] =
2170 {
2171 "ZERODEVIDE", "DEBUGTRAP", "NMI", "INT3", "OVERFLOW", "BOUND", "INVALIDOP",
2172 "NOMATHCOP", "DOUBLEFAULT", "RESERVED(9)", "INVALIDTSS", "SEGMENTNOTPRESENT",
2173 "STACKFAULT", "GPF", "PAGEFAULT", "RESERVED(15)", "MATHFAULT", "ALIGNMENTCHECK",
2174 "MACHINECHECK", "SIMDFAULT", "OTHERS"
2175 };
2176
2177 if (Argc == 1)
2178 {
2179 KdbpPrint("Available settings:\n");
2180 KdbpPrint(" syntax [intel|at&t]\n");
2181 KdbpPrint(" condition [exception|*] [first|last] [never|always|kmode|umode]\n");
2182 KdbpPrint(" break_on_module_load [true|false]\n");
2183 }
2184 else if (strcmp(Argv[1], "syntax") == 0)
2185 {
2186 if (Argc == 2)
2187 {
2188 KdbpPrint("syntax = %s\n", KdbUseIntelSyntax ? "intel" : "at&t");
2189 }
2190 else if (Argc >= 3)
2191 {
2192 if (_stricmp(Argv[2], "intel") == 0)
2193 KdbUseIntelSyntax = TRUE;
2194 else if (_stricmp(Argv[2], "at&t") == 0)
2195 KdbUseIntelSyntax = FALSE;
2196 else
2197 KdbpPrint("Unknown syntax '%s'.\n", Argv[2]);
2198 }
2199 }
2200 else if (strcmp(Argv[1], "condition") == 0)
2201 {
2202 if (Argc == 2)
2203 {
2204 KdbpPrint("Conditions: (First) (Last)\n");
2205 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames) - 1; l++)
2206 {
2207 if (!ExceptionNames[l])
2208 continue;
2209
2210 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
2211 ASSERT(0);
2212
2213 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
2214 ASSERT(0);
2215
2216 KdbpPrint(" #%02d %-20s %-8s %-8s\n", l, ExceptionNames[l],
2217 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2218 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2219 }
2220
2221 ASSERT(l == (RTL_NUMBER_OF(ExceptionNames) - 1));
2222 KdbpPrint(" %-20s %-8s %-8s\n", ExceptionNames[l],
2223 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2224 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2225 }
2226 else
2227 {
2228 if (Argc >= 5 && strcmp(Argv[2], "*") == 0) /* Allow * only when setting condition */
2229 {
2230 l = -1;
2231 }
2232 else
2233 {
2234 l = strtoul(Argv[2], &pend, 0);
2235
2236 if (Argv[2] == pend)
2237 {
2238 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++)
2239 {
2240 if (!ExceptionNames[l])
2241 continue;
2242
2243 if (_stricmp(ExceptionNames[l], Argv[2]) == 0)
2244 break;
2245 }
2246 }
2247
2248 if (l >= RTL_NUMBER_OF(ExceptionNames))
2249 {
2250 KdbpPrint("Unknown exception '%s'.\n", Argv[2]);
2251 return TRUE;
2252 }
2253 }
2254
2255 if (Argc > 4)
2256 {
2257 if (_stricmp(Argv[3], "first") == 0)
2258 First = TRUE;
2259 else if (_stricmp(Argv[3], "last") == 0)
2260 First = FALSE;
2261 else
2262 {
2263 KdbpPrint("set condition: second argument must be 'first' or 'last'\n");
2264 return TRUE;
2265 }
2266
2267 if (_stricmp(Argv[4], "never") == 0)
2268 ConditionFirst = KdbDoNotEnter;
2269 else if (_stricmp(Argv[4], "always") == 0)
2270 ConditionFirst = KdbEnterAlways;
2271 else if (_stricmp(Argv[4], "umode") == 0)
2272 ConditionFirst = KdbEnterFromUmode;
2273 else if (_stricmp(Argv[4], "kmode") == 0)
2274 ConditionFirst = KdbEnterFromKmode;
2275 else
2276 {
2277 KdbpPrint("set condition: third argument must be 'never', 'always', 'umode' or 'kmode'\n");
2278 return TRUE;
2279 }
2280
2281 if (!KdbpSetEnterCondition(l, First, ConditionFirst))
2282 {
2283 if (l >= 0)
2284 KdbpPrint("Couldn't change condition for exception #%02d\n", l);
2285 else
2286 KdbpPrint("Couldn't change condition for all exceptions\n", l);
2287 }
2288 }
2289 else /* Argc >= 3 */
2290 {
2291 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
2292 ASSERT(0);
2293
2294 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
2295 ASSERT(0);
2296
2297 if (l < (RTL_NUMBER_OF(ExceptionNames) - 1))
2298 {
2299 KdbpPrint("Condition for exception #%02d (%s): FirstChance %s LastChance %s\n",
2300 l, ExceptionNames[l],
2301 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2302 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2303 }
2304 else
2305 {
2306 KdbpPrint("Condition for all other exceptions: FirstChance %s LastChance %s\n",
2307 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
2308 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
2309 }
2310 }
2311 }
2312 }
2313 else if (strcmp(Argv[1], "break_on_module_load") == 0)
2314 {
2315 if (Argc == 2)
2316 KdbpPrint("break_on_module_load = %s\n", KdbBreakOnModuleLoad ? "enabled" : "disabled");
2317 else if (Argc >= 3)
2318 {
2319 if (_stricmp(Argv[2], "enable") == 0 || _stricmp(Argv[2], "enabled") == 0 || _stricmp(Argv[2], "true") == 0)
2320 KdbBreakOnModuleLoad = TRUE;
2321 else if (_stricmp(Argv[2], "disable") == 0 || _stricmp(Argv[2], "disabled") == 0 || _stricmp(Argv[2], "false") == 0)
2322 KdbBreakOnModuleLoad = FALSE;
2323 else
2324 KdbpPrint("Unknown setting '%s'.\n", Argv[2]);
2325 }
2326 }
2327 else
2328 {
2329 KdbpPrint("Unknown setting '%s'.\n", Argv[1]);
2330 }
2331
2332 return TRUE;
2333 }
2334
2335 /*!\brief Displays help screen.
2336 */
2337 static BOOLEAN
2338 KdbpCmdHelp(
2339 ULONG Argc,
2340 PCHAR Argv[])
2341 {
2342 ULONG i;
2343
2344 KdbpPrint("Kernel debugger commands:\n");
2345 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
2346 {
2347 if (!KdbDebuggerCommands[i].Syntax) /* Command group */
2348 {
2349 if (i > 0)
2350 KdbpPrint("\n");
2351
2352 KdbpPrint("\x1b[7m* %s:\x1b[0m\n", KdbDebuggerCommands[i].Help);
2353 continue;
2354 }
2355
2356 KdbpPrint(" %-20s - %s\n",
2357 KdbDebuggerCommands[i].Syntax,
2358 KdbDebuggerCommands[i].Help);
2359 }
2360
2361 return TRUE;
2362 }
2363
2364 /*!\brief Prints the given string with printf-like formatting.
2365 *
2366 * \param Format Format of the string/arguments.
2367 * \param ... Variable number of arguments matching the format specified in \a Format.
2368 *
2369 * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
2370 * number of lines required to print a single line from the Buffer in the terminal.
2371 */
2372 VOID
2373 KdbpPrint(
2374 IN PCHAR Format,
2375 IN ... OPTIONAL)
2376 {
2377 static CHAR Buffer[4096];
2378 static BOOLEAN TerminalInitialized = FALSE;
2379 static BOOLEAN TerminalConnected = FALSE;
2380 static BOOLEAN TerminalReportsSize = TRUE;
2381 CHAR c = '\0';
2382 PCHAR p, p2;
2383 ULONG Length;
2384 ULONG i, j;
2385 LONG RowsPrintedByTerminal;
2386 ULONG ScanCode;
2387 va_list ap;
2388
2389 /* Check if the user has aborted output of the current command */
2390 if (KdbOutputAborted)
2391 return;
2392
2393 /* Initialize the terminal */
2394 if (!TerminalInitialized)
2395 {
2396 DbgPrint("\x1b[7h"); /* Enable linewrap */
2397
2398 /* Query terminal type */
2399 /*DbgPrint("\x1b[Z");*/
2400 DbgPrint("\x05");
2401
2402 TerminalInitialized = TRUE;
2403 Length = 0;
2404 KeStallExecutionProcessor(100000);
2405
2406 for (;;)
2407 {
2408 c = KdbpTryGetCharSerial(5000);
2409 if (c == -1)
2410 break;
2411
2412 Buffer[Length++] = c;
2413 if (Length >= (sizeof (Buffer) - 1))
2414 break;
2415 }
2416
2417 Buffer[Length] = '\0';
2418 if (Length > 0)
2419 TerminalConnected = TRUE;
2420 }
2421
2422 /* Get number of rows and columns in terminal */
2423 if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
2424 (KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
2425 {
2426 if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
2427 {
2428 /* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
2429 TerminalReportsSize = FALSE;
2430 KeStallExecutionProcessor(100000);
2431 DbgPrint("\x1b[18t");
2432 c = KdbpTryGetCharSerial(5000);
2433
2434 if (c == KEY_ESC)
2435 {
2436 c = KdbpTryGetCharSerial(5000);
2437 if (c == '[')
2438 {
2439 Length = 0;
2440
2441 for (;;)
2442 {
2443 c = KdbpTryGetCharSerial(5000);
2444 if (c == -1)
2445 break;
2446
2447 Buffer[Length++] = c;
2448 if (isalpha(c) || Length >= (sizeof (Buffer) - 1))
2449 break;
2450 }
2451
2452 Buffer[Length] = '\0';
2453 if (Buffer[0] == '8' && Buffer[1] == ';')
2454 {
2455 for (i = 2; (i < Length) && (Buffer[i] != ';'); i++);
2456
2457 if (Buffer[i] == ';')
2458 {
2459 Buffer[i++] = '\0';
2460
2461 /* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
2462 KdbNumberOfRowsTerminal = strtoul(Buffer + 2, NULL, 0);
2463 KdbNumberOfColsTerminal = strtoul(Buffer + i, NULL, 0);
2464 TerminalReportsSize = TRUE;
2465 }
2466 }
2467 }
2468 /* Clear further characters */
2469 while ((c = KdbpTryGetCharSerial(5000)) != -1);
2470 }
2471 }
2472
2473 if (KdbNumberOfRowsTerminal <= 0)
2474 {
2475 /* Set number of rows to the default. */
2476 KdbNumberOfRowsTerminal = 24;
2477 }
2478 else if (KdbNumberOfColsTerminal <= 0)
2479 {
2480 /* Set number of cols to the default. */
2481 KdbNumberOfColsTerminal = 80;
2482 }
2483 }
2484
2485 /* Get the string */
2486 va_start(ap, Format);
2487 Length = _vsnprintf(Buffer, sizeof (Buffer) - 1, Format, ap);
2488 Buffer[Length] = '\0';
2489 va_end(ap);
2490
2491 p = Buffer;
2492 while (p[0] != '\0')
2493 {
2494 i = strcspn(p, "\n");
2495
2496 /* Calculate the number of lines which will be printed in the terminal
2497 * when outputting the current line
2498 */
2499 if (i > 0)
2500 RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
2501 else
2502 RowsPrintedByTerminal = 0;
2503
2504 if (p[i] == '\n')
2505 RowsPrintedByTerminal++;
2506
2507 /*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
2508
2509 /* Display a prompt if we printed one screen full of text */
2510 if (KdbNumberOfRowsTerminal > 0 &&
2511 (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
2512 {
2513 if (KdbNumberOfColsPrinted > 0)
2514 DbgPrint("\n");
2515
2516 DbgPrint("--- Press q to abort, any other key to continue ---");
2517
2518 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2519 c = KdbpGetCharSerial();
2520 else
2521 c = KdbpGetCharKeyboard(&ScanCode);
2522
2523 if (c == '\r')
2524 {
2525 /* Try to read '\n' which might follow '\r' - if \n is not received here
2526 * it will be interpreted as "return" when the next command should be read.
2527 */
2528 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2529 c = KdbpTryGetCharSerial(5);
2530 else
2531 c = KdbpTryGetCharKeyboard(&ScanCode, 5);
2532 }
2533
2534 DbgPrint("\n");
2535 if (c == 'q')
2536 {
2537 KdbOutputAborted = TRUE;
2538 return;
2539 }
2540
2541 KdbNumberOfRowsPrinted = 0;
2542 KdbNumberOfColsPrinted = 0;
2543 }
2544
2545 /* Insert a NUL after the line and print only the current line. */
2546 if (p[i] == '\n' && p[i + 1] != '\0')
2547 {
2548 c = p[i + 1];
2549 p[i + 1] = '\0';
2550 }
2551 else
2552 {
2553 c = '\0';
2554 }
2555
2556 /* Remove escape sequences from the line if there's no terminal connected */
2557 if (!TerminalConnected)
2558 {
2559 while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
2560 {
2561 if (p2[1] == '[')
2562 {
2563 j = 2;
2564 while (!isalpha(p2[j++]));
2565 strcpy(p2, p2 + j);
2566 }
2567 else
2568 {
2569 strcpy(p2, p2 + 1);
2570 }
2571 }
2572 }
2573
2574 DbgPrint("%s", p);
2575
2576 if (c != '\0')
2577 p[i + 1] = c;
2578
2579 /* Set p to the start of the next line and
2580 * remember the number of rows/cols printed
2581 */
2582 p += i;
2583 if (p[0] == '\n')
2584 {
2585 p++;
2586 KdbNumberOfColsPrinted = 0;
2587 }
2588 else
2589 {
2590 ASSERT(p[0] == '\0');
2591 KdbNumberOfColsPrinted += i;
2592 }
2593
2594 KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
2595 }
2596 }
2597
2598 /*!\brief Appends a command to the command history
2599 *
2600 * \param Command Pointer to the command to append to the history.
2601 */
2602 static VOID
2603 KdbpCommandHistoryAppend(
2604 IN PCHAR Command)
2605 {
2606 ULONG Length1 = strlen(Command) + 1;
2607 ULONG Length2 = 0;
2608 INT i;
2609 PCHAR Buffer;
2610
2611 ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer));
2612
2613 if (Length1 <= 1 ||
2614 (KdbCommandHistory[KdbCommandHistoryIndex] &&
2615 strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0))
2616 {
2617 return;
2618 }
2619
2620 /* Calculate Length1 and Length2 */
2621 Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex;
2622 KdbCommandHistoryBufferIndex += Length1;
2623 if (KdbCommandHistoryBufferIndex >= (LONG)RTL_NUMBER_OF(KdbCommandHistoryBuffer))
2624 {
2625 KdbCommandHistoryBufferIndex -= RTL_NUMBER_OF(KdbCommandHistoryBuffer);
2626 Length2 = KdbCommandHistoryBufferIndex;
2627 Length1 -= Length2;
2628 }
2629
2630 /* Remove previous commands until there is enough space to append the new command */
2631 for (i = KdbCommandHistoryIndex; KdbCommandHistory[i];)
2632 {
2633 if ((Length2 > 0 &&
2634 (KdbCommandHistory[i] >= Buffer ||
2635 KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))) ||
2636 (Length2 <= 0 &&
2637 (KdbCommandHistory[i] >= Buffer &&
2638 KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))))
2639 {
2640 KdbCommandHistory[i] = NULL;
2641 }
2642
2643 i--;
2644 if (i < 0)
2645 i = RTL_NUMBER_OF(KdbCommandHistory) - 1;
2646
2647 if (i == KdbCommandHistoryIndex)
2648 break;
2649 }
2650
2651 /* Make sure the new command history entry is free */
2652 KdbCommandHistoryIndex++;
2653 KdbCommandHistoryIndex %= RTL_NUMBER_OF(KdbCommandHistory);
2654 if (KdbCommandHistory[KdbCommandHistoryIndex])
2655 {
2656 KdbCommandHistory[KdbCommandHistoryIndex] = NULL;
2657 }
2658
2659 /* Append command */
2660 KdbCommandHistory[KdbCommandHistoryIndex] = Buffer;
2661 ASSERT((KdbCommandHistory[KdbCommandHistoryIndex] + Length1) <= KdbCommandHistoryBuffer + RTL_NUMBER_OF(KdbCommandHistoryBuffer));
2662 memcpy(KdbCommandHistory[KdbCommandHistoryIndex], Command, Length1);
2663 if (Length2 > 0)
2664 {
2665 memcpy(KdbCommandHistoryBuffer, Command + Length1, Length2);
2666 }
2667 }
2668
2669 /*!\brief Reads a line of user-input.
2670 *
2671 * \param Buffer Buffer to store the input into. Trailing newlines are removed.
2672 * \param Size Size of \a Buffer.
2673 *
2674 * \note Accepts only \n newlines, \r is ignored.
2675 */
2676 static VOID
2677 KdbpReadCommand(
2678 OUT PCHAR Buffer,
2679 IN ULONG Size)
2680 {
2681 CHAR Key;
2682 PCHAR Orig = Buffer;
2683 ULONG ScanCode = 0;
2684 BOOLEAN EchoOn;
2685 static CHAR LastCommand[1024] = "";
2686 static CHAR NextKey = '\0';
2687 INT CmdHistIndex = -1;
2688 INT i;
2689
2690 EchoOn = !((KdbDebugState & KD_DEBUG_KDNOECHO) != 0);
2691
2692 for (;;)
2693 {
2694 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2695 {
2696 Key = (NextKey == '\0') ? KdbpGetCharSerial() : NextKey;
2697 NextKey = '\0';
2698 ScanCode = 0;
2699 if (Key == KEY_ESC) /* ESC */
2700 {
2701 Key = KdbpGetCharSerial();
2702 if (Key == '[')
2703 {
2704 Key = KdbpGetCharSerial();
2705
2706 switch (Key)
2707 {
2708 case 'A':
2709 ScanCode = KEY_SCAN_UP;
2710 break;
2711 case 'B':
2712 ScanCode = KEY_SCAN_DOWN;
2713 break;
2714 case 'C':
2715 break;
2716 case 'D':
2717 break;
2718 }
2719 }
2720 }
2721 }
2722 else
2723 {
2724 ScanCode = 0;
2725 Key = (NextKey == '\0') ? KdbpGetCharKeyboard(&ScanCode) : NextKey;
2726 NextKey = '\0';
2727 }
2728
2729 if ((ULONG_PTR)(Buffer - Orig) >= (Size - 1))
2730 {
2731 /* Buffer is full, accept only newlines */
2732 if (Key != '\n')
2733 continue;
2734 }
2735
2736 if (Key == '\r')
2737 {
2738 /* Read the next char - this is to throw away a \n which most clients should
2739 * send after \r.
2740 */
2741 KeStallExecutionProcessor(100000);
2742
2743 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2744 NextKey = KdbpTryGetCharSerial(5);
2745 else
2746 NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5);
2747
2748 if (NextKey == '\n' || NextKey == -1) /* \n or no response at all */
2749 NextKey = '\0';
2750
2751 KdbpPrint("\n");
2752
2753 /*
2754 * Repeat the last command if the user presses enter. Reduces the
2755 * risk of RSI when single-stepping.
2756 */
2757 if (Buffer == Orig)
2758 {
2759 strncpy(Buffer, LastCommand, Size);
2760 Buffer[Size - 1] = '\0';
2761 }
2762 else
2763 {
2764 *Buffer = '\0';
2765 strncpy(LastCommand, Orig, sizeof (LastCommand));
2766 LastCommand[sizeof (LastCommand) - 1] = '\0';
2767 }
2768
2769 return;
2770 }
2771 else if (Key == KEY_BS || Key == KEY_DEL)
2772 {
2773 if (Buffer > Orig)
2774 {
2775 Buffer--;
2776 *Buffer = 0;
2777
2778 if (EchoOn)
2779 KdbpPrint("%c %c", KEY_BS, KEY_BS);
2780 else
2781 KdbpPrint(" %c", KEY_BS);
2782 }
2783 }
2784 else if (ScanCode == KEY_SCAN_UP)
2785 {
2786 BOOLEAN Print = TRUE;
2787
2788 if (CmdHistIndex < 0)
2789 {
2790 CmdHistIndex = KdbCommandHistoryIndex;
2791 }
2792 else
2793 {
2794 i = CmdHistIndex - 1;
2795
2796 if (i < 0)
2797 CmdHistIndex = RTL_NUMBER_OF(KdbCommandHistory) - 1;
2798
2799 if (KdbCommandHistory[i] && i != KdbCommandHistoryIndex)
2800 CmdHistIndex = i;
2801 else
2802 Print = FALSE;
2803 }
2804
2805 if (Print && KdbCommandHistory[CmdHistIndex])
2806 {
2807 while (Buffer > Orig)
2808 {
2809 Buffer--;
2810 *Buffer = 0;
2811
2812 if (EchoOn)
2813 KdbpPrint("%c %c", KEY_BS, KEY_BS);
2814 else
2815 KdbpPrint(" %c", KEY_BS);
2816 }
2817
2818 i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
2819 memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
2820 Orig[i] = '\0';
2821 Buffer = Orig + i;
2822 KdbpPrint("%s", Orig);
2823 }
2824 }
2825 else if (ScanCode == KEY_SCAN_DOWN)
2826 {
2827 if (CmdHistIndex > 0 && CmdHistIndex != KdbCommandHistoryIndex)
2828 {
2829 i = CmdHistIndex + 1;
2830 if (i >= (INT)RTL_NUMBER_OF(KdbCommandHistory))
2831 i = 0;
2832
2833 if (KdbCommandHistory[i])
2834 {
2835 CmdHistIndex = i;
2836 while (Buffer > Orig)
2837 {
2838 Buffer--;
2839 *Buffer = 0;
2840
2841 if (EchoOn)
2842 KdbpPrint("%c %c", KEY_BS, KEY_BS);
2843 else
2844 KdbpPrint(" %c", KEY_BS);
2845 }
2846
2847 i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
2848 memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
2849 Orig[i] = '\0';
2850 Buffer = Orig + i;
2851 KdbpPrint("%s", Orig);
2852 }
2853 }
2854 }
2855 else
2856 {
2857 if (EchoOn)
2858 KdbpPrint("%c", Key);
2859
2860 *Buffer = Key;
2861 Buffer++;
2862 }
2863 }
2864 }
2865
2866 /*!\brief Parses command line and executes command if found
2867 *
2868 * \param Command Command line to parse and execute if possible.
2869 *
2870 * \retval TRUE Don't continue execution.
2871 * \retval FALSE Continue execution (leave KDB)
2872 */
2873 static BOOLEAN
2874 KdbpDoCommand(
2875 IN PCHAR Command)
2876 {
2877 ULONG i;
2878 PCHAR p;
2879 ULONG Argc;
2880 static PCH Argv[256];
2881 static CHAR OrigCommand[1024];
2882
2883 strncpy(OrigCommand, Command, sizeof(OrigCommand) - 1);
2884 OrigCommand[sizeof(OrigCommand) - 1] = '\0';
2885
2886 Argc = 0;
2887 p = Command;
2888
2889 for (;;)
2890 {
2891 while (*p == '\t' || *p == ' ')
2892 p++;
2893
2894 if (*p == '\0')
2895 break;
2896
2897 i = strcspn(p, "\t ");
2898 Argv[Argc++] = p;
2899 p += i;
2900 if (*p == '\0')
2901 break;
2902
2903 *p = '\0';
2904 p++;
2905 }
2906
2907 if (Argc < 1)
2908 return TRUE;
2909
2910 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
2911 {
2912 if (!KdbDebuggerCommands[i].Name)
2913 continue;
2914
2915 if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0)
2916 {
2917 return KdbDebuggerCommands[i].Fn(Argc, Argv);
2918 }
2919 }
2920
2921 KdbpPrint("Command '%s' is unknown.\n", OrigCommand);
2922 return TRUE;
2923 }
2924
2925 /*!\brief KDB Main Loop.
2926 *
2927 * \param EnteredOnSingleStep TRUE if KDB was entered on single step.
2928 */
2929 VOID
2930 KdbpCliMainLoop(
2931 IN BOOLEAN EnteredOnSingleStep)
2932 {
2933 static CHAR Command[1024];
2934 BOOLEAN Continue;
2935
2936 if (EnteredOnSingleStep)
2937 {
2938 if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
2939 {
2940 KdbpPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip);
2941 }
2942
2943 KdbpPrint(": ");
2944 if (KdbpDisassemble(KdbCurrentTrapFrame->Tf.Eip, KdbUseIntelSyntax) < 0)
2945 {
2946 KdbpPrint("<INVALID>");
2947 }
2948 KdbpPrint("\n");
2949 }
2950
2951 /* Flush the input buffer */
2952 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2953 {
2954 while (KdbpTryGetCharSerial(1) != -1);
2955 }
2956 else
2957 {
2958 ULONG ScanCode;
2959 while (KdbpTryGetCharKeyboard(&ScanCode, 1) != -1);
2960 }
2961
2962 /* Main loop */
2963 do
2964 {
2965 /* Print the prompt */
2966 KdbpPrint("kdb:> ");
2967
2968 /* Read a command and remember it */
2969 KdbpReadCommand(Command, sizeof (Command));
2970 KdbpCommandHistoryAppend(Command);
2971
2972 /* Reset the number of rows/cols printed and output aborted state */
2973 KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
2974 KdbOutputAborted = FALSE;
2975
2976 /* Call the command */
2977 Continue = KdbpDoCommand(Command);
2978 }
2979 while (Continue);
2980 }
2981
2982 /*!\brief Called when a module is loaded.
2983 *
2984 * \param Name Filename of the module which was loaded.
2985 */
2986 VOID
2987 KdbpCliModuleLoaded(
2988 IN PUNICODE_STRING Name)
2989 {
2990 if (!KdbBreakOnModuleLoad)
2991 return;
2992
2993 KdbpPrint("Module %wZ loaded.\n", Name);
2994 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
2995 }
2996
2997 /*!\brief This function is called by KdbEnterDebuggerException...
2998 *
2999 * Used to interpret the init file in a context with a trapframe setup
3000 * (KdbpCliInit call KdbEnter which will call KdbEnterDebuggerException which will
3001 * call this function if KdbInitFileBuffer is not NULL.
3002 */
3003 VOID
3004 KdbpCliInterpretInitFile()
3005 {
3006 PCHAR p1, p2;
3007 INT i;
3008 CHAR c;
3009
3010 /* Execute the commands in the init file */
3011 DPRINT("KDB: Executing KDBinit file...\n");
3012 p1 = KdbInitFileBuffer;
3013 while (p1[0] != '\0')
3014 {
3015 i = strcspn(p1, "\r\n");
3016 if (i > 0)
3017 {
3018 c = p1[i];
3019 p1[i] = '\0';
3020
3021 /* Look for "break" command and comments */
3022 p2 = p1;
3023
3024 while (isspace(p2[0]))
3025 p2++;
3026
3027 if (strncmp(p2, "break", sizeof("break")-1) == 0 &&
3028 (p2[sizeof("break")-1] == '\0' || isspace(p2[sizeof("break")-1])))
3029 {
3030 /* break into the debugger */
3031 KdbpCliMainLoop(FALSE);
3032 }
3033 else if (p2[0] != '#' && p2[0] != '\0') /* Ignore empty lines and comments */
3034 {
3035 KdbpDoCommand(p1);
3036 }
3037
3038 p1[i] = c;
3039 }
3040
3041 p1 += i;
3042 while (p1[0] == '\r' || p1[0] == '\n')
3043 p1++;
3044 }
3045 DPRINT("KDB: KDBinit executed\n");
3046 }
3047
3048 /*!\brief Called when KDB is initialized
3049 *
3050 * Reads the KDBinit file from the SystemRoot\system32\drivers\etc directory and executes it.
3051 */
3052 VOID
3053 KdbpCliInit()
3054 {
3055 NTSTATUS Status;
3056 OBJECT_ATTRIBUTES ObjectAttributes;
3057 UNICODE_STRING FileName;
3058 IO_STATUS_BLOCK Iosb;
3059 FILE_STANDARD_INFORMATION FileStdInfo;
3060 HANDLE hFile = NULL;
3061 INT FileSize;
3062 PCHAR FileBuffer;
3063 ULONG OldEflags;
3064
3065 /* Initialize the object attributes */
3066 RtlInitUnicodeString(&FileName, L"\\SystemRoot\\system32\\drivers\\etc\\KDBinit");
3067 InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL);
3068
3069 /* Open the file */
3070 Status = ZwOpenFile(&hFile, FILE_READ_DATA, &ObjectAttributes, &Iosb, 0,
3071 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
3072 FILE_NO_INTERMEDIATE_BUFFERING);
3073 if (!NT_SUCCESS(Status))
3074 {
3075 DPRINT("Could not open \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
3076 return;
3077 }
3078
3079 /* Get the size of the file */
3080 Status = ZwQueryInformationFile(hFile, &Iosb, &FileStdInfo, sizeof (FileStdInfo),
3081 FileStandardInformation);
3082 if (!NT_SUCCESS(Status))
3083 {
3084 ZwClose(hFile);
3085 DPRINT("Could not query size of \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
3086 return;
3087 }
3088 FileSize = FileStdInfo.EndOfFile.u.LowPart;
3089
3090 /* Allocate memory for the file */
3091 FileBuffer = ExAllocatePool(PagedPool, FileSize + 1); /* add 1 byte for terminating '\0' */
3092 if (!FileBuffer)
3093 {
3094 ZwClose(hFile);
3095 DPRINT("Could not allocate %d bytes for KDBinit file\n", FileSize);
3096 return;
3097 }
3098
3099 /* Load file into memory */
3100 Status = ZwReadFile(hFile, 0, 0, 0, &Iosb, FileBuffer, FileSize, 0, 0);
3101 ZwClose(hFile);
3102
3103 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
3104 {
3105 ExFreePool(FileBuffer);
3106 DPRINT("Could not read KDBinit file into memory (Status 0x%lx)\n", Status);
3107 return;
3108 }
3109
3110 FileSize = min(FileSize, (INT)Iosb.Information);
3111 FileBuffer[FileSize] = '\0';
3112
3113 /* Enter critical section */
3114 OldEflags = __readeflags();
3115 _disable();
3116
3117 /* Interpret the init file... */
3118 KdbInitFileBuffer = FileBuffer;
3119 KdbEnter();
3120 KdbInitFileBuffer = NULL;
3121
3122 /* Leave critical section */
3123 __writeeflags(OldEflags);
3124
3125 ExFreePool(FileBuffer);
3126 }
3127
3128 VOID
3129 NTAPI
3130 KdpSerialDebugPrint(
3131 LPSTR Message,
3132 ULONG Length
3133 );
3134
3135 STRING KdpPromptString = RTL_CONSTANT_STRING("kdb:> ");
3136 extern KSPIN_LOCK KdpSerialSpinLock;
3137
3138 ULONG
3139 NTAPI
3140 KdpPrompt(IN LPSTR InString,
3141 IN USHORT InStringLength,
3142 OUT LPSTR OutString,
3143 IN USHORT OutStringLength)
3144 {
3145 USHORT i;
3146 CHAR Response;
3147 ULONG DummyScanCode;
3148 KIRQL OldIrql;
3149
3150 /* Acquire the printing spinlock without waiting at raised IRQL */
3151 while (TRUE)
3152 {
3153 /* Wait when the spinlock becomes available */
3154 while (!KeTestSpinLock(&KdpSerialSpinLock));
3155
3156 /* Spinlock was free, raise IRQL */
3157 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
3158
3159 /* Try to get the spinlock */
3160 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock))
3161 break;
3162
3163 /* Someone else got the spinlock, lower IRQL back */
3164 KeLowerIrql(OldIrql);
3165 }
3166
3167 /* Loop the string to send */
3168 for (i = 0; i < InStringLength; i++)
3169 {
3170 /* Print it to serial */
3171 KdPortPutByteEx(&SerialPortInfo, *(PCHAR)(InString + i));
3172 }
3173
3174 /* Print a new line for log neatness */
3175 KdPortPutByteEx(&SerialPortInfo, '\r');
3176 KdPortPutByteEx(&SerialPortInfo, '\n');
3177
3178 /* Print the kdb prompt */
3179 for (i = 0; i < KdpPromptString.Length; i++)
3180 {
3181 /* Print it to serial */
3182 KdPortPutByteEx(&SerialPortInfo,
3183 *(KdpPromptString.Buffer + i));
3184 }
3185
3186 /* Loop the whole string */
3187 for (i = 0; i < OutStringLength; i++)
3188 {
3189 /* Check if this is serial debugging mode */
3190 if (KdbDebugState & KD_DEBUG_KDSERIAL)
3191 {
3192 /* Get the character from serial */
3193 do
3194 {
3195 Response = KdbpTryGetCharSerial(MAXULONG);
3196 } while (Response == -1);
3197 }
3198 else
3199 {
3200 /* Get the response from the keyboard */
3201 do
3202 {
3203 Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG);
3204 } while (Response == -1);
3205 }
3206
3207 /* Check for return */
3208 if (Response == '\r')
3209 {
3210 /*
3211 * We might need to discard the next '\n'.
3212 * Wait a bit to make sure we receive it.
3213 */
3214 KeStallExecutionProcessor(100000);
3215
3216 /* Check the mode */
3217 if (KdbDebugState & KD_DEBUG_KDSERIAL)
3218 {
3219 /* Read and discard the next character, if any */
3220 KdbpTryGetCharSerial(5);
3221 }
3222 else
3223 {
3224 /* Read and discard the next character, if any */
3225 KdbpTryGetCharKeyboard(&DummyScanCode, 5);
3226 }
3227
3228 /*
3229 * Null terminate the output string -- documentation states that
3230 * DbgPrompt does not null terminate, but it does
3231 */
3232 *(PCHAR)(OutString + i) = 0;
3233
3234 /* Print a new line */
3235 KdPortPutByteEx(&SerialPortInfo, '\r');
3236 KdPortPutByteEx(&SerialPortInfo, '\n');
3237
3238 /* Release spinlock */
3239 KiReleaseSpinLock(&KdpSerialSpinLock);
3240
3241 /* Lower IRQL back */
3242 KeLowerIrql(OldIrql);
3243
3244 /* Return the length */
3245 return OutStringLength + 1;
3246 }
3247
3248 /* Write it back and print it to the log */
3249 *(PCHAR)(OutString + i) = Response;
3250 KdPortPutByteEx(&SerialPortInfo, Response);
3251 }
3252
3253 /* Print a new line */
3254 KdPortPutByteEx(&SerialPortInfo, '\r');
3255 KdPortPutByteEx(&SerialPortInfo, '\n');
3256
3257 /* Release spinlock */
3258 KiReleaseSpinLock(&KdpSerialSpinLock);
3259
3260 /* Lower IRQL back */
3261 KeLowerIrql(OldIrql);
3262
3263 /* Return the length */
3264 return OutStringLength;
3265 }