Fix kdbg build and some more header cleanups: add csq.q to ntifs, since it's now...
[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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/dbg/kdb_cli.c
23 * PURPOSE: Kernel debugger command line interface
24 * PROGRAMMER: Gregor Anich (blight@blight.eu.org)
25 * UPDATE HISTORY:
26 * Created 16/01/2005
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <ntoskrnl.h>
32
33 #define NDEBUG
34 #include <internal/debug.h>
35
36 /* DEFINES *******************************************************************/
37
38 /* FIXME: NDK headers */
39 #define TempEsp TempEip
40 #define TempSegSs TempCs
41
42 #define KEY_BS 8
43 #define KEY_ESC 27
44 #define KEY_DEL 127
45
46 #define KEY_SCAN_UP 72
47 #define KEY_SCAN_DOWN 80
48
49 #define KDB_ENTER_CONDITION_TO_STRING(cond) \
50 ((cond) == KdbDoNotEnter ? "never" : \
51 ((cond) == KdbEnterAlways ? "always" : \
52 ((cond) == KdbEnterFromKmode ? "kmode" : "umode")))
53
54 #define KDB_ACCESS_TYPE_TO_STRING(type) \
55 ((type) == KdbAccessRead ? "read" : \
56 ((type) == KdbAccessWrite ? "write" : \
57 ((type) == KdbAccessReadWrite ? "rdwr" : "exec")))
58
59 #define NPX_STATE_TO_STRING(state) \
60 ((state) == NPX_STATE_INVALID ? "Invalid" : \
61 ((state) == NPX_STATE_VALID ? "Valid" : \
62 ((state) == NPX_STATE_DIRTY ? "Dirty" : "Unknown")))
63
64 /* PROTOTYPES ****************************************************************/
65
66 STATIC BOOLEAN KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]);
67 STATIC BOOLEAN KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[]);
68 STATIC BOOLEAN KdbpCmdRegs(ULONG Argc, PCHAR Argv[]);
69 STATIC BOOLEAN KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]);
70
71 STATIC BOOLEAN KdbpCmdContinue(ULONG Argc, PCHAR Argv[]);
72 STATIC BOOLEAN KdbpCmdStep(ULONG Argc, PCHAR Argv[]);
73 STATIC BOOLEAN KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[]);
74 STATIC BOOLEAN KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[]);
75 STATIC BOOLEAN KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]);
76
77 STATIC BOOLEAN KdbpCmdThread(ULONG Argc, PCHAR Argv[]);
78 STATIC BOOLEAN KdbpCmdProc(ULONG Argc, PCHAR Argv[]);
79
80 STATIC BOOLEAN KdbpCmdMod(ULONG Argc, PCHAR Argv[]);
81 STATIC BOOLEAN KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]);
82 STATIC BOOLEAN KdbpCmdPcr(ULONG Argc, PCHAR Argv[]);
83 STATIC BOOLEAN KdbpCmdTss(ULONG Argc, PCHAR Argv[]);
84
85 STATIC BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]);
86 STATIC BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
87 STATIC BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
88
89 /* GLOBALS *******************************************************************/
90
91 STATIC BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */
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
106 STATIC CONST struct
107 {
108 PCHAR Name;
109 PCHAR Syntax;
110 PCHAR Help;
111 BOOLEAN (*Fn)(ULONG Argc, PCHAR Argv[]);
112 } KdbDebuggerCommands[] = {
113 /* Data */
114 { NULL, NULL, "Data", NULL },
115 { "?", "? expression", "Evaluate expression.", KdbpCmdEvalExpression },
116 { "disasm", "disasm [address] [L count]", "Disassemble count instructions at address.", KdbpCmdDisassembleX },
117 { "x", "x [address] [L count]", "Display count dwords, starting at addr.", KdbpCmdDisassembleX },
118 { "regs", "regs", "Display general purpose registers.", KdbpCmdRegs },
119 { "cregs", "cregs", "Display control registers.", KdbpCmdRegs },
120 { "sregs", "sregs", "Display status registers.", KdbpCmdRegs },
121 { "dregs", "dregs", "Display debug registers.", KdbpCmdRegs },
122 { "bt", "bt [*frameaddr|thread id]", "Prints current backtrace or from given frame addr", KdbpCmdBackTrace },
123
124 /* Flow control */
125 { NULL, NULL, "Flow control", NULL },
126 { "cont", "cont", "Continue execution (leave debugger)", KdbpCmdContinue },
127 { "step", "step [count]", "Execute single instructions, stepping into interrupts.", KdbpCmdStep },
128 { "next", "next [count]", "Execute single instructions, skipping calls and reps.", KdbpCmdStep },
129 { "bl", "bl", "List breakpoints.", KdbpCmdBreakPointList },
130 { "be", "be [breakpoint]", "Enable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
131 { "bd", "bd [breakpoint]", "Disable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
132 { "bc", "bc [breakpoint]", "Clear breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
133 { "bpx", "bpx [address] [IF condition]", "Set software execution breakpoint at address.", KdbpCmdBreakPoint },
134 { "bpm", "bpm [r|w|rw|x] [byte|word|dword] [address] [IF condition]", "Set memory breakpoint at address.", KdbpCmdBreakPoint },
135
136 /* Process/Thread */
137 { NULL, NULL, "Process/Thread", NULL },
138 { "thread", "thread [list[ pid]|[attach ]tid]", "List threads in current or specified process, display thread with given id or attach to thread.", KdbpCmdThread },
139 { "proc", "proc [list|[attach ]pid]", "List processes, display process with given id or attach to process.", KdbpCmdProc },
140
141 /* System information */
142 { NULL, NULL, "System info", NULL },
143 { "mod", "mod [address]", "List all modules or the one containing address.", KdbpCmdMod },
144 { "gdt", "gdt", "Display global descriptor table.", KdbpCmdGdtLdtIdt },
145 { "ldt", "ldt", "Display local descriptor table.", KdbpCmdGdtLdtIdt },
146 { "idt", "idt", "Display interrupt descriptor table.", KdbpCmdGdtLdtIdt },
147 { "pcr", "pcr", "Display processor control region.", KdbpCmdPcr },
148 { "tss", "tss", "Display task state segment.", KdbpCmdTss },
149
150 /* Others */
151 { NULL, NULL, "Others", NULL },
152 { "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck },
153 { "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet },
154 { "help", "help", "Display help screen.", KdbpCmdHelp }
155 };
156
157 /* FUNCTIONS *****************************************************************/
158
159 /*!\brief Evaluates an expression...
160 *
161 * Much like KdbpRpnEvaluateExpression, but prints the error message (if any)
162 * at the given offset.
163 *
164 * \param Expression Expression to evaluate.
165 * \param ErrOffset Offset (in characters) to print the error message at.
166 * \param Result Receives the result on success.
167 *
168 * \retval TRUE Success.
169 * \retval FALSE Failure.
170 */
171 STATIC BOOLEAN
172 KdbpEvaluateExpression(
173 IN PCHAR Expression,
174 IN LONG ErrOffset,
175 OUT PULONGLONG Result)
176 {
177 STATIC CHAR ErrMsgBuffer[130] = "^ ";
178 LONG ExpressionErrOffset = -1;
179 PCHAR ErrMsg = ErrMsgBuffer;
180 BOOLEAN Ok;
181
182 Ok = KdbpRpnEvaluateExpression(Expression, KdbCurrentTrapFrame, Result,
183 &ExpressionErrOffset, ErrMsgBuffer + 2);
184 if (!Ok)
185 {
186 if (ExpressionErrOffset >= 0)
187 ExpressionErrOffset += ErrOffset;
188 else
189 ErrMsg += 2;
190 KdbpPrint("%*s%s\n", ExpressionErrOffset, "", ErrMsg);
191 }
192
193 return Ok;
194 }
195
196 /*!\brief Evaluates an expression and displays the result.
197 */
198 STATIC BOOLEAN
199 KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[])
200 {
201 INT i, len;
202 ULONGLONG Result = 0;
203 ULONG ul;
204 LONG l = 0;
205 BOOLEAN Ok;
206
207 if (Argc < 2)
208 {
209 KdbpPrint("?: Argument required\n");
210 return TRUE;
211 }
212
213 /* Put the arguments back together */
214 Argc--;
215 for (i = 1; i < Argc; i++)
216 {
217 len = strlen(Argv[i]);
218 Argv[i][len] = ' ';
219 }
220
221 /* Evaluate the expression */
222 Ok = KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result);
223 if (Ok)
224 {
225 if (Result > 0x00000000ffffffffLL)
226 {
227 if (Result & 0x8000000000000000LL)
228 KdbpPrint("0x%016I64x %20I64u %20I64d\n", Result, Result, Result);
229 else
230 KdbpPrint("0x%016I64x %20I64u\n", Result, Result);
231 }
232 else
233 {
234 ul = (ULONG)Result;
235 if (ul <= 0xff && ul >= 0x80)
236 l = (LONG)((CHAR)ul);
237 else if (ul <= 0xffff && ul >= 0x8000)
238 l = (LONG)((SHORT)ul);
239 else
240 l = (LONG)ul;
241 if (l < 0)
242 KdbpPrint("0x%08lx %10lu %10ld\n", ul, ul, l);
243 else
244 KdbpPrint("0x%08lx %10lu\n", ul, ul);
245 }
246 }
247
248 return TRUE;
249 }
250
251 /*!\brief Disassembles 10 instructions at eip or given address or
252 * displays 16 dwords from memory at given address.
253 */
254 STATIC BOOLEAN
255 KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[])
256 {
257 ULONG Count;
258 ULONG ul;
259 INT i;
260 ULONGLONG Result = 0;
261 ULONG_PTR Address = KdbCurrentTrapFrame->Tf.Eip;
262 LONG InstLen;
263
264 if (Argv[0][0] == 'x') /* display memory */
265 Count = 16;
266 else /* disassemble */
267 Count = 10;
268
269 if (Argc >= 2)
270 {
271 /* Check for [L count] part */
272 ul = 0;
273 if (strcmp(Argv[Argc-2], "L") == 0)
274 {
275 ul = strtoul(Argv[Argc-1], NULL, 0);
276 if (ul > 0)
277 {
278 Count = ul;
279 Argc -= 2;
280 }
281 }
282 else if (Argv[Argc-1][0] == 'L')
283 {
284 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
285 if (ul > 0)
286 {
287 Count = ul;
288 Argc--;
289 }
290 }
291
292 /* Put the remaining arguments back together */
293 Argc--;
294 for (ul = 1; ul < Argc; ul++)
295 {
296 Argv[ul][strlen(Argv[ul])] = ' ';
297 }
298 Argc++;
299 }
300
301 /* Evaluate the expression */
302 if (Argc > 1)
303 {
304 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
305 return TRUE;
306 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
307 KdbpPrint("Warning: Address %I64x is beeing truncated\n");
308 Address = (ULONG_PTR)Result;
309 }
310 else if (Argv[0][0] == 'x')
311 {
312 KdbpPrint("x: Address argument required.\n");
313 return TRUE;
314 }
315
316 if (Argv[0][0] == 'x')
317 {
318 /* Display dwords */
319 ul = 0;
320 while (Count > 0)
321 {
322 if (!KdbSymPrintAddress((PVOID)Address))
323 KdbpPrint("<%x>:", Address);
324 else
325 KdbpPrint(":");
326 i = min(4, Count);
327 Count -= i;
328 while (--i >= 0)
329 {
330 if (!NT_SUCCESS(KdbpSafeReadMemory(&ul, (PVOID)Address, sizeof(ul))))
331 KdbpPrint(" ????????");
332 else
333 KdbpPrint(" %08x", ul);
334 Address += sizeof(ul);
335 }
336 KdbpPrint("\n");
337 }
338 }
339 else
340 {
341 /* Disassemble */
342 while (Count-- > 0)
343 {
344 if (!KdbSymPrintAddress((PVOID)Address))
345 KdbpPrint("<%08x>: ", Address);
346 else
347 KdbpPrint(": ");
348 InstLen = KdbpDisassemble(Address, KdbUseIntelSyntax);
349 if (InstLen < 0)
350 {
351 KdbpPrint("<INVALID>\n");
352 return TRUE;
353 }
354 KdbpPrint("\n");
355 Address += InstLen;
356 }
357 }
358
359 return TRUE;
360 }
361
362 /*!\brief Displays CPU registers.
363 */
364 STATIC BOOLEAN
365 KdbpCmdRegs(ULONG Argc, PCHAR Argv[])
366 {
367 PKTRAP_FRAME Tf = &KdbCurrentTrapFrame->Tf;
368 INT i;
369 STATIC CONST PCHAR EflagsBits[32] = { " CF", NULL, " PF", " BIT3", " AF", " BIT5",
370 " ZF", " SF", " TF", " IF", " DF", " OF",
371 NULL, NULL, " NT", " BIT15", " RF", " VF",
372 " AC", " VIF", " VIP", " ID", " BIT22",
373 " BIT23", " BIT24", " BIT25", " BIT26",
374 " BIT27", " BIT28", " BIT29", " BIT30",
375 " BIT31" };
376
377 if (Argv[0][0] == 'r') /* regs */
378 {
379 KdbpPrint("CS:EIP 0x%04x:0x%08x\n"
380 "SS:ESP 0x%04x:0x%08x\n"
381 " EAX 0x%08x EBX 0x%08x\n"
382 " ECX 0x%08x EDX 0x%08x\n"
383 " ESI 0x%08x EDI 0x%08x\n"
384 " EBP 0x%08x\n",
385 Tf->Cs & 0xFFFF, Tf->Eip,
386 Tf->Ss, Tf->Esp,
387 Tf->Eax, Tf->Ebx,
388 Tf->Ecx, Tf->Edx,
389 Tf->Esi, Tf->Edi,
390 Tf->Ebp);
391 KdbpPrint("EFLAGS 0x%08x ", Tf->Eflags);
392 for (i = 0; i < 32; i++)
393 {
394 if (i == 1)
395 {
396 if ((Tf->Eflags & (1 << 1)) == 0)
397 KdbpPrint(" !BIT1");
398 }
399 else if (i == 12)
400 {
401 KdbpPrint(" IOPL%d", (Tf->Eflags >> 12) & 3);
402 }
403 else if (i == 13)
404 {
405 }
406 else if ((Tf->Eflags & (1 << i)) != 0)
407 KdbpPrint(EflagsBits[i]);
408 }
409 KdbpPrint("\n");
410 }
411 else if (Argv[0][0] == 'c') /* cregs */
412 {
413 ULONG Cr0, Cr2, Cr3, Cr4;
414 struct __attribute__((packed)) {
415 USHORT Limit;
416 ULONG Base;
417 } Gdtr, Ldtr, Idtr;
418 ULONG Tr;
419 STATIC CONST PCHAR Cr0Bits[32] = { " PE", " MP", " EM", " TS", " ET", " NE", NULL, NULL,
420 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
421 " WP", NULL, " AM", NULL, NULL, NULL, NULL, NULL,
422 NULL, NULL, NULL, NULL, NULL, " NW", " CD", " PG" };
423 STATIC CONST PCHAR Cr4Bits[32] = { " VME", " PVI", " TSD", " DE", " PSE", " PAE", " MCE", " PGE",
424 " PCE", " OSFXSR", " OSXMMEXCPT", NULL, NULL, NULL, NULL, NULL,
425 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
426 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
427
428 Cr0 = KdbCurrentTrapFrame->Cr0;
429 Cr2 = KdbCurrentTrapFrame->Cr2;
430 Cr3 = KdbCurrentTrapFrame->Cr3;
431 Cr4 = KdbCurrentTrapFrame->Cr4;
432
433 /* Get descriptor table regs */
434 asm volatile("sgdt %0" : : "m"(Gdtr));
435 asm volatile("sldt %0" : : "m"(Ldtr));
436 asm volatile("sidt %0" : : "m"(Idtr));
437
438 /* Get the task register */
439 asm volatile("str %0" : "=g"(Tr));
440
441 /* Display the control registers */
442 KdbpPrint("CR0 0x%08x ", Cr0);
443 for (i = 0; i < 32; i++)
444 {
445 if (Cr0Bits[i] == NULL)
446 continue;
447 if ((Cr0 & (1 << i)) != 0)
448 KdbpPrint(Cr0Bits[i]);
449 }
450 KdbpPrint("\nCR2 0x%08x\n", Cr2);
451 KdbpPrint("CR3 0x%08x Pagedir-Base 0x%08x %s%s\n", Cr3, (Cr3 & 0xfffff000),
452 (Cr3 & (1 << 3)) ? " PWT" : "", (Cr3 & (1 << 4)) ? " PCD" : "" );
453 KdbpPrint("CR4 0x%08x ", Cr4);
454 for (i = 0; i < 32; i++)
455 {
456 if (Cr4Bits[i] == NULL)
457 continue;
458 if ((Cr4 & (1 << i)) != 0)
459 KdbpPrint(Cr4Bits[i]);
460 }
461
462 /* Display the descriptor table regs */
463 KdbpPrint("\nGDTR Base 0x%08x Size 0x%04x\n", Gdtr.Base, Gdtr.Limit);
464 KdbpPrint("LDTR Base 0x%08x Size 0x%04x\n", Ldtr.Base, Ldtr.Limit);
465 KdbpPrint("IDTR Base 0x%08x Size 0x%04x\n", Idtr.Base, Idtr.Limit);
466 }
467 else if (Argv[0][0] == 's') /* sregs */
468 {
469 KdbpPrint("CS 0x%04x Index 0x%04x %cDT RPL%d\n",
470 Tf->Cs & 0xffff, (Tf->Cs & 0xffff) >> 3,
471 (Tf->Cs & (1 << 2)) ? 'L' : 'G', Tf->Cs & 3);
472 KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n",
473 Tf->Ds, Tf->Ds >> 3, (Tf->Ds & (1 << 2)) ? 'L' : 'G', Tf->Ds & 3);
474 KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n",
475 Tf->Es, Tf->Es >> 3, (Tf->Es & (1 << 2)) ? 'L' : 'G', Tf->Es & 3);
476 KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n",
477 Tf->Fs, Tf->Fs >> 3, (Tf->Fs & (1 << 2)) ? 'L' : 'G', Tf->Fs & 3);
478 KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n",
479 Tf->Gs, Tf->Gs >> 3, (Tf->Gs & (1 << 2)) ? 'L' : 'G', Tf->Gs & 3);
480 KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
481 Tf->Ss, Tf->Ss >> 3, (Tf->Ss & (1 << 2)) ? 'L' : 'G', Tf->Ss & 3);
482 }
483 else /* dregs */
484 {
485 ASSERT(Argv[0][0] == 'd');
486 KdbpPrint("DR0 0x%08x\n"
487 "DR1 0x%08x\n"
488 "DR2 0x%08x\n"
489 "DR3 0x%08x\n"
490 "DR6 0x%08x\n"
491 "DR7 0x%08x\n",
492 Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3,
493 Tf->Dr6, Tf->Dr7);
494 }
495 return TRUE;
496 }
497
498 /*!\brief Displays a backtrace.
499 */
500 STATIC BOOLEAN
501 KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[])
502 {
503 ULONG Count;
504 ULONG ul;
505 ULONGLONG Result = 0;
506 ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp;
507 ULONG_PTR Address;
508
509 if (Argc >= 2)
510 {
511 /* Check for [L count] part */
512 ul = 0;
513 if (strcmp(Argv[Argc-2], "L") == 0)
514 {
515 ul = strtoul(Argv[Argc-1], NULL, 0);
516 if (ul > 0)
517 {
518 Count = ul;
519 Argc -= 2;
520 }
521 }
522 else if (Argv[Argc-1][0] == 'L')
523 {
524 ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
525 if (ul > 0)
526 {
527 Count = ul;
528 Argc--;
529 }
530 }
531
532 /* Put the remaining arguments back together */
533 Argc--;
534 for (ul = 1; ul < Argc; ul++)
535 {
536 Argv[ul][strlen(Argv[ul])] = ' ';
537 }
538 Argc++;
539 }
540
541 /* Check if frame addr or thread id is given. */
542 if (Argc > 1)
543 {
544 if (Argv[1][0] == '*')
545 {
546 Argv[1]++;
547 /* Evaluate the expression */
548 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
549 return TRUE;
550 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
551 KdbpPrint("Warning: Address %I64x is beeing truncated\n");
552 Frame = (ULONG_PTR)Result;
553 }
554 else
555 {
556
557 KdbpPrint("Thread backtrace not supported yet!\n");
558 return TRUE;
559 }
560 }
561 else
562 {
563 KdbpPrint("Eip:\n");
564 /* Try printing the function at EIP */
565 if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
566 KdbpPrint("<%08x>\n", KdbCurrentTrapFrame->Tf.Eip);
567 else
568 KdbpPrint("\n");
569 }
570
571 KdbpPrint("Frames:\n");
572 for (;;)
573 {
574 if (Frame == 0)
575 break;
576 if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR))))
577 {
578 KdbpPrint("Couldn't access memory at 0x%x!\n", Frame + sizeof(ULONG_PTR));
579 break;
580 }
581 if (!KdbSymPrintAddress((PVOID)Address))
582 KdbpPrint("<%08x>\n", Address);
583 else
584 KdbpPrint("\n");
585 if (Address == 0)
586 break;
587 if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR))))
588 {
589 KdbpPrint("Couldn't access memory at 0x%x!\n", Frame);
590 break;
591 }
592 }
593
594 return TRUE;
595 }
596
597 /*!\brief Continues execution of the system/leaves KDB.
598 */
599 STATIC BOOLEAN
600 KdbpCmdContinue(ULONG Argc, PCHAR Argv[])
601 {
602 /* Exit the main loop */
603 return FALSE;
604 }
605
606 /*!\brief Continues execution of the system/leaves KDB.
607 */
608 STATIC BOOLEAN
609 KdbpCmdStep(ULONG Argc, PCHAR Argv[])
610 {
611 ULONG Count = 1;
612
613 if (Argc > 1)
614 {
615 Count = strtoul(Argv[1], NULL, 0);
616 if (Count == 0)
617 {
618 KdbpPrint("%s: Integer argument required\n", Argv[0]);
619 return TRUE;
620 }
621 }
622
623 if (Argv[0][0] == 'n')
624 KdbSingleStepOver = TRUE;
625 else
626 KdbSingleStepOver = FALSE;
627
628 /* Set the number of single steps and return to the interrupted code. */
629 KdbNumSingleSteps = Count;
630
631 return FALSE;
632 }
633
634 /*!\brief Lists breakpoints.
635 */
636 STATIC BOOLEAN
637 KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[])
638 {
639 LONG l;
640 ULONG_PTR Address = 0;
641 KDB_BREAKPOINT_TYPE Type = 0;
642 KDB_ACCESS_TYPE AccessType = 0;
643 UCHAR Size = 0;
644 UCHAR DebugReg = 0;
645 BOOLEAN Enabled = FALSE;
646 BOOLEAN Global = FALSE;
647 PEPROCESS Process = NULL;
648 PCHAR str1, str2, ConditionExpr, GlobalOrLocal;
649 CHAR Buffer[20];
650
651 l = KdbpGetNextBreakPointNr(0);
652 if (l < 0)
653 {
654 KdbpPrint("No breakpoints.\n");
655 return TRUE;
656 }
657
658 KdbpPrint("Breakpoints:\n");
659 do
660 {
661 if (!KdbpGetBreakPointInfo(l, &Address, &Type, &Size, &AccessType, &DebugReg,
662 &Enabled, &Global, &Process, &ConditionExpr))
663 {
664 continue;
665 }
666
667 if (l == KdbLastBreakPointNr)
668 {
669 str1 = "\x1b[1m*";
670 str2 = "\x1b[0m";
671 }
672 else
673 {
674 str1 = " ";
675 str2 = "";
676 }
677
678 if (Global)
679 GlobalOrLocal = " global";
680 else
681 {
682 GlobalOrLocal = Buffer;
683 sprintf(Buffer, " PID 0x%08lx",
684 (ULONG)(Process ? Process->UniqueProcessId : INVALID_HANDLE_VALUE));
685 }
686
687 if (Type == KdbBreakPointSoftware || Type == KdbBreakPointTemporary)
688 {
689 KdbpPrint(" %s%03d BPX 0x%08x%s%s%s%s%s\n",
690 str1, l, Address,
691 Enabled ? "" : " disabled",
692 GlobalOrLocal,
693 ConditionExpr ? " IF " : "",
694 ConditionExpr ? ConditionExpr : "",
695 str2);
696 }
697 else if (Type == KdbBreakPointHardware)
698 {
699 if (!Enabled)
700 {
701 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s disabled%s%s%s%s\n", str1, l, Address,
702 KDB_ACCESS_TYPE_TO_STRING(AccessType),
703 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
704 GlobalOrLocal,
705 ConditionExpr ? " IF " : "",
706 ConditionExpr ? ConditionExpr : "",
707 str2);
708 }
709 else
710 {
711 KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s DR%d%s%s%s%s\n", str1, l, Address,
712 KDB_ACCESS_TYPE_TO_STRING(AccessType),
713 Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
714 DebugReg,
715 GlobalOrLocal,
716 ConditionExpr ? " IF " : "",
717 ConditionExpr ? ConditionExpr : "",
718 str2);
719 }
720 }
721 }
722 while ((l = KdbpGetNextBreakPointNr(l+1)) >= 0);
723
724 return TRUE;
725 }
726
727 /*!\brief Enables, disables or clears a breakpoint.
728 */
729 STATIC BOOLEAN
730 KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[])
731 {
732 PCHAR pend;
733 ULONG BreakPointNr;
734
735 if (Argc < 2)
736 {
737 KdbpPrint("%s: argument required\n", Argv[0]);
738 return TRUE;
739 }
740
741 pend = Argv[1];
742 BreakPointNr = strtoul(Argv[1], &pend, 0);
743 if (pend == Argv[1] || *pend != '\0')
744 {
745 KdbpPrint("%s: integer argument required\n", Argv[0]);
746 return TRUE;
747 }
748
749 if (Argv[0][1] == 'e') /* enable */
750 {
751 KdbpEnableBreakPoint(BreakPointNr, NULL);
752 }
753 else if (Argv [0][1] == 'd') /* disable */
754 {
755 KdbpDisableBreakPoint(BreakPointNr, NULL);
756 }
757 else /* clear */
758 {
759 ASSERT(Argv[0][1] == 'c');
760 KdbpDeleteBreakPoint(BreakPointNr, NULL);
761 }
762
763 return TRUE;
764 }
765
766 /*!\brief Sets a software or hardware (memory) breakpoint at the given address.
767 */
768 STATIC BOOLEAN
769 KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[])
770 {
771 ULONGLONG Result = 0;
772 ULONG_PTR Address;
773 KDB_BREAKPOINT_TYPE Type;
774 UCHAR Size = 0;
775 KDB_ACCESS_TYPE AccessType = 0;
776 INT AddressArgIndex, ConditionArgIndex, i;
777 BOOLEAN Global = TRUE;
778
779 if (Argv[0][2] == 'x') /* software breakpoint */
780 {
781 if (Argc < 2)
782 {
783 KdbpPrint("bpx: Address argument required.\n");
784 return TRUE;
785 }
786
787 AddressArgIndex = 1;
788 Type = KdbBreakPointSoftware;
789 }
790 else /* memory breakpoint */
791 {
792 ASSERT(Argv[0][2] == 'm');
793
794 if (Argc < 2)
795 {
796 KdbpPrint("bpm: Access type argument required (one of r, w, rw, x)\n");
797 return TRUE;
798 }
799
800 if (_stricmp(Argv[1], "x") == 0)
801 AccessType = KdbAccessExec;
802 else if (_stricmp(Argv[1], "r") == 0)
803 AccessType = KdbAccessRead;
804 else if (_stricmp(Argv[1], "w") == 0)
805 AccessType = KdbAccessWrite;
806 else if (_stricmp(Argv[1], "rw") == 0)
807 AccessType = KdbAccessReadWrite;
808 else
809 {
810 KdbpPrint("bpm: Unknown access type '%s'\n", Argv[1]);
811 return TRUE;
812 }
813
814 if (Argc < 3)
815 {
816 KdbpPrint("bpm: %s argument required.\n", AccessType == KdbAccessExec ? "Address" : "Memory size");
817 return TRUE;
818 }
819 AddressArgIndex = 3;
820 if (_stricmp(Argv[2], "byte") == 0)
821 Size = 1;
822 else if (_stricmp(Argv[2], "word") == 0)
823 Size = 2;
824 else if (_stricmp(Argv[2], "dword") == 0)
825 Size = 4;
826 else if (AccessType == KdbAccessExec)
827 {
828 Size = 1;
829 AddressArgIndex--;
830 }
831 else
832 {
833 KdbpPrint("bpm: Unknown memory size '%s'\n", Argv[2]);
834 return TRUE;
835 }
836
837 if (Argc <= AddressArgIndex)
838 {
839 KdbpPrint("bpm: Address argument required.\n");
840 return TRUE;
841 }
842
843 Type = KdbBreakPointHardware;
844 }
845
846 /* Put the arguments back together */
847 ConditionArgIndex = -1;
848 for (i = AddressArgIndex; i < (Argc-1); i++)
849 {
850 if (strcmp(Argv[i+1], "IF") == 0) /* IF found */
851 {
852 ConditionArgIndex = i + 2;
853 if (ConditionArgIndex >= Argc)
854 {
855 KdbpPrint("%s: IF requires condition expression.\n", Argv[0]);
856 return TRUE;
857 }
858 for (i = ConditionArgIndex; i < (Argc-1); i++)
859 Argv[i][strlen(Argv[i])] = ' ';
860 break;
861 }
862 Argv[i][strlen(Argv[i])] = ' ';
863 }
864
865 /* Evaluate the address expression */
866 if (!KdbpEvaluateExpression(Argv[AddressArgIndex],
867 sizeof("kdb:> ")-1 + (Argv[AddressArgIndex]-Argv[0]),
868 &Result))
869 {
870 return TRUE;
871 }
872 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
873 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0]);
874 Address = (ULONG_PTR)Result;
875
876 KdbpInsertBreakPoint(Address, Type, Size, AccessType,
877 (ConditionArgIndex < 0) ? NULL : Argv[ConditionArgIndex],
878 Global, NULL);
879
880 return TRUE;
881 }
882
883 /*!\brief Lists threads or switches to another thread context.
884 */
885 STATIC BOOLEAN
886 KdbpCmdThread(ULONG Argc, PCHAR Argv[])
887 {
888 PLIST_ENTRY Entry;
889 PETHREAD Thread = NULL;
890 PEPROCESS Process = NULL;
891 PULONG Esp;
892 PULONG Ebp;
893 ULONG Eip;
894 ULONG ul = 0;
895 PCHAR State, pend, str1, str2;
896 STATIC CONST PCHAR ThreadStateToString[DeferredReady+1] =
897 { "Initialized", "Ready", "Running",
898 "Standby", "Terminated", "Waiting",
899 "Transition", "DeferredReady" };
900 ASSERT(KdbCurrentProcess != NULL);
901
902 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
903 {
904 Process = KdbCurrentProcess;
905
906 if (Argc >= 3)
907 {
908 ul = strtoul(Argv[2], &pend, 0);
909 if (Argv[2] == pend)
910 {
911 KdbpPrint("thread: '%s' is not a valid process id!\n", Argv[2]);
912 return TRUE;
913 }
914 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
915 {
916 KdbpPrint("thread: Invalid process id!\n");
917 return TRUE;
918 }
919 }
920
921 Entry = Process->ThreadListHead.Flink;
922 if (Entry == &Process->ThreadListHead)
923 {
924 if (Argc >= 3)
925 KdbpPrint("No threads in process 0x%08x!\n", ul);
926 else
927 KdbpPrint("No threads in current process!\n");
928 return TRUE;
929 }
930
931 KdbpPrint(" TID State Prior. Affinity EBP EIP\n");
932 do
933 {
934 Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
935
936 if (Thread == KdbCurrentThread)
937 {
938 str1 = "\x1b[1m*";
939 str2 = "\x1b[0m";
940 }
941 else
942 {
943 str1 = " ";
944 str2 = "";
945 }
946
947 if (Thread->Tcb.TrapFrame != NULL)
948 {
949 if (Thread->Tcb.TrapFrame->PreviousMode == KernelMode)
950 Esp = (PULONG)Thread->Tcb.TrapFrame->TempEsp;
951 else
952 Esp = (PULONG)Thread->Tcb.TrapFrame->Esp;
953 Ebp = (PULONG)Thread->Tcb.TrapFrame->Ebp;
954 Eip = Thread->Tcb.TrapFrame->Eip;
955 }
956 else
957 {
958 Esp = (PULONG)Thread->Tcb.KernelStack;
959 Ebp = (PULONG)Esp[4];
960 Eip = 0;
961 if (Ebp != NULL) /* FIXME: Should we attach to the process to read Ebp[1]? */
962 KdbpSafeReadMemory(&Eip, Ebp + 1, sizeof (Eip));
963 }
964 if (Thread->Tcb.State < (DeferredReady + 1))
965 State = ThreadStateToString[Thread->Tcb.State];
966 else
967 State = "Unknown";
968
969 KdbpPrint(" %s0x%08x %-11s %3d 0x%08x 0x%08x 0x%08x%s\n",
970 str1,
971 Thread->Cid.UniqueThread,
972 State,
973 Thread->Tcb.Priority,
974 Thread->Tcb.Affinity,
975 Ebp,
976 Eip,
977 str2);
978
979 Entry = Entry->Flink;
980 }
981 while (Entry != &Process->ThreadListHead);
982 }
983 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
984 {
985 if (Argc < 3)
986 {
987 KdbpPrint("thread attach: thread id argument required!\n");
988 return TRUE;
989 }
990
991 ul = strtoul(Argv[2], &pend, 0);
992 if (Argv[2] == pend)
993 {
994 KdbpPrint("thread attach: '%s' is not a valid thread id!\n", Argv[2]);
995 return TRUE;
996 }
997 if (!KdbpAttachToThread((PVOID)ul))
998 {
999 return TRUE;
1000 }
1001 KdbpPrint("Attached to thread 0x%08x.\n", ul);
1002 }
1003 else
1004 {
1005 Thread = KdbCurrentThread;
1006
1007 if (Argc >= 2)
1008 {
1009 ul = strtoul(Argv[1], &pend, 0);
1010 if (Argv[1] == pend)
1011 {
1012 KdbpPrint("thread: '%s' is not a valid thread id!\n", Argv[1]);
1013 return TRUE;
1014 }
1015 if (!NT_SUCCESS(PsLookupThreadByThreadId((PVOID)ul, &Thread)))
1016 {
1017 KdbpPrint("thread: Invalid thread id!\n");
1018 return TRUE;
1019 }
1020 }
1021
1022 if (Thread->Tcb.State < (DeferredReady + 1))
1023 State = ThreadStateToString[Thread->Tcb.State];
1024 else
1025 State = "Unknown";
1026 KdbpPrint("%s"
1027 " TID: 0x%08x\n"
1028 " State: %s (0x%x)\n"
1029 " Priority: %d\n"
1030 " Affinity: 0x%08x\n"
1031 " Initial Stack: 0x%08x\n"
1032 " Stack Limit: 0x%08x\n"
1033 " Stack Base: 0x%08x\n"
1034 " Kernel Stack: 0x%08x\n"
1035 " Trap Frame: 0x%08x\n"
1036 " NPX State: %s (0x%x)\n",
1037 (Argc < 2) ? "Current Thread:\n" : "",
1038 Thread->Cid.UniqueThread,
1039 State, Thread->Tcb.State,
1040 Thread->Tcb.Priority,
1041 Thread->Tcb.Affinity,
1042 Thread->Tcb.InitialStack,
1043 Thread->Tcb.StackLimit,
1044 Thread->Tcb.StackBase,
1045 Thread->Tcb.KernelStack,
1046 Thread->Tcb.TrapFrame,
1047 NPX_STATE_TO_STRING(Thread->Tcb.NpxState), Thread->Tcb.NpxState);
1048
1049 }
1050
1051 return TRUE;
1052 }
1053
1054 /*!\brief Lists processes or switches to another process context.
1055 */
1056 STATIC BOOLEAN
1057 KdbpCmdProc(ULONG Argc, PCHAR Argv[])
1058 {
1059 PLIST_ENTRY Entry;
1060 PEPROCESS Process;
1061 PCHAR State, pend, str1, str2;
1062 ULONG ul;
1063 extern LIST_ENTRY PsActiveProcessHead;
1064
1065 if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
1066 {
1067 Entry = PsActiveProcessHead.Flink;
1068 if (Entry == &PsActiveProcessHead)
1069 {
1070 KdbpPrint("No processes in the system!\n");
1071 return TRUE;
1072 }
1073
1074 KdbpPrint(" PID State Filename\n");
1075 do
1076 {
1077 Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
1078
1079 if (Process == KdbCurrentProcess)
1080 {
1081 str1 = "\x1b[1m*";
1082 str2 = "\x1b[0m";
1083 }
1084 else
1085 {
1086 str1 = " ";
1087 str2 = "";
1088 }
1089
1090 State = ((Process->Pcb.State == PROCESS_STATE_TERMINATED) ? "Terminated" :
1091 ((Process->Pcb.State == PROCESS_STATE_ACTIVE) ? "Active" : "Unknown"));
1092
1093 KdbpPrint(" %s0x%08x %-10s %s%s\n",
1094 str1,
1095 Process->UniqueProcessId,
1096 State,
1097 Process->ImageFileName,
1098 str2);
1099
1100 Entry = Entry->Flink;
1101 }
1102 while(Entry != &PsActiveProcessHead);
1103 }
1104 else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
1105 {
1106 if (Argc < 3)
1107 {
1108 KdbpPrint("process attach: process id argument required!\n");
1109 return TRUE;
1110 }
1111
1112 ul = strtoul(Argv[2], &pend, 0);
1113 if (Argv[2] == pend)
1114 {
1115 KdbpPrint("process attach: '%s' is not a valid process id!\n", Argv[2]);
1116 return TRUE;
1117 }
1118 if (!KdbpAttachToProcess((PVOID)ul))
1119 {
1120 return TRUE;
1121 }
1122 KdbpPrint("Attached to process 0x%08x, thread 0x%08x.\n", (UINT)ul,
1123 (UINT)KdbCurrentThread->Cid.UniqueThread);
1124 }
1125 else
1126 {
1127 Process = KdbCurrentProcess;
1128
1129 if (Argc >= 2)
1130 {
1131 ul = strtoul(Argv[1], &pend, 0);
1132 if (Argv[1] == pend)
1133 {
1134 KdbpPrint("proc: '%s' is not a valid process id!\n", Argv[1]);
1135 return TRUE;
1136 }
1137 if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
1138 {
1139 KdbpPrint("proc: Invalid process id!\n");
1140 return TRUE;
1141 }
1142 }
1143
1144 State = ((Process->Pcb.State == PROCESS_STATE_TERMINATED) ? "Terminated" :
1145 ((Process->Pcb.State == PROCESS_STATE_ACTIVE) ? "Active" : "Unknown"));
1146 KdbpPrint("%s"
1147 " PID: 0x%08x\n"
1148 " State: %s (0x%x)\n"
1149 " Image Filename: %s\n",
1150 (Argc < 2) ? "Current process:\n" : "",
1151 Process->UniqueProcessId,
1152 State, Process->Pcb.State,
1153 Process->ImageFileName);
1154 }
1155
1156 return TRUE;
1157 }
1158
1159 /*!\brief Lists loaded modules or the one containing the specified address.
1160 */
1161 STATIC BOOLEAN
1162 KdbpCmdMod(ULONG Argc, PCHAR Argv[])
1163 {
1164 ULONGLONG Result = 0;
1165 ULONG_PTR Address;
1166 KDB_MODULE_INFO Info;
1167 BOOLEAN DisplayOnlyOneModule = FALSE;
1168 INT i = 0;
1169
1170 if (Argc >= 2)
1171 {
1172 /* Put the arguments back together */
1173 Argc--;
1174 while (--Argc >= 1)
1175 Argv[Argc][strlen(Argv[Argc])] = ' ';
1176
1177 /* Evaluate the expression */
1178 if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
1179 {
1180 return TRUE;
1181 }
1182 if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
1183 KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0]);
1184 Address = (ULONG_PTR)Result;
1185
1186 if (!KdbpSymFindModuleByAddress((PVOID)Address, &Info))
1187 {
1188 KdbpPrint("No module containing address 0x%x found!\n", Address);
1189 return TRUE;
1190 }
1191 DisplayOnlyOneModule = TRUE;
1192 }
1193 else
1194 {
1195 if (!KdbpSymFindModuleByIndex(0, &Info))
1196 {
1197 KdbpPrint("No modules.\n");
1198 return TRUE;
1199 }
1200 i = 1;
1201 }
1202
1203 KdbpPrint(" Base Size Name\n");
1204 for (;;)
1205 {
1206 KdbpPrint(" %08x %08x %ws\n", Info.Base, Info.Size, Info.Name);
1207
1208 if ((!DisplayOnlyOneModule && !KdbpSymFindModuleByIndex(i++, &Info)) ||
1209 DisplayOnlyOneModule)
1210 {
1211 break;
1212 }
1213 }
1214
1215 return TRUE;
1216 }
1217
1218 /*!\brief Displays GDT, LDT or IDTd.
1219 */
1220 STATIC BOOLEAN
1221 KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[])
1222 {
1223 struct __attribute__((packed)) {
1224 USHORT Limit;
1225 ULONG Base;
1226 } Reg;
1227 ULONG SegDesc[2];
1228 ULONG SegBase;
1229 ULONG SegLimit;
1230 PCHAR SegType;
1231 USHORT SegSel;
1232 UCHAR Type, Dpl;
1233 INT i;
1234 ULONG ul;
1235
1236 if (Argv[0][0] == 'i')
1237 {
1238 /* Read IDTR */
1239 asm volatile("sidt %0" : : "m"(Reg));
1240
1241 if (Reg.Limit < 7)
1242 {
1243 KdbpPrint("Interrupt descriptor table is empty.\n");
1244 return TRUE;
1245 }
1246 KdbpPrint("IDT Base: 0x%08x Limit: 0x%04x\n", Reg.Base, Reg.Limit);
1247 KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n");
1248 for (i = 0; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
1249 {
1250 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)(Reg.Base + i), sizeof(SegDesc))))
1251 {
1252 KdbpPrint("Couldn't access memory at 0x%08x!\n", Reg.Base + i);
1253 return TRUE;
1254 }
1255
1256 Dpl = ((SegDesc[1] >> 13) & 3);
1257 if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
1258 SegType = "TASKGATE";
1259 else if ((SegDesc[1] & 0x1fe0) == 0x0e00) /* 32 bit Interrupt gate */
1260 SegType = "INTGATE32";
1261 else if ((SegDesc[1] & 0x1fe0) == 0x0600) /* 16 bit Interrupt gate */
1262 SegType = "INTGATE16";
1263 else if ((SegDesc[1] & 0x1fe0) == 0x0f00) /* 32 bit Trap gate */
1264 SegType = "TRAPGATE32";
1265 else if ((SegDesc[1] & 0x1fe0) == 0x0700) /* 16 bit Trap gate */
1266 SegType = "TRAPGATE16";
1267 else
1268 SegType = "UNKNOWN";
1269
1270 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
1271 {
1272 KdbpPrint(" %03d %-10s [NP] [NP] %02d\n",
1273 i / 8, SegType, Dpl);
1274 }
1275 else if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
1276 {
1277 SegSel = SegDesc[0] >> 16;
1278 KdbpPrint(" %03d %-10s 0x%04x %02d\n",
1279 i / 8, SegType, SegSel, Dpl);
1280 }
1281 else
1282 {
1283 SegSel = SegDesc[0] >> 16;
1284 SegBase = (SegDesc[1] & 0xffff0000) | (SegDesc[0] & 0x0000ffff);
1285 KdbpPrint(" %03d %-10s 0x%04x 0x%08x %02d\n",
1286 i / 8, SegType, SegSel, SegBase, Dpl);
1287 }
1288 }
1289 }
1290 else
1291 {
1292 ul = 0;
1293 if (Argv[0][0] == 'g')
1294 {
1295 /* Read GDTR */
1296 asm volatile("sgdt %0" : : "m"(Reg));
1297 i = 8;
1298 }
1299 else
1300 {
1301 ASSERT(Argv[0][0] == 'l');
1302 /* Read LDTR */
1303 asm volatile("sldt %0" : : "m"(Reg));
1304 i = 0;
1305 ul = 1 << 2;
1306 }
1307
1308 if (Reg.Limit < 7)
1309 {
1310 KdbpPrint("%s descriptor table is empty.\n",
1311 Argv[0][0] == 'g' ? "Global" : "Local");
1312 return TRUE;
1313 }
1314 KdbpPrint("%cDT Base: 0x%08x Limit: 0x%04x\n",
1315 Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit);
1316 KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n");
1317 for ( ; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
1318 {
1319 if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)(Reg.Base + i), sizeof(SegDesc))))
1320 {
1321 KdbpPrint("Couldn't access memory at 0x%08x!\n", Reg.Base + i);
1322 return TRUE;
1323 }
1324 Dpl = ((SegDesc[1] >> 13) & 3);
1325 Type = ((SegDesc[1] >> 8) & 0xf);
1326
1327 SegBase = SegDesc[0] >> 16;
1328 SegBase |= (SegDesc[1] & 0xff) << 16;
1329 SegBase |= SegDesc[1] & 0xff000000;
1330 SegLimit = SegDesc[0] & 0x0000ffff;
1331 SegLimit |= (SegDesc[1] >> 16) & 0xf;
1332 if ((SegDesc[1] & (1 << 23)) != 0)
1333 {
1334 SegLimit *= 4096;
1335 SegLimit += 4095;
1336 }
1337 else
1338 {
1339 SegLimit++;
1340 }
1341
1342 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
1343 {
1344 switch (Type)
1345 {
1346 case 1: SegType = "TSS16(Avl)"; break;
1347 case 2: SegType = "LDT"; break;
1348 case 3: SegType = "TSS16(Busy)"; break;
1349 case 4: SegType = "CALLGATE16"; break;
1350 case 5: SegType = "TASKGATE"; break;
1351 case 6: SegType = "INTGATE16"; break;
1352 case 7: SegType = "TRAPGATE16"; break;
1353 case 9: SegType = "TSS32(Avl)"; break;
1354 case 11: SegType = "TSS32(Busy)"; break;
1355 case 12: SegType = "CALLGATE32"; break;
1356 case 14: SegType = "INTGATE32"; break;
1357 case 15: SegType = "INTGATE32"; break;
1358 default: SegType = "UNKNOWN"; break;
1359 }
1360 if (!(Type >= 1 && Type <= 3) &&
1361 Type != 9 && Type != 11)
1362 {
1363 SegBase = 0;
1364 SegLimit = 0;
1365 }
1366 }
1367 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
1368 {
1369 if ((SegDesc[1] & (1 << 22)) != 0)
1370 SegType = "DATA32";
1371 else
1372 SegType = "DATA16";
1373
1374 }
1375 else /* Code segment */
1376 {
1377 if ((SegDesc[1] & (1 << 22)) != 0)
1378 SegType = "CODE32";
1379 else
1380 SegType = "CODE16";
1381 }
1382
1383 if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
1384 {
1385 KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n",
1386 i / 8, i | Dpl | ul, SegType, Dpl);
1387 }
1388 else
1389 {
1390 KdbpPrint(" %03d 0x%04x %-11s 0x%08x 0x%08x %02d ",
1391 i / 8, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl);
1392 if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
1393 {
1394 /* FIXME: Display system segment */
1395 }
1396 else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
1397 {
1398 if ((SegDesc[1] & (1 << 10)) != 0) /* Expand-down */
1399 KdbpPrint(" E");
1400 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/W" : " R");
1401 if ((SegDesc[1] & (1 << 8)) != 0)
1402 KdbpPrint(" A");
1403 }
1404 else /* Code segment */
1405 {
1406 if ((SegDesc[1] & (1 << 10)) != 0) /* Conforming */
1407 KdbpPrint(" C");
1408 KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/X" : " X");
1409 if ((SegDesc[1] & (1 << 8)) != 0)
1410 KdbpPrint(" A");
1411 }
1412 if ((SegDesc[1] & (1 << 20)) != 0)
1413 KdbpPrint(" AVL");
1414 KdbpPrint("\n");
1415 }
1416 }
1417 }
1418
1419 return TRUE;
1420 }
1421
1422 /*!\brief Displays the KPCR
1423 */
1424 STATIC BOOLEAN
1425 KdbpCmdPcr(ULONG Argc, PCHAR Argv[])
1426 {
1427 PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
1428
1429 KdbpPrint("Current PCR is at 0x%08x.\n", (INT)Pcr);
1430 KdbpPrint(" Tib.ExceptionList: 0x%08x\n"
1431 " Tib.StackBase: 0x%08x\n"
1432 " Tib.StackLimit: 0x%08x\n"
1433 " Tib.SubSystemTib: 0x%08x\n"
1434 " Tib.FiberData/Version: 0x%08x\n"
1435 " Tib.ArbitraryUserPointer: 0x%08x\n"
1436 " Tib.Self: 0x%08x\n"
1437 " Self: 0x%08x\n"
1438 " PCRCB: 0x%08x\n"
1439 " Irql: 0x%02x\n"
1440 " IRR: 0x%08x\n"
1441 " IrrActive: 0x%08x\n"
1442 " IDR: 0x%08x\n"
1443 " KdVersionBlock: 0x%08x\n"
1444 " IDT: 0x%08x\n"
1445 " GDT: 0x%08x\n"
1446 " TSS: 0x%08x\n"
1447 " MajorVersion: 0x%04x\n"
1448 " MinorVersion: 0x%04x\n"
1449 " SetMember: 0x%08x\n"
1450 " StallScaleFactor: 0x%08x\n"
1451 " Number: 0x%02x\n"
1452 " L2CacheAssociativity: 0x%02x\n"
1453 " VdmAlert: 0x%08x\n"
1454 " L2CacheSize: 0x%08x\n"
1455 " InterruptMode: 0x%08x\n",
1456 Pcr->Tib.ExceptionList, Pcr->Tib.StackBase, Pcr->Tib.StackLimit,
1457 Pcr->Tib.SubSystemTib, Pcr->Tib.FiberData, Pcr->Tib.ArbitraryUserPointer,
1458 Pcr->Tib.Self, Pcr->Self, Pcr->Prcb, Pcr->Irql, Pcr->IRR, Pcr->IrrActive,
1459 Pcr->IDR, Pcr->KdVersionBlock, Pcr->IDT, Pcr->GDT, Pcr->TSS,
1460 Pcr->MajorVersion, Pcr->MinorVersion, Pcr->SetMember, Pcr->StallScaleFactor,
1461 Pcr->Number, Pcr->L2CacheAssociativity,
1462 Pcr->VdmAlert, Pcr->L2CacheSize, Pcr->InterruptMode);
1463
1464 return TRUE;
1465 }
1466
1467 /*!\brief Displays the TSS
1468 */
1469 STATIC BOOLEAN
1470 KdbpCmdTss(ULONG Argc, PCHAR Argv[])
1471 {
1472 KTSS *Tss = KeGetCurrentKPCR()->TSS;
1473
1474 KdbpPrint("Current TSS is at 0x%08x.\n", (INT)Tss);
1475 KdbpPrint(" PreviousTask: 0x%08x\n"
1476 " Ss0:Esp0: 0x%04x:0x%08x\n"
1477 " Ss1:Esp1: 0x%04x:0x%08x\n"
1478 " Ss2:Esp2: 0x%04x:0x%08x\n"
1479 " Cr3: 0x%08x\n"
1480 " Eip: 0x%08x\n"
1481 " Eflags: 0x%08x\n"
1482 " Eax: 0x%08x\n"
1483 " Ecx: 0x%08x\n"
1484 " Edx: 0x%08x\n"
1485 " Ebx: 0x%08x\n"
1486 " Esp: 0x%08x\n"
1487 " Ebp: 0x%08x\n"
1488 " Esi: 0x%08x\n"
1489 " Edi: 0x%08x\n"
1490 " Es: 0x%04x\n"
1491 " Cs: 0x%04x\n"
1492 " Ss: 0x%04x\n"
1493 " Ds: 0x%04x\n"
1494 " Fs: 0x%04x\n"
1495 " Gs: 0x%04x\n"
1496 " Ldt: 0x%04x\n"
1497 " Trap: 0x%04x\n"
1498 " IoMapBase: 0x%04x\n",
1499 Tss->PreviousTask, Tss->Ss0, Tss->Esp0, Tss->Ss1, Tss->Esp1,
1500 Tss->Ss2, Tss->Esp2, Tss->Cr3, Tss->Eip, Tss->Eflags, Tss->Eax,
1501 Tss->Ecx, Tss->Edx, Tss->Ebx, Tss->Esp, Tss->Ebp, Tss->Esi,
1502 Tss->Edi, Tss->Es, Tss->Cs, Tss->Ss, Tss->Ds, Tss->Fs, Tss->Gs,
1503 Tss->Ldt, Tss->Trap, Tss->IoMapBase);
1504 return TRUE;
1505 }
1506
1507 /*!\brief Bugchecks the system.
1508 */
1509 STATIC BOOLEAN
1510 KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[])
1511 {
1512 KEBUGCHECK(0xDEADDEAD);
1513 return TRUE;
1514 }
1515
1516 /*!\brief Sets or displays a config variables value.
1517 */
1518 STATIC BOOLEAN
1519 KdbpCmdSet(ULONG Argc, PCHAR Argv[])
1520 {
1521 LONG l;
1522 BOOLEAN First;
1523 PCHAR pend = 0;
1524 KDB_ENTER_CONDITION ConditionFirst = KdbDoNotEnter;
1525 KDB_ENTER_CONDITION ConditionLast = KdbDoNotEnter;
1526 STATIC CONST PCHAR ExceptionNames[21] =
1527 { "ZERODEVIDE", "DEBUGTRAP", "NMI", "INT3", "OVERFLOW", "BOUND", "INVALIDOP",
1528 "NOMATHCOP", "DOUBLEFAULT", "RESERVED(9)", "INVALIDTSS", "SEGMENTNOTPRESENT",
1529 "STACKFAULT", "GPF", "PAGEFAULT", "RESERVED(15)", "MATHFAULT", "ALIGNMENTCHECK",
1530 "MACHINECHECK", "SIMDFAULT", "OTHERS" };
1531
1532 if (Argc == 1)
1533 {
1534 KdbpPrint("Available settings:\n");
1535 KdbpPrint(" syntax [intel|at&t]\n");
1536 KdbpPrint(" condition [exception|*] [first|last] [never|always|kmode|umode]\n");
1537 }
1538 else if (strcmp(Argv[1], "syntax") == 0)
1539 {
1540 if (Argc == 2)
1541 KdbpPrint("syntax = %s\n", KdbUseIntelSyntax ? "intel" : "at&t");
1542 else if (Argc >= 3)
1543 {
1544 if (_stricmp(Argv[2], "intel") == 0)
1545 KdbUseIntelSyntax = TRUE;
1546 else if (_stricmp(Argv[2], "at&t") == 0)
1547 KdbUseIntelSyntax = FALSE;
1548 else
1549 KdbpPrint("Unknown syntax '%s'.\n", Argv[2]);
1550 }
1551 }
1552 else if (strcmp(Argv[1], "condition") == 0)
1553 {
1554 if (Argc == 2)
1555 {
1556 KdbpPrint("Conditions: (First) (Last)\n");
1557 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames) - 1; l++)
1558 {
1559 if (ExceptionNames[l] == NULL)
1560 continue;
1561 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
1562 ASSERT(0);
1563 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
1564 ASSERT(0);
1565 KdbpPrint(" #%02d %-20s %-8s %-8s\n", l, ExceptionNames[l],
1566 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
1567 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
1568 }
1569 ASSERT(l == (RTL_NUMBER_OF(ExceptionNames) - 1));
1570 KdbpPrint(" %-20s %-8s %-8s\n", ExceptionNames[l],
1571 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
1572 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
1573 }
1574 else
1575 {
1576 if (Argc >= 5 && strcmp(Argv[2], "*") == 0) /* Allow * only when setting condition */
1577 l = -1;
1578 else
1579 {
1580 l = (LONG)strtoul(Argv[2], &pend, 0);
1581 if (Argv[2] == pend)
1582 {
1583 for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++)
1584 {
1585 if (ExceptionNames[l] == NULL)
1586 continue;
1587 if (_stricmp(ExceptionNames[l], Argv[2]) == 0)
1588 break;
1589 }
1590 }
1591 if (l >= RTL_NUMBER_OF(ExceptionNames))
1592 {
1593 KdbpPrint("Unknown exception '%s'.\n", Argv[2]);
1594 return TRUE;
1595 }
1596 }
1597 if (Argc > 4)
1598 {
1599 if (_stricmp(Argv[3], "first") == 0)
1600 First = TRUE;
1601 else if (_stricmp(Argv[3], "last") == 0)
1602 First = FALSE;
1603 else
1604 {
1605 KdbpPrint("set condition: second argument must be 'first' or 'last'\n");
1606 return TRUE;
1607 }
1608 if (_stricmp(Argv[4], "never") == 0)
1609 ConditionFirst = KdbDoNotEnter;
1610 else if (_stricmp(Argv[4], "always") == 0)
1611 ConditionFirst = KdbEnterAlways;
1612 else if (_stricmp(Argv[4], "umode") == 0)
1613 ConditionFirst = KdbEnterFromUmode;
1614 else if (_stricmp(Argv[4], "kmode") == 0)
1615 ConditionFirst = KdbEnterFromKmode;
1616 else
1617 {
1618 KdbpPrint("set condition: third argument must be 'never', 'always', 'umode' or 'kmode'\n");
1619 return TRUE;
1620 }
1621 if (!KdbpSetEnterCondition(l, First, ConditionFirst))
1622 {
1623 if (l >= 0)
1624 KdbpPrint("Couldn't change condition for exception #%02d\n", l);
1625 else
1626 KdbpPrint("Couldn't change condition for all exceptions\n", l);
1627 }
1628 }
1629 else /* Argc >= 3 */
1630 {
1631 if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
1632 ASSERT(0);
1633 if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
1634 ASSERT(0);
1635 if (l < (RTL_NUMBER_OF(ExceptionNames) - 1))
1636 {
1637 KdbpPrint("Condition for exception #%02d (%s): FirstChance %s LastChance %s\n",
1638 l, ExceptionNames[l],
1639 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
1640 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
1641 }
1642 else
1643 {
1644 KdbpPrint("Condition for all other exceptions: FirstChance %s LastChance %s\n",
1645 KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
1646 KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
1647 }
1648 }
1649 }
1650 }
1651 else
1652 KdbpPrint("Unknown setting '%s'.\n", Argv[1]);
1653
1654 return TRUE;
1655 }
1656
1657 /*!\brief Displays help screen.
1658 */
1659 STATIC BOOLEAN
1660 KdbpCmdHelp(ULONG Argc, PCHAR Argv[])
1661 {
1662 ULONG i;
1663
1664 KdbpPrint("Kernel debugger commands:\n");
1665 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
1666 {
1667 if (KdbDebuggerCommands[i].Syntax == NULL) /* Command group */
1668 {
1669 if (i > 0)
1670 KdbpPrint("\n");
1671 KdbpPrint("\x1b[7m* %s:\x1b[0m\n", KdbDebuggerCommands[i].Help);
1672 continue;
1673 }
1674
1675 KdbpPrint(" %-20s - %s\n",
1676 KdbDebuggerCommands[i].Syntax,
1677 KdbDebuggerCommands[i].Help);
1678 }
1679
1680 return TRUE;
1681 }
1682
1683 /*!\brief Prints the given string with printf-like formatting.
1684 *
1685 * \param Format Format of the string/arguments.
1686 * \param ... Variable number of arguments matching the format specified in \a Format.
1687 *
1688 * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
1689 * number of lines required to print a single line from the Buffer in the terminal.
1690 */
1691 VOID
1692 KdbpPrint(
1693 IN PCHAR Format,
1694 IN ... OPTIONAL)
1695 {
1696 STATIC CHAR Buffer[4096];
1697 STATIC BOOLEAN TerminalInitialized = FALSE;
1698 STATIC BOOLEAN TerminalConnected = FALSE;
1699 STATIC BOOLEAN TerminalReportsSize = TRUE;
1700 CHAR c = '\0';
1701 PCHAR p, p2;
1702 INT Length;
1703 INT i, j;
1704 INT RowsPrintedByTerminal;
1705 ULONG ScanCode;
1706 va_list ap;
1707
1708 /* Check if the user has aborted output of the current command */
1709 if (KdbOutputAborted)
1710 return;
1711
1712 /* Initialize the terminal */
1713 if (!TerminalInitialized)
1714 {
1715 DbgPrint("\x1b[7h"); /* Enable linewrap */
1716
1717 /* Query terminal type */
1718 /*DbgPrint("\x1b[Z");*/
1719 DbgPrint("\x05");
1720
1721 TerminalInitialized = TRUE;
1722 Length = 0;
1723 for (;;)
1724 {
1725 c = KdbpTryGetCharSerial(5000);
1726 if (c == -1)
1727 break;
1728 Buffer[Length++] = c;
1729 if (Length >= (sizeof (Buffer) - 1))
1730 break;
1731 }
1732 Buffer[Length] = '\0';
1733 if (Length > 0)
1734 TerminalConnected = TRUE;
1735 }
1736
1737 /* Get number of rows and columns in terminal */
1738 if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
1739 (KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
1740 {
1741 if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalReportsSize)
1742 {
1743 /* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
1744 TerminalReportsSize = FALSE;
1745 DbgPrint("\x1b[18t");
1746 c = KdbpTryGetCharSerial(5000);
1747 if (c == KEY_ESC)
1748 {
1749 c = KdbpTryGetCharSerial(5000);
1750 if (c == '[')
1751 {
1752 Length = 0;
1753 for (;;)
1754 {
1755 c = KdbpTryGetCharSerial(5000);
1756 if (c == -1)
1757 break;
1758 Buffer[Length++] = c;
1759 if (isalpha(c) || Length >= (sizeof (Buffer) - 1))
1760 break;
1761 }
1762 Buffer[Length] = '\0';
1763 if (Buffer[0] == '8' && Buffer[1] == ';')
1764 {
1765 for (i = 2; (i < Length) && (Buffer[i] != ';'); i++);
1766 if (Buffer[i] == ';')
1767 {
1768 Buffer[i++] = '\0';
1769 /* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
1770 KdbNumberOfRowsTerminal = strtoul(Buffer + 2, NULL, 0);
1771 KdbNumberOfColsTerminal = strtoul(Buffer + i, NULL, 0);
1772 TerminalReportsSize = TRUE;
1773 }
1774 }
1775 }
1776 }
1777 }
1778
1779 if (KdbNumberOfRowsTerminal <= 0)
1780 {
1781 /* Set number of rows to the default. */
1782 KdbNumberOfRowsTerminal = 24;
1783 }
1784 else if (KdbNumberOfColsTerminal <= 0)
1785 {
1786 /* Set number of cols to the default. */
1787 KdbNumberOfColsTerminal = 80;
1788 }
1789 }
1790
1791 /* Get the string */
1792 va_start(ap, Format);
1793 Length = _vsnprintf(Buffer, sizeof (Buffer) - 1, Format, ap);
1794 Buffer[Length] = '\0';
1795 va_end(ap);
1796
1797 p = Buffer;
1798 while (p[0] != '\0')
1799 {
1800 i = strcspn(p, "\n");
1801
1802 /* Calculate the number of lines which will be printed in the terminal
1803 * when outputting the current line
1804 */
1805 if (i > 0)
1806 RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
1807 else
1808 RowsPrintedByTerminal = 0;
1809 if (p[i] == '\n')
1810 RowsPrintedByTerminal++;
1811
1812 /*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
1813
1814 /* Display a prompt if we printed one screen full of text */
1815 if ((KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
1816 {
1817 if (KdbNumberOfColsPrinted > 0)
1818 DbgPrint("\n");
1819 DbgPrint("--- Press q to abort, any other key to continue ---");
1820 if (KdbDebugState & KD_DEBUG_KDSERIAL)
1821 c = KdbpGetCharSerial();
1822 else
1823 c = KdbpGetCharKeyboard(&ScanCode);
1824 if (c == '\r')
1825 {
1826 /* Try to read '\n' which might follow '\r' - if \n is not received here
1827 * it will be interpreted as "return" when the next command should be read.
1828 */
1829 if (KdbDebugState & KD_DEBUG_KDSERIAL)
1830 c = KdbpTryGetCharSerial(5);
1831 else
1832 c = KdbpTryGetCharKeyboard(&ScanCode, 5);
1833 }
1834 DbgPrint("\n");
1835 if (c == 'q')
1836 {
1837 KdbOutputAborted = TRUE;
1838 return;
1839 }
1840 KdbNumberOfRowsPrinted = 0;
1841 KdbNumberOfColsPrinted = 0;
1842 }
1843
1844 /* Insert a NUL after the line and print only the current line. */
1845 if (p[i] == '\n' && p[i + 1] != '\0')
1846 {
1847 c = p[i + 1];
1848 p[i + 1] = '\0';
1849 }
1850 else
1851 {
1852 c = '\0';
1853 }
1854
1855 /* Remove escape sequences from the line if there's no terminal connected */
1856 if (!TerminalConnected)
1857 {
1858 while ((p2 = strrchr(p, '\x1b')) != NULL) /* Look for escape character */
1859 {
1860 if (p2[1] == '[')
1861 {
1862 j = 2;
1863 while (!isalpha(p2[j++]));
1864 strcpy(p2, p2 + j);
1865 }
1866 }
1867 }
1868
1869 DbgPrint("%s", p);
1870
1871 if (c != '\0')
1872 p[i + 1] = c;
1873
1874 /* Set p to the start of the next line and
1875 * remember the number of rows/cols printed
1876 */
1877 p += i;
1878 if (p[0] == '\n')
1879 {
1880 p++;
1881 KdbNumberOfColsPrinted = 0;
1882 }
1883 else
1884 {
1885 ASSERT(p[0] == '\0');
1886 KdbNumberOfColsPrinted += i;
1887 }
1888 KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
1889 }
1890 }
1891
1892 /*!\brief Appends a command to the command history
1893 *
1894 * \param Command Pointer to the command to append to the history.
1895 */
1896 STATIC VOID
1897 KdbpCommandHistoryAppend(
1898 IN PCHAR Command)
1899 {
1900 LONG Length1 = strlen(Command) + 1;
1901 LONG Length2 = 0;
1902 INT i;
1903 PCHAR Buffer;
1904
1905 ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer));
1906
1907 if (Length1 <= 1 ||
1908 (KdbCommandHistory[KdbCommandHistoryIndex] != NULL &&
1909 strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0))
1910 {
1911 return;
1912 }
1913
1914 /* Calculate Length1 and Length2 */
1915 Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex;
1916 KdbCommandHistoryBufferIndex += Length1;
1917 if (KdbCommandHistoryBufferIndex >= RTL_NUMBER_OF(KdbCommandHistoryBuffer))
1918 {
1919 KdbCommandHistoryBufferIndex -= RTL_NUMBER_OF(KdbCommandHistoryBuffer);
1920 Length2 = KdbCommandHistoryBufferIndex;
1921 Length1 -= Length2;
1922 }
1923
1924 /* Remove previous commands until there is enough space to append the new command */
1925 for (i = KdbCommandHistoryIndex; KdbCommandHistory[i] != NULL;)
1926 {
1927 if ((Length2 > 0 &&
1928 (KdbCommandHistory[i] >= Buffer ||
1929 KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))) ||
1930 (Length2 <= 0 &&
1931 (KdbCommandHistory[i] >= Buffer &&
1932 KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))))
1933 {
1934 KdbCommandHistory[i] = NULL;
1935 }
1936 i--;
1937 if (i < 0)
1938 i = RTL_NUMBER_OF(KdbCommandHistory) - 1;
1939 if (i == KdbCommandHistoryIndex)
1940 break;
1941 }
1942
1943 /* Make sure the new command history entry is free */
1944 KdbCommandHistoryIndex++;
1945 KdbCommandHistoryIndex %= RTL_NUMBER_OF(KdbCommandHistory);
1946 if (KdbCommandHistory[KdbCommandHistoryIndex] != NULL)
1947 {
1948 KdbCommandHistory[KdbCommandHistoryIndex] = NULL;
1949 }
1950
1951 /* Append command */
1952 KdbCommandHistory[KdbCommandHistoryIndex] = Buffer;
1953 ASSERT((KdbCommandHistory[KdbCommandHistoryIndex] + Length1) <= KdbCommandHistoryBuffer + RTL_NUMBER_OF(KdbCommandHistoryBuffer));
1954 memcpy(KdbCommandHistory[KdbCommandHistoryIndex], Command, Length1);
1955 if (Length2 > 0)
1956 {
1957 memcpy(KdbCommandHistoryBuffer, Command + Length1, Length2);
1958 }
1959 }
1960
1961 /*!\brief Reads a line of user-input.
1962 *
1963 * \param Buffer Buffer to store the input into. Trailing newlines are removed.
1964 * \param Size Size of \a Buffer.
1965 *
1966 * \note Accepts only \n newlines, \r is ignored.
1967 */
1968 STATIC VOID
1969 KdbpReadCommand(
1970 OUT PCHAR Buffer,
1971 IN ULONG Size)
1972 {
1973 CHAR Key;
1974 PCHAR Orig = Buffer;
1975 ULONG ScanCode = 0;
1976 BOOLEAN EchoOn;
1977 STATIC CHAR LastCommand[1024] = "";
1978 STATIC CHAR NextKey = '\0';
1979 INT CmdHistIndex = -1;
1980 INT i;
1981
1982 EchoOn = !((KdbDebugState & KD_DEBUG_KDNOECHO) != 0);
1983
1984 for (;;)
1985 {
1986 if (KdbDebugState & KD_DEBUG_KDSERIAL)
1987 {
1988 Key = (NextKey == '\0') ? KdbpGetCharSerial() : NextKey;
1989 NextKey = '\0';
1990 ScanCode = 0;
1991 if (Key == KEY_ESC) /* ESC */
1992 {
1993 Key = KdbpGetCharSerial();
1994 if (Key == '[')
1995 {
1996 Key = KdbpGetCharSerial();
1997 switch (Key)
1998 {
1999 case 'A':
2000 ScanCode = KEY_SCAN_UP;
2001 break;
2002 case 'B':
2003 ScanCode = KEY_SCAN_DOWN;
2004 break;
2005 case 'C':
2006 break;
2007 case 'D':
2008 break;
2009 }
2010 }
2011 }
2012 }
2013 else
2014 {
2015 ScanCode = 0;
2016 Key = (NextKey == '\0') ? KdbpGetCharKeyboard(&ScanCode) : NextKey;
2017 NextKey = '\0';
2018 }
2019
2020 if ((Buffer - Orig) >= (Size - 1))
2021 {
2022 /* Buffer is full, accept only newlines */
2023 if (Key != '\n')
2024 continue;
2025 }
2026
2027 if (Key == '\r')
2028 {
2029 /* Read the next char - this is to throw away a \n which most clients should
2030 * send after \r.
2031 */
2032 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2033 NextKey = KdbpTryGetCharSerial(5);
2034 else
2035 NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5);
2036 if (NextKey == '\n' || NextKey == -1) /* \n or no response at all */
2037 NextKey = '\0';
2038 DbgPrint("\n");
2039 /*
2040 * Repeat the last command if the user presses enter. Reduces the
2041 * risk of RSI when single-stepping.
2042 */
2043 if (Buffer == Orig)
2044 {
2045 strncpy(Buffer, LastCommand, Size);
2046 Buffer[Size - 1] = '\0';
2047 }
2048 else
2049 {
2050 *Buffer = '\0';
2051 strncpy(LastCommand, Orig, sizeof (LastCommand));
2052 LastCommand[sizeof (LastCommand) - 1] = '\0';
2053 }
2054 return;
2055 }
2056 else if (Key == KEY_BS || Key == KEY_DEL)
2057 {
2058 if (Buffer > Orig)
2059 {
2060 Buffer--;
2061 *Buffer = 0;
2062 if (EchoOn)
2063 DbgPrint("%c %c", KEY_BS, KEY_BS);
2064 else
2065 DbgPrint(" %c", KEY_BS);
2066 }
2067 }
2068 else if (ScanCode == KEY_SCAN_UP)
2069 {
2070 BOOLEAN Print = TRUE;
2071 if (CmdHistIndex < 0)
2072 CmdHistIndex = KdbCommandHistoryIndex;
2073 else
2074 {
2075 i = CmdHistIndex - 1;
2076 if (i < 0)
2077 CmdHistIndex = RTL_NUMBER_OF(KdbCommandHistory) - 1;
2078 if (KdbCommandHistory[i] != NULL && i != KdbCommandHistoryIndex)
2079 CmdHistIndex = i;
2080 else
2081 Print = FALSE;
2082 }
2083 if (Print && KdbCommandHistory[CmdHistIndex] != NULL)
2084 {
2085 while (Buffer > Orig)
2086 {
2087 Buffer--;
2088 *Buffer = 0;
2089 if (EchoOn)
2090 DbgPrint("%c %c", KEY_BS, KEY_BS);
2091 else
2092 DbgPrint(" %c", KEY_BS);
2093 }
2094 i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
2095 memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
2096 Orig[i] = '\0';
2097 Buffer = Orig + i;
2098 DbgPrint("%s", Orig);
2099 }
2100 }
2101 else if (ScanCode == KEY_SCAN_DOWN)
2102 {
2103 if (CmdHistIndex > 0 && CmdHistIndex != KdbCommandHistoryIndex)
2104 {
2105 i = CmdHistIndex + 1;
2106 if (i >= RTL_NUMBER_OF(KdbCommandHistory))
2107 i = 0;
2108 if (KdbCommandHistory[i] != NULL)
2109 {
2110 CmdHistIndex = i;
2111 while (Buffer > Orig)
2112 {
2113 Buffer--;
2114 *Buffer = 0;
2115 if (EchoOn)
2116 DbgPrint("%c %c", KEY_BS, KEY_BS);
2117 else
2118 DbgPrint(" %c", KEY_BS);
2119 }
2120 i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
2121 memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
2122 Orig[i] = '\0';
2123 Buffer = Orig + i;
2124 DbgPrint("%s", Orig);
2125 }
2126 }
2127 }
2128 else
2129 {
2130 if (EchoOn)
2131 DbgPrint("%c", Key);
2132
2133 *Buffer = Key;
2134 Buffer++;
2135 }
2136 }
2137 }
2138
2139 /*!\brief Parses command line and executes command if found
2140 *
2141 * \param Command Command line to parse and execute if possible.
2142 *
2143 * \retval TRUE Don't continue execution.
2144 * \retval FALSE Continue execution (leave KDB)
2145 */
2146 STATIC BOOL
2147 KdbpDoCommand(
2148 IN PCHAR Command)
2149 {
2150 ULONG i;
2151 PCHAR p;
2152 ULONG Argc;
2153 STATIC PCH Argv[256];
2154 STATIC CHAR OrigCommand[1024];
2155
2156 strncpy(OrigCommand, Command, sizeof(OrigCommand) - 1);
2157 OrigCommand[sizeof(OrigCommand) - 1] = '\0';
2158
2159 Argc = 0;
2160 p = Command;
2161 for (;;)
2162 {
2163 while (*p == '\t' || *p == ' ')
2164 p++;
2165 if (*p == '\0')
2166 break;
2167
2168 i = strcspn(p, "\t ");
2169 Argv[Argc++] = p;
2170 p += i;
2171 if (*p == '\0')
2172 break;
2173 *p = '\0';
2174 p++;
2175 }
2176 if (Argc < 1)
2177 return TRUE;
2178
2179 for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
2180 {
2181 if (KdbDebuggerCommands[i].Name == NULL)
2182 continue;
2183
2184 if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0)
2185 {
2186 return KdbDebuggerCommands[i].Fn(Argc, Argv);
2187 }
2188 }
2189
2190 KdbpPrint("Command '%s' is unknown.\n", OrigCommand);
2191 return TRUE;
2192 }
2193
2194 /*!\brief KDB Main Loop.
2195 *
2196 * \param EnteredOnSingleStep TRUE if KDB was entered on single step.
2197 */
2198 VOID
2199 KdbpCliMainLoop(
2200 IN BOOLEAN EnteredOnSingleStep)
2201 {
2202 STATIC CHAR Command[1024];
2203 BOOLEAN Continue;
2204
2205 if (EnteredOnSingleStep)
2206 {
2207 if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
2208 {
2209 DbgPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip);
2210 }
2211 DbgPrint(": ");
2212 if (KdbpDisassemble(KdbCurrentTrapFrame->Tf.Eip, KdbUseIntelSyntax) < 0)
2213 {
2214 DbgPrint("<INVALID>");
2215 }
2216 DbgPrint("\n");
2217 }
2218
2219 /* Flush the input buffer */
2220 if (KdbDebugState & KD_DEBUG_KDSERIAL)
2221 {
2222 while (KdbpTryGetCharSerial(1) != -1);
2223 }
2224 else
2225 {
2226 ULONG ScanCode;
2227 while (KdbpTryGetCharKeyboard(&ScanCode, 1) != -1);
2228 }
2229
2230 /* Main loop */
2231 do
2232 {
2233 /* Print the prompt */
2234 DbgPrint("kdb:> ");
2235
2236 /* Read a command and remember it */
2237 KdbpReadCommand(Command, sizeof (Command));
2238 KdbpCommandHistoryAppend(Command);
2239
2240 /* Reset the number of rows/cols printed and output aborted state */
2241 KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
2242 KdbOutputAborted = FALSE;
2243
2244 /* Call the command */
2245 Continue = KdbpDoCommand(Command);
2246 } while (Continue);
2247 }
2248
2249 /*!\brief Called when a module is loaded.
2250 *
2251 * \param Name Filename of the module which was loaded.
2252 */
2253 VOID
2254 KdbpCliModuleLoaded(IN PUNICODE_STRING Name)
2255 {
2256 return;
2257
2258 DbgPrint("Module %wZ loaded.\n", Name);
2259 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
2260 }
2261
2262 /*!\brief This function is called by KdbEnterDebuggerException...
2263 *
2264 * Used to interpret the init file in a context with a trapframe setup
2265 * (KdbpCliInit call KdbEnter which will call KdbEnterDebuggerException which will
2266 * call this function if KdbInitFileBuffer is not NULL.
2267 */
2268 VOID
2269 KdbpCliInterpretInitFile()
2270 {
2271 PCHAR p1, p2;
2272 INT i;
2273 CHAR c;
2274
2275 /* Execute the commands in the init file */
2276 DPRINT("KDB: Executing KDBinit file...\n");
2277 p1 = KdbInitFileBuffer;
2278 while (p1[0] != '\0')
2279 {
2280 i = strcspn(p1, "\r\n");
2281 if (i > 0)
2282 {
2283 c = p1[i];
2284 p1[i] = '\0';
2285
2286 /* Look for "break" command and comments */
2287 p2 = p1;
2288 while (isspace(p2[0]))
2289 p2++;
2290 if (strncmp(p2, "break", sizeof("break")-1) == 0 &&
2291 (p2[sizeof("break")-1] == '\0' || isspace(p2[sizeof("break")-1])))
2292 {
2293 /* break into the debugger */
2294 KdbpCliMainLoop(FALSE);
2295 }
2296 else if (p2[0] != '#' && p2[0] != '\0') /* Ignore empty lines and comments */
2297 {
2298 KdbpDoCommand(p1);
2299 }
2300
2301 p1[i] = c;
2302 }
2303 p1 += i;
2304 while (p1[0] == '\r' || p1[0] == '\n')
2305 p1++;
2306 }
2307 DPRINT("KDB: KDBinit executed\n");
2308 }
2309
2310 /*!\brief Called when KDB is initialized
2311 *
2312 * Reads the KDBinit file from the SystemRoot\system32\drivers\etc directory and executes it.
2313 */
2314 VOID
2315 KdbpCliInit()
2316 {
2317 NTSTATUS Status;
2318 OBJECT_ATTRIBUTES ObjectAttributes;
2319 UNICODE_STRING FileName;
2320 IO_STATUS_BLOCK Iosb;
2321 FILE_STANDARD_INFORMATION FileStdInfo;
2322 HANDLE hFile = NULL;
2323 INT FileSize;
2324 PCHAR FileBuffer;
2325 ULONG OldEflags;
2326
2327 /* Initialize the object attributes */
2328 RtlInitUnicodeString(&FileName, L"\\SystemRoot\\system32\\drivers\\etc\\KDBinit");
2329 InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL);
2330
2331 /* Open the file */
2332 Status = ZwOpenFile(&hFile, FILE_READ_DATA, &ObjectAttributes, &Iosb, 0,
2333 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
2334 FILE_NO_INTERMEDIATE_BUFFERING);
2335 if (!NT_SUCCESS(Status))
2336 {
2337 DPRINT("Could not open \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
2338 return;
2339 }
2340
2341 /* Get the size of the file */
2342 Status = ZwQueryInformationFile(hFile, &Iosb, &FileStdInfo, sizeof (FileStdInfo),
2343 FileStandardInformation);
2344 if (!NT_SUCCESS(Status))
2345 {
2346 ZwClose(hFile);
2347 DPRINT("Could not query size of \\SystemRoot\\system32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
2348 return;
2349 }
2350 FileSize = FileStdInfo.EndOfFile.u.LowPart;
2351
2352 /* Allocate memory for the file */
2353 FileBuffer = ExAllocatePool(PagedPool, FileSize + 1); /* add 1 byte for terminating '\0' */
2354 if (FileBuffer == NULL)
2355 {
2356 ZwClose(hFile);
2357 DPRINT("Could not allocate %d bytes for KDBinit file\n", FileSize);
2358 return;
2359 }
2360
2361 /* Load file into memory */
2362 Status = ZwReadFile(hFile, 0, 0, 0, &Iosb, FileBuffer, FileSize, 0, 0);
2363 ZwClose(hFile);
2364 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
2365 {
2366 ExFreePool(FileBuffer);
2367 DPRINT("Could not read KDBinit file into memory (Status 0x%lx)\n", Status);
2368 return;
2369 }
2370 FileSize = min(FileSize, Iosb.Information);
2371 FileBuffer[FileSize] = '\0';
2372
2373 /* Enter critical section */
2374 Ke386SaveFlags(OldEflags);
2375 Ke386DisableInterrupts();
2376
2377 /* Interpret the init file... */
2378 KdbInitFileBuffer = FileBuffer;
2379 KdbEnter();
2380 KdbInitFileBuffer = NULL;
2381
2382 /* Leave critical section */
2383 Ke386RestoreFlags(OldEflags);
2384
2385 ExFreePool(FileBuffer);
2386 }
2387