1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel 4 * FILE: ntoskrnl/kd/kdmain.c 5 * PURPOSE: Kernel Debugger Initialization 6 * 7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 8 */ 9 10 #include <ntoskrnl.h> 11 #define NDEBUG 12 #include <debug.h> 13 14 /* VARIABLES ***************************************************************/ 15 16 BOOLEAN KdDebuggerEnabled = FALSE; 17 BOOLEAN KdEnteredDebugger = FALSE; 18 BOOLEAN KdDebuggerNotPresent = TRUE; 19 BOOLEAN KdBreakAfterSymbolLoad = FALSE; 20 BOOLEAN KdpBreakPending = FALSE; 21 BOOLEAN KdPitchDebugger = TRUE; 22 BOOLEAN KdIgnoreUmExceptions = FALSE; 23 KD_CONTEXT KdpContext; 24 ULONG Kd_WIN2000_Mask; 25 VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads); 26 27 typedef struct 28 { 29 ULONG ComponentId; 30 ULONG Level; 31 } KD_COMPONENT_DATA; 32 #define MAX_KD_COMPONENT_TABLE_ENTRIES 128 33 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES]; 34 ULONG KdComponentTableEntries = 0; 35 36 ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL; 37 38 /* PRIVATE FUNCTIONS *********************************************************/ 39 40 ULONG 41 NTAPI 42 KdpServiceDispatcher(ULONG Service, 43 PVOID Buffer1, 44 ULONG Buffer1Length, 45 KPROCESSOR_MODE PreviousMode) 46 { 47 ULONG Result = 0; 48 49 switch (Service) 50 { 51 case BREAKPOINT_PRINT: /* DbgPrint */ 52 Result = KdpPrintString(Buffer1, Buffer1Length, PreviousMode); 53 break; 54 55 #if DBG 56 case ' soR': /* ROS-INTERNAL */ 57 { 58 switch ((ULONG_PTR)Buffer1) 59 { 60 case DumpAllThreads: 61 PspDumpThreads(TRUE); 62 break; 63 64 case DumpUserThreads: 65 PspDumpThreads(FALSE); 66 break; 67 68 case KdSpare3: 69 MmDumpArmPfnDatabase(FALSE); 70 break; 71 72 default: 73 break; 74 } 75 break; 76 } 77 78 #if defined(_M_IX86) && !defined(_WINKD_) // See ke/i386/traphdlr.c 79 /* Register a debug callback */ 80 case 'CsoR': 81 { 82 switch (Buffer1Length) 83 { 84 case ID_Win32PreServiceHook: 85 KeWin32PreServiceHook = Buffer1; 86 break; 87 88 case ID_Win32PostServiceHook: 89 KeWin32PostServiceHook = Buffer1; 90 break; 91 92 } 93 break; 94 } 95 #endif 96 97 /* Special case for stack frame dumps */ 98 case 'DsoR': 99 { 100 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length); 101 break; 102 } 103 104 #if defined(KDBG) 105 /* Register KDBG CLI callback */ 106 case 'RbdK': 107 { 108 Result = KdbRegisterCliCallback(Buffer1, Buffer1Length); 109 break; 110 } 111 #endif /* KDBG */ 112 #endif /* DBG */ 113 default: 114 DPRINT1("Invalid debug service call!\n"); 115 HalDisplayString("Invalid debug service call!\r\n"); 116 break; 117 } 118 119 return Result; 120 } 121 122 BOOLEAN 123 NTAPI 124 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame, 125 IN PKEXCEPTION_FRAME ExceptionFrame, 126 IN PEXCEPTION_RECORD ExceptionRecord, 127 IN PCONTEXT Context, 128 IN KPROCESSOR_MODE PreviousMode, 129 IN BOOLEAN SecondChance) 130 { 131 KD_CONTINUE_TYPE Return = kdHandleException; 132 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0]; 133 134 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */ 135 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 136 (ExceptionRecord->NumberParameters > 0) && 137 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || 138 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || 139 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || 140 (ExceptionCommand == BREAKPOINT_PRINT) || 141 (ExceptionCommand == BREAKPOINT_PROMPT))) 142 { 143 /* Check if this is a debug print */ 144 if (ExceptionCommand == BREAKPOINT_PRINT) 145 { 146 /* Print the string */ 147 KdpServiceDispatcher(BREAKPOINT_PRINT, 148 (PVOID)ExceptionRecord->ExceptionInformation[1], 149 ExceptionRecord->ExceptionInformation[2], 150 PreviousMode); 151 152 /* Return success */ 153 KeSetContextReturnRegister(Context, STATUS_SUCCESS); 154 } 155 #ifdef KDBG 156 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) 157 { 158 PKD_SYMBOLS_INFO SymbolsInfo; 159 KD_SYMBOLS_INFO CapturedSymbolsInfo; 160 PLDR_DATA_TABLE_ENTRY LdrEntry; 161 162 SymbolsInfo = (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2]; 163 if (PreviousMode != KernelMode) 164 { 165 _SEH2_TRY 166 { 167 ProbeForRead(SymbolsInfo, 168 sizeof(*SymbolsInfo), 169 1); 170 RtlCopyMemory(&CapturedSymbolsInfo, 171 SymbolsInfo, 172 sizeof(*SymbolsInfo)); 173 SymbolsInfo = &CapturedSymbolsInfo; 174 } 175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 176 { 177 SymbolsInfo = NULL; 178 } 179 _SEH2_END; 180 } 181 182 if (SymbolsInfo != NULL) 183 { 184 /* Load symbols. Currently implemented only for KDBG! */ 185 if (KdbpSymFindModule(SymbolsInfo->BaseOfDll, NULL, -1, &LdrEntry)) 186 { 187 KdbSymProcessSymbols(LdrEntry); 188 } 189 } 190 } 191 else if (ExceptionCommand == BREAKPOINT_PROMPT) 192 { 193 ULONG ReturnValue; 194 LPSTR OutString; 195 USHORT OutStringLength; 196 197 /* Get the response string and length */ 198 OutString = (LPSTR)Context->Ebx; 199 OutStringLength = (USHORT)Context->Edi; 200 201 /* Call KDBG */ 202 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord-> 203 ExceptionInformation[1], 204 (USHORT)ExceptionRecord-> 205 ExceptionInformation[2], 206 OutString, 207 OutStringLength, 208 PreviousMode); 209 210 /* Return the number of characters that we received */ 211 Context->Eax = ReturnValue; 212 } 213 #endif 214 215 /* This we can handle: simply bump the Program Counter */ 216 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE); 217 return TRUE; 218 } 219 220 #ifdef KDBG 221 /* Check if this is an assertion failure */ 222 if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE) 223 { 224 /* Bump EIP to the instruction following the int 2C */ 225 Context->Eip += 2; 226 } 227 #endif 228 229 /* Get out of here if the Debugger isn't connected */ 230 if (KdDebuggerNotPresent) return FALSE; 231 232 #ifdef KDBG 233 /* Call KDBG if available */ 234 Return = KdbEnterDebuggerException(ExceptionRecord, 235 PreviousMode, 236 Context, 237 TrapFrame, 238 !SecondChance); 239 #else /* not KDBG */ 240 if (WrapperInitRoutine) 241 { 242 /* Call GDB */ 243 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord, 244 Context, 245 TrapFrame); 246 } 247 #endif /* not KDBG */ 248 249 /* Debugger didn't handle it, please handle! */ 250 if (Return == kdHandleException) return FALSE; 251 252 /* Debugger handled it */ 253 return TRUE; 254 } 255 256 BOOLEAN 257 NTAPI 258 KdpCallGdb(IN PKTRAP_FRAME TrapFrame, 259 IN PEXCEPTION_RECORD ExceptionRecord, 260 IN PCONTEXT Context) 261 { 262 KD_CONTINUE_TYPE Return = kdDoNotHandleException; 263 264 /* Get out of here if the Debugger isn't connected */ 265 if (KdDebuggerNotPresent) return FALSE; 266 267 /* FIXME: 268 * Right now, the GDB wrapper seems to handle exceptions differntly 269 * from KDGB and both are called at different times, while the GDB 270 * one is only called once and that's it. I don't really have the knowledge 271 * to fix the GDB stub, so until then, we'll be using this hack 272 */ 273 if (WrapperInitRoutine) 274 { 275 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord, 276 Context, 277 TrapFrame); 278 } 279 280 /* Debugger didn't handle it, please handle! */ 281 if (Return == kdHandleException) return FALSE; 282 283 /* Debugger handled it */ 284 return TRUE; 285 } 286 287 BOOLEAN 288 NTAPI 289 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, 290 IN PCONTEXT Context, 291 IN KPROCESSOR_MODE PreviousMode) 292 { 293 /* KDBG has its own mechanism for ignoring user mode exceptions */ 294 return FALSE; 295 } 296 297 /* PUBLIC FUNCTIONS *********************************************************/ 298 299 /* 300 * @implemented 301 */ 302 BOOLEAN 303 NTAPI 304 KdRefreshDebuggerNotPresent(VOID) 305 { 306 UNIMPLEMENTED; 307 308 /* Just return whatever was set previously -- FIXME! */ 309 return KdDebuggerNotPresent; 310 } 311 312 /* 313 * @implemented 314 */ 315 NTSTATUS 316 NTAPI 317 KdDisableDebugger(VOID) 318 { 319 KIRQL OldIrql; 320 321 /* Raise IRQL */ 322 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 323 324 /* TODO: Disable any breakpoints */ 325 326 /* Disable the Debugger */ 327 KdDebuggerEnabled = FALSE; 328 SharedUserData->KdDebuggerEnabled = FALSE; 329 330 /* Lower the IRQL */ 331 KeLowerIrql(OldIrql); 332 333 /* Return success */ 334 return STATUS_SUCCESS; 335 } 336 337 /* 338 * @implemented 339 */ 340 NTSTATUS 341 NTAPI 342 KdEnableDebugger(VOID) 343 { 344 KIRQL OldIrql; 345 346 /* Raise IRQL */ 347 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 348 349 /* TODO: Re-enable any breakpoints */ 350 351 /* Enable the Debugger */ 352 KdDebuggerEnabled = TRUE; 353 SharedUserData->KdDebuggerEnabled = TRUE; 354 355 /* Lower the IRQL */ 356 KeLowerIrql(OldIrql); 357 358 /* Return success */ 359 return STATUS_SUCCESS; 360 } 361 362 /* 363 * @implemented 364 */ 365 BOOLEAN 366 NTAPI 367 KdPollBreakIn(VOID) 368 { 369 return KdpBreakPending; 370 } 371 372 /* 373 * @unimplemented 374 */ 375 NTSTATUS 376 NTAPI 377 KdPowerTransition(ULONG PowerState) 378 { 379 UNIMPLEMENTED; 380 return STATUS_NOT_IMPLEMENTED; 381 } 382 383 /* 384 * @unimplemented 385 */ 386 NTSTATUS 387 NTAPI 388 KdChangeOption(IN KD_OPTION Option, 389 IN ULONG InBufferLength OPTIONAL, 390 IN PVOID InBuffer, 391 IN ULONG OutBufferLength OPTIONAL, 392 OUT PVOID OutBuffer, 393 OUT PULONG OutBufferRequiredLength OPTIONAL) 394 { 395 UNIMPLEMENTED; 396 return STATUS_NOT_IMPLEMENTED; 397 } 398 399 400 NTSTATUS 401 NTAPI 402 NtQueryDebugFilterState(IN ULONG ComponentId, 403 IN ULONG Level) 404 { 405 ULONG i; 406 407 /* Convert Level to mask if it isn't already one */ 408 if (Level < 32) 409 Level = 1 << Level; 410 411 /* Check if it is not the default component */ 412 if (ComponentId != MAXULONG) 413 { 414 /* No, search for an existing entry in the table */ 415 for (i = 0; i < KdComponentTableEntries; i++) 416 { 417 /* Check if it is the right component */ 418 if (ComponentId == KdComponentTable[i].ComponentId) 419 { 420 /* Check if mask are matching */ 421 return (Level & KdComponentTable[i].Level) ? TRUE : FALSE; 422 } 423 } 424 } 425 426 /* Entry not found in the table, use default mask */ 427 return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE; 428 } 429 430 NTSTATUS 431 NTAPI 432 NtSetDebugFilterState(IN ULONG ComponentId, 433 IN ULONG Level, 434 IN BOOLEAN State) 435 { 436 ULONG i; 437 438 /* Convert Level to mask if it isn't already one */ 439 if (Level < 32) 440 Level = 1 << Level; 441 Level &= ~DPFLTR_MASK; 442 443 /* Check if it is the default component */ 444 if (ComponentId == MAXULONG) 445 { 446 /* Yes, modify the default mask */ 447 if (State) 448 Kd_DEFAULT_MASK |= Level; 449 else 450 Kd_DEFAULT_MASK &= ~Level; 451 452 return STATUS_SUCCESS; 453 } 454 455 /* Search for an existing entry */ 456 for (i = 0; i < KdComponentTableEntries; i++ ) 457 { 458 if (ComponentId == KdComponentTable[i].ComponentId) 459 break; 460 } 461 462 /* Check if we have found an existing entry */ 463 if (i == KdComponentTableEntries) 464 { 465 /* Check if we have enough space in the table */ 466 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES) 467 return STATUS_INVALID_PARAMETER_1; 468 469 /* Add a new entry */ 470 ++KdComponentTableEntries; 471 KdComponentTable[i].ComponentId = ComponentId; 472 KdComponentTable[i].Level = Kd_DEFAULT_MASK; 473 } 474 475 /* Update entry table */ 476 if (State) 477 KdComponentTable[i].Level |= Level; 478 else 479 KdComponentTable[i].Level &= ~Level; 480 481 return STATUS_SUCCESS; 482 } 483 484 /* 485 * @unimplemented 486 */ 487 NTSTATUS 488 NTAPI 489 KdSystemDebugControl(IN SYSDBG_COMMAND Command, 490 IN PVOID InputBuffer, 491 IN ULONG InputBufferLength, 492 OUT PVOID OutputBuffer, 493 IN ULONG OutputBufferLength, 494 IN OUT PULONG ReturnLength, 495 IN KPROCESSOR_MODE PreviousMode) 496 { 497 /* HACK */ 498 return KdpServiceDispatcher(Command, 499 InputBuffer, 500 InputBufferLength, 501 PreviousMode); 502 } 503 504 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException; 505 506 /* EOF */ 507