1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/kd64/kdprint.c 5 * PURPOSE: KD64 Trap Handler Routines 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Stefan Ginsberg (stefan.ginsberg@reactos.org) 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 #define KD_PRINT_MAX_BYTES 512 17 18 /* FUNCTIONS *****************************************************************/ 19 20 KIRQL 21 NTAPI 22 KdpAcquireLock( 23 _In_ PKSPIN_LOCK SpinLock) 24 { 25 KIRQL OldIrql; 26 27 /* Acquire the spinlock without waiting at raised IRQL */ 28 while (TRUE) 29 { 30 /* Loop until the spinlock becomes available */ 31 while (!KeTestSpinLock(SpinLock)); 32 33 /* Spinlock is free, raise IRQL to high level */ 34 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 35 36 /* Try to get the spinlock */ 37 if (KeTryToAcquireSpinLockAtDpcLevel(SpinLock)) 38 break; 39 40 /* Someone else got the spinlock, lower IRQL back */ 41 KeLowerIrql(OldIrql); 42 } 43 44 return OldIrql; 45 } 46 47 VOID 48 NTAPI 49 KdpReleaseLock( 50 _In_ PKSPIN_LOCK SpinLock, 51 _In_ KIRQL OldIrql) 52 { 53 /* Release the spinlock */ 54 KiReleaseSpinLock(SpinLock); 55 // KeReleaseSpinLockFromDpcLevel(SpinLock); 56 57 /* Restore the old IRQL */ 58 KeLowerIrql(OldIrql); 59 } 60 61 VOID 62 NTAPI 63 KdLogDbgPrint( 64 _In_ PSTRING String) 65 { 66 SIZE_T Length, Remaining; 67 KIRQL OldIrql; 68 69 /* If the string is empty, bail out */ 70 if (!String->Buffer || (String->Length == 0)) 71 return; 72 73 /* If no log buffer available, bail out */ 74 if (!KdPrintCircularBuffer /*|| (KdPrintBufferSize == 0)*/) 75 return; 76 77 /* Acquire the log spinlock without waiting at raised IRQL */ 78 OldIrql = KdpAcquireLock(&KdpPrintSpinLock); 79 80 Length = min(String->Length, KdPrintBufferSize); 81 Remaining = KdPrintCircularBuffer + KdPrintBufferSize - KdPrintWritePointer; 82 83 if (Length < Remaining) 84 { 85 KdpMoveMemory(KdPrintWritePointer, String->Buffer, Length); 86 KdPrintWritePointer += Length; 87 } 88 else 89 { 90 KdpMoveMemory(KdPrintWritePointer, String->Buffer, Remaining); 91 Length -= Remaining; 92 if (Length > 0) 93 KdpMoveMemory(KdPrintCircularBuffer, String->Buffer + Remaining, Length); 94 95 KdPrintWritePointer = KdPrintCircularBuffer + Length; 96 97 /* Got a rollover, update count (handle wrapping, must always be >= 1) */ 98 ++KdPrintRolloverCount; 99 if (KdPrintRolloverCount == 0) 100 ++KdPrintRolloverCount; 101 } 102 103 /* Release the spinlock */ 104 KdpReleaseLock(&KdpPrintSpinLock, OldIrql); 105 } 106 107 BOOLEAN 108 NTAPI 109 KdpPrintString( 110 _In_ PSTRING Output) 111 { 112 STRING Data, Header; 113 DBGKD_DEBUG_IO DebugIo; 114 USHORT Length; 115 116 /* Copy the string */ 117 KdpMoveMemory(KdpMessageBuffer, 118 Output->Buffer, 119 Output->Length); 120 121 /* Make sure we don't exceed the KD Packet size */ 122 Length = Output->Length; 123 if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE) 124 { 125 /* Normalize length */ 126 Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO); 127 } 128 129 /* Build the packet header */ 130 DebugIo.ApiNumber = DbgKdPrintStringApi; 131 DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel; 132 DebugIo.Processor = KeGetCurrentPrcb()->Number; 133 DebugIo.u.PrintString.LengthOfString = Length; 134 Header.Length = sizeof(DBGKD_DEBUG_IO); 135 Header.Buffer = (PCHAR)&DebugIo; 136 137 /* Build the data */ 138 Data.Length = Length; 139 Data.Buffer = KdpMessageBuffer; 140 141 /* Send the packet */ 142 KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext); 143 144 /* Check if the user pressed CTRL+C */ 145 return KdpPollBreakInWithPortLock(); 146 } 147 148 BOOLEAN 149 NTAPI 150 KdpPromptString( 151 _In_ PSTRING PromptString, 152 _In_ PSTRING ResponseString) 153 { 154 STRING Data, Header; 155 DBGKD_DEBUG_IO DebugIo; 156 ULONG Length; 157 KDSTATUS Status; 158 159 /* Copy the string to the message buffer */ 160 KdpMoveMemory(KdpMessageBuffer, 161 PromptString->Buffer, 162 PromptString->Length); 163 164 /* Make sure we don't exceed the KD Packet size */ 165 Length = PromptString->Length; 166 if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE) 167 { 168 /* Normalize length */ 169 Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO); 170 } 171 172 /* Build the packet header */ 173 DebugIo.ApiNumber = DbgKdGetStringApi; 174 DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel; 175 DebugIo.Processor = KeGetCurrentPrcb()->Number; 176 DebugIo.u.GetString.LengthOfPromptString = Length; 177 DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength; 178 Header.Length = sizeof(DBGKD_DEBUG_IO); 179 Header.Buffer = (PCHAR)&DebugIo; 180 181 /* Build the data */ 182 Data.Length = Length; 183 Data.Buffer = KdpMessageBuffer; 184 185 /* Send the packet */ 186 KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext); 187 188 /* Set the maximum lengths for the receive */ 189 Header.MaximumLength = sizeof(DBGKD_DEBUG_IO); 190 Data.MaximumLength = sizeof(KdpMessageBuffer); 191 192 /* Enter receive loop */ 193 do 194 { 195 /* Get our reply */ 196 Status = KdReceivePacket(PACKET_TYPE_KD_DEBUG_IO, 197 &Header, 198 &Data, 199 &Length, 200 &KdpContext); 201 202 /* Return TRUE if we need to resend */ 203 if (Status == KdPacketNeedsResend) return TRUE; 204 205 /* Loop until we succeed */ 206 } while (Status != KdPacketReceived); 207 208 /* Don't copy back a larger response than there is room for */ 209 Length = min(Length, 210 ResponseString->MaximumLength); 211 212 /* Copy back the string and return the length */ 213 KdpMoveMemory(ResponseString->Buffer, 214 KdpMessageBuffer, 215 Length); 216 ResponseString->Length = (USHORT)Length; 217 218 /* Success; we don't need to resend */ 219 return FALSE; 220 } 221 222 VOID 223 NTAPI 224 KdpCommandString(IN PSTRING NameString, 225 IN PSTRING CommandString, 226 IN KPROCESSOR_MODE PreviousMode, 227 IN PCONTEXT ContextRecord, 228 IN PKTRAP_FRAME TrapFrame, 229 IN PKEXCEPTION_FRAME ExceptionFrame) 230 { 231 BOOLEAN Enable; 232 PKPRCB Prcb = KeGetCurrentPrcb(); 233 234 /* Check if we need to do anything */ 235 if ((PreviousMode != KernelMode) || (KdDebuggerNotPresent)) return; 236 237 /* Enter the debugger */ 238 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); 239 240 /* Save the CPU Control State and save the context */ 241 KiSaveProcessorControlState(&Prcb->ProcessorState); 242 KdpMoveMemory(&Prcb->ProcessorState.ContextFrame, 243 ContextRecord, 244 sizeof(CONTEXT)); 245 246 /* Send the command string to the debugger */ 247 KdpReportCommandStringStateChange(NameString, 248 CommandString, 249 &Prcb->ProcessorState.ContextFrame); 250 251 /* Restore the processor state */ 252 KdpMoveMemory(ContextRecord, 253 &Prcb->ProcessorState.ContextFrame, 254 sizeof(CONTEXT)); 255 KiRestoreProcessorControlState(&Prcb->ProcessorState); 256 257 /* Exit the debugger and return */ 258 KdExitDebugger(Enable); 259 } 260 261 VOID 262 NTAPI 263 KdpSymbol(IN PSTRING DllPath, 264 IN PKD_SYMBOLS_INFO SymbolInfo, 265 IN BOOLEAN Unload, 266 IN KPROCESSOR_MODE PreviousMode, 267 IN PCONTEXT ContextRecord, 268 IN PKTRAP_FRAME TrapFrame, 269 IN PKEXCEPTION_FRAME ExceptionFrame) 270 { 271 BOOLEAN Enable; 272 PKPRCB Prcb = KeGetCurrentPrcb(); 273 274 /* Check if we need to do anything */ 275 if ((PreviousMode != KernelMode) || (KdDebuggerNotPresent)) return; 276 277 /* Enter the debugger */ 278 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); 279 280 /* Save the CPU Control State and save the context */ 281 KiSaveProcessorControlState(&Prcb->ProcessorState); 282 KdpMoveMemory(&Prcb->ProcessorState.ContextFrame, 283 ContextRecord, 284 sizeof(CONTEXT)); 285 286 /* Report the new state */ 287 KdpReportLoadSymbolsStateChange(DllPath, 288 SymbolInfo, 289 Unload, 290 &Prcb->ProcessorState.ContextFrame); 291 292 /* Restore the processor state */ 293 KdpMoveMemory(ContextRecord, 294 &Prcb->ProcessorState.ContextFrame, 295 sizeof(CONTEXT)); 296 KiRestoreProcessorControlState(&Prcb->ProcessorState); 297 298 /* Exit the debugger and return */ 299 KdExitDebugger(Enable); 300 } 301 302 USHORT 303 NTAPI 304 KdpPrompt( 305 _In_reads_bytes_(PromptLength) PCHAR PromptString, 306 _In_ USHORT PromptLength, 307 _Out_writes_bytes_(MaximumResponseLength) PCHAR ResponseString, 308 _In_ USHORT MaximumResponseLength, 309 _In_ KPROCESSOR_MODE PreviousMode, 310 _In_ PKTRAP_FRAME TrapFrame, 311 _In_ PKEXCEPTION_FRAME ExceptionFrame) 312 { 313 STRING PromptBuffer, ResponseBuffer; 314 BOOLEAN Enable, Resend; 315 PCHAR SafeResponseString; 316 CHAR CapturedPrompt[KD_PRINT_MAX_BYTES]; 317 CHAR SafeResponseBuffer[KD_PRINT_MAX_BYTES]; 318 319 /* Normalize the lengths */ 320 PromptLength = min(PromptLength, 321 sizeof(CapturedPrompt)); 322 MaximumResponseLength = min(MaximumResponseLength, 323 sizeof(SafeResponseBuffer)); 324 325 /* Check if we need to verify the string */ 326 if (PreviousMode != KernelMode) 327 { 328 /* Handle user-mode buffers safely */ 329 _SEH2_TRY 330 { 331 /* Probe and capture the prompt */ 332 ProbeForRead(PromptString, PromptLength, 1); 333 KdpMoveMemory(CapturedPrompt, PromptString, PromptLength); 334 PromptString = CapturedPrompt; 335 336 /* Probe and make room for the response */ 337 ProbeForWrite(ResponseString, MaximumResponseLength, 1); 338 SafeResponseString = SafeResponseBuffer; 339 } 340 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 341 { 342 /* Bad string pointer, bail out */ 343 _SEH2_YIELD(return 0); 344 } 345 _SEH2_END; 346 } 347 else 348 { 349 SafeResponseString = ResponseString; 350 } 351 352 /* Setup the prompt and response buffers */ 353 PromptBuffer.Buffer = PromptString; 354 PromptBuffer.Length = PromptBuffer.MaximumLength = PromptLength; 355 ResponseBuffer.Buffer = SafeResponseString; 356 ResponseBuffer.Length = 0; 357 ResponseBuffer.MaximumLength = MaximumResponseLength; 358 359 /* Log the print */ 360 KdLogDbgPrint(&PromptBuffer); 361 362 /* Enter the debugger */ 363 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); 364 365 /* Enter prompt loop */ 366 do 367 { 368 /* Send the prompt and receive the response */ 369 Resend = KdpPromptString(&PromptBuffer, &ResponseBuffer); 370 371 /* Loop while we need to resend */ 372 } while (Resend); 373 374 /* Exit the debugger */ 375 KdExitDebugger(Enable); 376 377 /* Copy back the response if required */ 378 if (PreviousMode != KernelMode) 379 { 380 _SEH2_TRY 381 { 382 /* Safely copy back the response to user mode */ 383 KdpMoveMemory(ResponseString, 384 ResponseBuffer.Buffer, 385 ResponseBuffer.Length); 386 } 387 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 388 { 389 /* String became invalid after we exited, fail */ 390 _SEH2_YIELD(return 0); 391 } 392 _SEH2_END; 393 } 394 395 /* Return the number of characters received */ 396 return ResponseBuffer.Length; 397 } 398 399 static 400 NTSTATUS 401 NTAPI 402 KdpPrintFromUser( 403 _In_ ULONG ComponentId, 404 _In_ ULONG Level, 405 _In_reads_bytes_(Length) PCHAR String, 406 _In_ USHORT Length, 407 _In_ KPROCESSOR_MODE PreviousMode, 408 _In_ PKTRAP_FRAME TrapFrame, 409 _In_ PKEXCEPTION_FRAME ExceptionFrame, 410 _Out_ PBOOLEAN Handled) 411 { 412 CHAR CapturedString[KD_PRINT_MAX_BYTES]; 413 414 ASSERT(PreviousMode == UserMode); 415 ASSERT(Length <= sizeof(CapturedString)); 416 417 /* Capture user-mode buffers */ 418 _SEH2_TRY 419 { 420 /* Probe and capture the string */ 421 ProbeForRead(String, Length, 1); 422 KdpMoveMemory(CapturedString, String, Length); 423 String = CapturedString; 424 } 425 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 426 { 427 /* Bad string pointer, bail out */ 428 _SEH2_YIELD(return STATUS_ACCESS_VIOLATION); 429 } 430 _SEH2_END; 431 432 /* Now go through the kernel-mode code path */ 433 return KdpPrint(ComponentId, 434 Level, 435 String, 436 Length, 437 KernelMode, 438 TrapFrame, 439 ExceptionFrame, 440 Handled); 441 } 442 443 NTSTATUS 444 NTAPI 445 KdpPrint( 446 _In_ ULONG ComponentId, 447 _In_ ULONG Level, 448 _In_reads_bytes_(Length) PCHAR String, 449 _In_ USHORT Length, 450 _In_ KPROCESSOR_MODE PreviousMode, 451 _In_ PKTRAP_FRAME TrapFrame, 452 _In_ PKEXCEPTION_FRAME ExceptionFrame, 453 _Out_ PBOOLEAN Handled) 454 { 455 NTSTATUS Status; 456 BOOLEAN Enable; 457 STRING OutputString; 458 459 if (NtQueryDebugFilterState(ComponentId, Level) == (NTSTATUS)FALSE) 460 { 461 /* Mask validation failed */ 462 *Handled = TRUE; 463 return STATUS_SUCCESS; 464 } 465 466 /* Assume failure */ 467 *Handled = FALSE; 468 469 /* Normalize the length */ 470 Length = min(Length, KD_PRINT_MAX_BYTES); 471 472 /* Check if we need to verify the string */ 473 if (PreviousMode != KernelMode) 474 { 475 /* This case requires a 512 byte stack buffer. 476 * We don't want to use that much stack in the kernel case, but we 477 * can't use _alloca due to PSEH. So the buffer exists in this 478 * helper function instead. 479 */ 480 return KdpPrintFromUser(ComponentId, 481 Level, 482 String, 483 Length, 484 PreviousMode, 485 TrapFrame, 486 ExceptionFrame, 487 Handled); 488 } 489 490 /* Setup the output string */ 491 OutputString.Buffer = String; 492 OutputString.Length = OutputString.MaximumLength = Length; 493 494 /* Log the print */ 495 KdLogDbgPrint(&OutputString); 496 497 /* Check for a debugger */ 498 if (KdDebuggerNotPresent) 499 { 500 /* Fail */ 501 *Handled = TRUE; 502 return STATUS_DEVICE_NOT_CONNECTED; 503 } 504 505 /* Enter the debugger */ 506 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); 507 508 /* Print the string */ 509 if (KdpPrintString(&OutputString)) 510 { 511 /* User pressed CTRL-C, breakpoint on return */ 512 Status = STATUS_BREAKPOINT; 513 } 514 else 515 { 516 /* String was printed */ 517 Status = STATUS_SUCCESS; 518 } 519 520 /* Exit the debugger and return */ 521 KdExitDebugger(Enable); 522 *Handled = TRUE; 523 return Status; 524 } 525 526 VOID 527 __cdecl 528 KdpDprintf( 529 _In_ PCSTR Format, 530 ...) 531 { 532 STRING String; 533 USHORT Length; 534 va_list ap; 535 CHAR Buffer[512]; 536 537 /* Format the string */ 538 va_start(ap, Format); 539 Length = (USHORT)_vsnprintf(Buffer, 540 sizeof(Buffer), 541 Format, 542 ap); 543 va_end(ap); 544 545 /* Set it up */ 546 String.Buffer = Buffer; 547 String.Length = String.MaximumLength = Length; 548 549 /* Send it to the debugger directly */ 550 KdpPrintString(&String); 551 } 552