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