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