[STOBJECT] Change eject hardware dialog buttons from OK/Cancel to just OK. (#2493)
[reactos.git] / ntoskrnl / kdbg / kdb_cli.c
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(