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