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