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