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