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