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