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