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