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