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