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