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