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