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