1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/kd/kdio.c 5 * PURPOSE: NT Kernel Debugger Input/Output Functions 6 * 7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #include <reactos/buildno.h> 14 #define NDEBUG 15 #include <debug.h> 16 17 /* GLOBALS *******************************************************************/ 18 19 #define KdpBufferSize (1024 * 512) 20 BOOLEAN KdpLoggingEnabled = FALSE; 21 PCHAR KdpDebugBuffer = NULL; 22 volatile ULONG KdpCurrentPosition = 0; 23 volatile ULONG KdpFreeBytes = 0; 24 KSPIN_LOCK KdpDebugLogSpinLock; 25 KEVENT KdpLoggerThreadEvent; 26 HANDLE KdpLogFileHandle; 27 ANSI_STRING KdpLogFileName = RTL_CONSTANT_STRING("\\SystemRoot\\debug.log"); 28 29 KSPIN_LOCK KdpSerialSpinLock; 30 ULONG SerialPortNumber = DEFAULT_DEBUG_PORT; 31 CPPORT SerialPortInfo = {0, DEFAULT_DEBUG_BAUD_RATE, 0}; 32 33 /* Current Port in use. FIXME: Do we support more then one? */ 34 ULONG KdpPort; 35 36 #define KdpScreenLineLengthDefault 80 37 CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = ""; 38 ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0; 39 40 const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024; 41 PCHAR KdpDmesgBuffer = NULL; 42 volatile ULONG KdpDmesgCurrentPosition = 0; 43 volatile ULONG KdpDmesgFreeBytes = 0; 44 volatile ULONG KdbDmesgTotalWritten = 0; 45 KSPIN_LOCK KdpDmesgLogSpinLock; 46 volatile BOOLEAN KdbpIsInDmesgMode = FALSE; 47 48 /* UTILITY FUNCTIONS *********************************************************/ 49 50 /* 51 * Get the total size of the memory before 52 * Mm is initialized, by counting the number 53 * of physical pages. Useful for debug logging. 54 * 55 * Strongly inspired by: 56 * mm\ARM3\mminit.c : MiScanMemoryDescriptors(...) 57 * 58 * See also: kd64\kdinit.c 59 */ 60 static SIZE_T 61 INIT_FUNCTION 62 KdpGetMemorySizeInMBs(IN PLOADER_PARAMETER_BLOCK LoaderBlock) 63 { 64 PLIST_ENTRY ListEntry; 65 PMEMORY_ALLOCATION_DESCRIPTOR Descriptor; 66 SIZE_T NumberOfPhysicalPages = 0; 67 68 /* Loop the memory descriptors */ 69 for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink; 70 ListEntry != &LoaderBlock->MemoryDescriptorListHead; 71 ListEntry = ListEntry->Flink) 72 { 73 /* Get the descriptor */ 74 Descriptor = CONTAINING_RECORD(ListEntry, 75 MEMORY_ALLOCATION_DESCRIPTOR, 76 ListEntry); 77 78 /* Check if this is invisible memory */ 79 if ((Descriptor->MemoryType == LoaderFirmwarePermanent) || 80 (Descriptor->MemoryType == LoaderSpecialMemory) || 81 (Descriptor->MemoryType == LoaderHALCachedMemory) || 82 (Descriptor->MemoryType == LoaderBBTMemory)) 83 { 84 /* Skip this descriptor */ 85 continue; 86 } 87 88 /* Check if this is bad memory */ 89 if (Descriptor->MemoryType != LoaderBad) 90 { 91 /* Count this in the total of pages */ 92 NumberOfPhysicalPages += Descriptor->PageCount; 93 } 94 } 95 96 return NumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024; 97 } 98 99 /* See also: kd64\kdinit.c */ 100 static VOID 101 INIT_FUNCTION 102 KdpPrintBanner(IN SIZE_T MemSizeMBs) 103 { 104 DPRINT1("-----------------------------------------------------\n"); 105 DPRINT1("ReactOS " KERNEL_VERSION_STR " (Build " KERNEL_VERSION_BUILD_STR ") (Commit " KERNEL_VERSION_COMMIT_HASH ")\n"); 106 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs); 107 DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions); 108 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName, KeLoaderBlock->NtHalPathName, KeLoaderBlock->ArcHalDeviceName, KeLoaderBlock->NtBootPathName); 109 } 110 111 /* FILE DEBUG LOG FUNCTIONS **************************************************/ 112 113 VOID 114 NTAPI 115 KdpLoggerThread(PVOID Context) 116 { 117 ULONG beg, end, num; 118 IO_STATUS_BLOCK Iosb; 119 120 KdpLoggingEnabled = TRUE; 121 122 while (TRUE) 123 { 124 KeWaitForSingleObject(&KdpLoggerThreadEvent, 0, KernelMode, FALSE, NULL); 125 126 /* Bug */ 127 /* Keep KdpCurrentPosition and KdpFreeBytes values in local 128 * variables to avoid their possible change from Producer part, 129 * KdpPrintToLogFile function 130 */ 131 end = KdpCurrentPosition; 132 num = KdpFreeBytes; 133 134 /* Now securely calculate values, based on local variables */ 135 beg = (end + num) % KdpBufferSize; 136 num = KdpBufferSize - num; 137 138 /* Nothing to do? */ 139 if (num == 0) 140 continue; 141 142 if (end > beg) 143 { 144 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb, 145 KdpDebugBuffer + beg, num, NULL, NULL); 146 } 147 else 148 { 149 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb, 150 KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL); 151 152 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb, 153 KdpDebugBuffer, end, NULL, NULL); 154 } 155 156 (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num); 157 } 158 } 159 160 VOID 161 NTAPI 162 KdpPrintToLogFile(PCH String, 163 ULONG StringLength) 164 { 165 ULONG beg, end, num; 166 KIRQL OldIrql; 167 168 if (KdpDebugBuffer == NULL) return; 169 170 /* Acquire the printing spinlock without waiting at raised IRQL */ 171 while (TRUE) 172 { 173 /* Wait when the spinlock becomes available */ 174 while (!KeTestSpinLock(&KdpDebugLogSpinLock)); 175 176 /* Spinlock was free, raise IRQL */ 177 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 178 179 /* Try to get the spinlock */ 180 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock)) 181 break; 182 183 /* Someone else got the spinlock, lower IRQL back */ 184 KeLowerIrql(OldIrql); 185 } 186 187 beg = KdpCurrentPosition; 188 num = KdpFreeBytes; 189 if (StringLength < num) 190 num = StringLength; 191 192 if (num != 0) 193 { 194 end = (beg + num) % KdpBufferSize; 195 KdpCurrentPosition = end; 196 KdpFreeBytes -= num; 197 198 if (end > beg) 199 { 200 RtlCopyMemory(KdpDebugBuffer + beg, String, num); 201 } 202 else 203 { 204 RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg); 205 RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end); 206 } 207 } 208 209 /* Release spinlock */ 210 KiReleaseSpinLock(&KdpDebugLogSpinLock); 211 212 /* Lower IRQL */ 213 KeLowerIrql(OldIrql); 214 215 /* Signal the logger thread */ 216 if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled) 217 KeSetEvent(&KdpLoggerThreadEvent, 0, FALSE); 218 } 219 220 VOID 221 NTAPI 222 INIT_FUNCTION 223 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable, 224 ULONG BootPhase) 225 { 226 NTSTATUS Status; 227 UNICODE_STRING FileName; 228 OBJECT_ATTRIBUTES ObjectAttributes; 229 IO_STATUS_BLOCK Iosb; 230 HANDLE ThreadHandle; 231 KPRIORITY Priority; 232 SIZE_T MemSizeMBs; 233 234 if (!KdpDebugMode.File) return; 235 236 if (BootPhase == 0) 237 { 238 KdComPortInUse = NULL; 239 240 /* Write out the functions that we support for now */ 241 DispatchTable->KdpInitRoutine = KdpInitDebugLog; 242 DispatchTable->KdpPrintRoutine = KdpPrintToLogFile; 243 244 /* Register as a Provider */ 245 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 246 247 } 248 else if (BootPhase == 1) 249 { 250 /* Allocate a buffer for debug log */ 251 KdpDebugBuffer = ExAllocatePool(NonPagedPool, KdpBufferSize); 252 KdpFreeBytes = KdpBufferSize; 253 254 /* Initialize spinlock */ 255 KeInitializeSpinLock(&KdpDebugLogSpinLock); 256 257 /* Display separator + ReactOS version at start of the debug log */ 258 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024; 259 KdpPrintBanner(MemSizeMBs); 260 } 261 else if (BootPhase == 2) 262 { 263 HalDisplayString("\r\n File log debugging enabled\r\n\r\n"); 264 } 265 else if (BootPhase == 3) 266 { 267 /* Setup the log name */ 268 Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE); 269 if (!NT_SUCCESS(Status)) return; 270 271 InitializeObjectAttributes(&ObjectAttributes, 272 &FileName, 273 0, 274 NULL, 275 NULL); 276 277 /* Create the log file */ 278 Status = NtCreateFile(&KdpLogFileHandle, 279 FILE_APPEND_DATA | SYNCHRONIZE, 280 &ObjectAttributes, 281 &Iosb, 282 NULL, 283 FILE_ATTRIBUTE_NORMAL, 284 FILE_SHARE_READ, 285 FILE_SUPERSEDE, 286 FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT, 287 NULL, 288 0); 289 290 RtlFreeUnicodeString(&FileName); 291 292 if (!NT_SUCCESS(Status)) return; 293 294 KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE); 295 296 /* Create the logger thread */ 297 Status = PsCreateSystemThread(&ThreadHandle, 298 THREAD_ALL_ACCESS, 299 NULL, 300 NULL, 301 NULL, 302 KdpLoggerThread, 303 NULL); 304 305 if (!NT_SUCCESS(Status)) return; 306 307 Priority = 7; 308 NtSetInformationThread(ThreadHandle, 309 ThreadPriority, 310 &Priority, 311 sizeof(Priority)); 312 } 313 } 314 315 /* SERIAL FUNCTIONS **********************************************************/ 316 317 VOID 318 NTAPI 319 KdpSerialDebugPrint(LPSTR Message, 320 ULONG Length) 321 { 322 KIRQL OldIrql; 323 PCHAR pch = (PCHAR) Message; 324 325 /* Acquire the printing spinlock without waiting at raised IRQL */ 326 while (TRUE) 327 { 328 /* Wait when the spinlock becomes available */ 329 while (!KeTestSpinLock(&KdpSerialSpinLock)); 330 331 /* Spinlock was free, raise IRQL */ 332 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 333 334 /* Try to get the spinlock */ 335 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock)) 336 break; 337 338 /* Someone else got the spinlock, lower IRQL back */ 339 KeLowerIrql(OldIrql); 340 } 341 342 /* Output the message */ 343 while (pch < Message + Length && *pch != '\0') 344 { 345 if (*pch == '\n') 346 { 347 KdPortPutByteEx(&SerialPortInfo, '\r'); 348 } 349 KdPortPutByteEx(&SerialPortInfo, *pch); 350 pch++; 351 } 352 353 /* Release spinlock */ 354 KiReleaseSpinLock(&KdpSerialSpinLock); 355 356 /* Lower IRQL */ 357 KeLowerIrql(OldIrql); 358 } 359 360 VOID 361 NTAPI 362 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable, 363 ULONG BootPhase) 364 { 365 SIZE_T MemSizeMBs; 366 if (!KdpDebugMode.Serial) return; 367 368 if (BootPhase == 0) 369 { 370 /* Write out the functions that we support for now */ 371 DispatchTable->KdpInitRoutine = KdpSerialInit; 372 DispatchTable->KdpPrintRoutine = KdpSerialDebugPrint; 373 374 /* Initialize the Port */ 375 if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber)) 376 { 377 KdpDebugMode.Serial = FALSE; 378 return; 379 } 380 KdComPortInUse = SerialPortInfo.Address; 381 382 /* Initialize spinlock */ 383 KeInitializeSpinLock(&KdpSerialSpinLock); 384 385 /* Register as a Provider */ 386 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 387 388 /* Display separator + ReactOS version at start of the debug log */ 389 MemSizeMBs = KdpGetMemorySizeInMBs(KeLoaderBlock); 390 KdpPrintBanner(MemSizeMBs); 391 } 392 else if (BootPhase == 2) 393 { 394 HalDisplayString("\r\n Serial debugging enabled\r\n\r\n"); 395 } 396 } 397 398 /* SCREEN FUNCTIONS **********************************************************/ 399 400 /* 401 * Screen debug logger function KdpScreenPrint() writes text messages into 402 * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could 403 * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects 404 * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock. 405 */ 406 VOID 407 NTAPI 408 KdpScreenPrint(LPSTR Message, 409 ULONG Length) 410 { 411 ULONG beg, end, num; 412 KIRQL OldIrql; 413 PCHAR pch = (PCHAR) Message; 414 415 while (pch < Message + Length && *pch) 416 { 417 if(*pch == '\b') 418 { 419 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */ 420 if(KdpScreenLineLength > 0) 421 { 422 /* Remove last character from buffer */ 423 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0'; 424 KdpScreenLineBufferPos = KdpScreenLineLength; 425 426 /* Clear row and print line again */ 427 HalDisplayString("\r"); 428 HalDisplayString(KdpScreenLineBuffer); 429 } 430 } 431 else 432 { 433 KdpScreenLineBuffer[KdpScreenLineLength++] = *pch; 434 KdpScreenLineBuffer[KdpScreenLineLength] = '\0'; 435 } 436 437 if(*pch == '\n' || KdpScreenLineLength == KdpScreenLineLengthDefault) 438 { 439 /* Print buffered characters */ 440 if(KdpScreenLineBufferPos != KdpScreenLineLength) 441 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos); 442 443 /* Clear line buffer */ 444 KdpScreenLineBuffer[0] = '\0'; 445 KdpScreenLineLength = KdpScreenLineBufferPos = 0; 446 } 447 448 ++pch; 449 } 450 451 /* Print buffered characters */ 452 if(KdpScreenLineBufferPos != KdpScreenLineLength) 453 { 454 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos); 455 KdpScreenLineBufferPos = KdpScreenLineLength; 456 } 457 458 /* Dmesg: store Message in the buffer to show it later */ 459 if (KdbpIsInDmesgMode) 460 return; 461 462 if (KdpDmesgBuffer == NULL) 463 return; 464 465 /* Acquire the printing spinlock without waiting at raised IRQL */ 466 while (TRUE) 467 { 468 /* Wait when the spinlock becomes available */ 469 while (!KeTestSpinLock(&KdpDmesgLogSpinLock)); 470 471 /* Spinlock was free, raise IRQL */ 472 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 473 474 /* Try to get the spinlock */ 475 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock)) 476 break; 477 478 /* Someone else got the spinlock, lower IRQL back */ 479 KeLowerIrql(OldIrql); 480 } 481 482 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize); 483 * set num to min(KdpDmesgFreeBytes, Length). 484 */ 485 num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes; 486 beg = KdpDmesgCurrentPosition; 487 if (num != 0) 488 { 489 end = (beg + num) % KdpDmesgBufferSize; 490 if (end > beg) 491 { 492 RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length); 493 } 494 else 495 { 496 RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg); 497 RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end); 498 } 499 KdpDmesgCurrentPosition = end; 500 501 /* Counting the total bytes written */ 502 KdbDmesgTotalWritten += num; 503 } 504 505 /* Release spinlock */ 506 KiReleaseSpinLock(&KdpDmesgLogSpinLock); 507 508 /* Lower IRQL */ 509 KeLowerIrql(OldIrql); 510 511 /* Optional step(?): find out a way to notify about buffer exhaustion, 512 * and possibly fall into kbd to use dmesg command: user will read 513 * debug messages before they will be wiped over by next writes. 514 */ 515 } 516 517 VOID 518 NTAPI 519 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable, 520 ULONG BootPhase) 521 { 522 SIZE_T MemSizeMBs; 523 if (!KdpDebugMode.Screen) return; 524 525 if (BootPhase == 0) 526 { 527 /* Write out the functions that we support for now */ 528 DispatchTable->KdpInitRoutine = KdpScreenInit; 529 DispatchTable->KdpPrintRoutine = KdpScreenPrint; 530 531 /* Register as a Provider */ 532 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 533 } 534 else if (BootPhase == 1) 535 { 536 /* Allocate a buffer for dmesg log buffer. +1 for terminating null, 537 * see kdbp_cli.c:KdbpCmdDmesg()/2 538 */ 539 KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1); 540 RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1); 541 KdpDmesgFreeBytes = KdpDmesgBufferSize; 542 KdbDmesgTotalWritten = 0; 543 544 /* Take control of the display */ 545 InbvAcquireDisplayOwnership(); 546 InbvResetDisplay(); 547 InbvSolidColorFill(0, 0, 639, 479, 0); 548 InbvSetTextColor(15); 549 InbvSetScrollRegion(0, 0, 639, 479); 550 InbvInstallDisplayStringFilter(NULL); 551 InbvEnableDisplayString(TRUE); 552 553 /* Initialize spinlock */ 554 KeInitializeSpinLock(&KdpDmesgLogSpinLock); 555 556 /* Display separator + ReactOS version at start of the debug log */ 557 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024; 558 KdpPrintBanner(MemSizeMBs); 559 } 560 else if (BootPhase == 2) 561 { 562 HalDisplayString("\r\n Screen debugging enabled\r\n\r\n"); 563 } 564 } 565 566 /* GENERAL FUNCTIONS *********************************************************/ 567 568 ULONG 569 NTAPI 570 KdpPrintString( 571 _In_reads_bytes_(Length) PCHAR UnsafeString, 572 _In_ ULONG Length, 573 _In_ KPROCESSOR_MODE PreviousMode) 574 { 575 PLIST_ENTRY CurrentEntry; 576 PKD_DISPATCH_TABLE CurrentTable; 577 PCHAR String; 578 CHAR StringBuffer[512]; 579 580 if (!KdpDebugMode.Value) return 0; 581 582 Length = min(Length, sizeof(StringBuffer)); 583 584 if (PreviousMode != KernelMode) 585 { 586 _SEH2_TRY 587 { 588 ProbeForRead(UnsafeString, Length, 1); 589 String = StringBuffer; 590 RtlCopyMemory(String, UnsafeString, Length); 591 } 592 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 593 { 594 return 0; 595 } 596 _SEH2_END; 597 } 598 else 599 { 600 String = UnsafeString; 601 } 602 603 /* Call the registered handlers */ 604 CurrentEntry = KdProviders.Flink; 605 while (CurrentEntry != &KdProviders) 606 { 607 /* Get the current table */ 608 CurrentTable = CONTAINING_RECORD(CurrentEntry, 609 KD_DISPATCH_TABLE, 610 KdProvidersList); 611 612 /* Call it */ 613 CurrentTable->KdpPrintRoutine(String, Length); 614 615 /* Next Table */ 616 CurrentEntry = CurrentEntry->Flink; 617 } 618 619 /* Call the Wrapper Routine */ 620 if (WrapperTable.KdpPrintRoutine) 621 WrapperTable.KdpPrintRoutine(String, Length); 622 623 /* Return the Length */ 624 return Length; 625 } 626 627 /* EOF */ 628