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
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
10 /* INCLUDES ******************************************************************/
13 #include <reactos/buildno.h>
16 /* GLOBALS *******************************************************************/
18 #define KdpBufferSize (1024 * 512)
19 BOOLEAN KdpLoggingEnabled
= FALSE
;
20 PCHAR KdpDebugBuffer
= NULL
;
21 volatile ULONG KdpCurrentPosition
= 0;
22 volatile ULONG KdpFreeBytes
= 0;
23 KSPIN_LOCK KdpDebugLogSpinLock
;
24 KEVENT KdpLoggerThreadEvent
;
25 HANDLE KdpLogFileHandle
;
26 ANSI_STRING KdpLogFileName
= RTL_CONSTANT_STRING("\\SystemRoot\\debug.log");
28 KSPIN_LOCK KdpSerialSpinLock
;
29 ULONG SerialPortNumber
= DEFAULT_DEBUG_PORT
;
30 CPPORT SerialPortInfo
= {0, DEFAULT_DEBUG_BAUD_RATE
, 0};
32 /* Current Port in use. FIXME: Do we support more then one? */
35 #define KdpScreenLineLengthDefault 80
36 CHAR KdpScreenLineBuffer
[KdpScreenLineLengthDefault
+ 1] = "";
37 ULONG KdpScreenLineBufferPos
= 0, KdpScreenLineLength
= 0;
39 const ULONG KdpDmesgBufferSize
= 128 * 1024; // 512*1024; // 5*1024*1024;
40 PCHAR KdpDmesgBuffer
= NULL
;
41 volatile ULONG KdpDmesgCurrentPosition
= 0;
42 volatile ULONG KdpDmesgFreeBytes
= 0;
43 volatile ULONG KdbDmesgTotalWritten
= 0;
44 KSPIN_LOCK KdpDmesgLogSpinLock
;
45 volatile BOOLEAN KdbpIsInDmesgMode
= FALSE
;
47 /* UTILITY FUNCTIONS *********************************************************/
50 * Get the total size of the memory before
51 * Mm is initialized, by counting the number
52 * of physical pages. Useful for debug logging.
54 * Strongly inspired by:
55 * mm\ARM3\mminit.c : MiScanMemoryDescriptors(...)
59 KdpGetMemorySizeInMBs(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
61 PLIST_ENTRY ListEntry
;
62 PMEMORY_ALLOCATION_DESCRIPTOR Descriptor
;
63 SIZE_T NumberOfPhysicalPages
= 0;
65 /* Loop the memory descriptors */
66 for (ListEntry
= LoaderBlock
->MemoryDescriptorListHead
.Flink
;
67 ListEntry
!= &LoaderBlock
->MemoryDescriptorListHead
;
68 ListEntry
= ListEntry
->Flink
)
70 /* Get the descriptor */
71 Descriptor
= CONTAINING_RECORD(ListEntry
,
72 MEMORY_ALLOCATION_DESCRIPTOR
,
75 /* Check if this is invisible memory */
76 if ((Descriptor
->MemoryType
== LoaderFirmwarePermanent
) ||
77 (Descriptor
->MemoryType
== LoaderSpecialMemory
) ||
78 (Descriptor
->MemoryType
== LoaderHALCachedMemory
) ||
79 (Descriptor
->MemoryType
== LoaderBBTMemory
))
81 /* Skip this descriptor */
85 /* Check if this is bad memory */
86 if (Descriptor
->MemoryType
!= LoaderBad
)
88 /* Count this in the total of pages */
89 NumberOfPhysicalPages
+= Descriptor
->PageCount
;
93 return NumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
96 /* FILE DEBUG LOG FUNCTIONS **************************************************/
100 KdpLoggerThread(PVOID Context
)
103 IO_STATUS_BLOCK Iosb
;
105 KdpLoggingEnabled
= TRUE
;
109 KeWaitForSingleObject(&KdpLoggerThreadEvent
, 0, KernelMode
, FALSE
, NULL
);
112 /* Keep KdpCurrentPosition and KdpFreeBytes values in local
113 * variables to avoid their possible change from Producer part,
114 * KdpPrintToLogFile function
116 end
= KdpCurrentPosition
;
119 /* Now securely calculate values, based on local variables */
120 beg
= (end
+ num
) % KdpBufferSize
;
121 num
= KdpBufferSize
- num
;
129 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
130 KdpDebugBuffer
+ beg
, num
, NULL
, NULL
);
134 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
135 KdpDebugBuffer
+ beg
, KdpBufferSize
- beg
, NULL
, NULL
);
137 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
138 KdpDebugBuffer
, end
, NULL
, NULL
);
141 (VOID
)InterlockedExchangeAddUL(&KdpFreeBytes
, num
);
147 KdpPrintToLogFile(PCH String
,
153 if (KdpDebugBuffer
== NULL
) return;
155 /* Acquire the printing spinlock without waiting at raised IRQL */
158 /* Wait when the spinlock becomes available */
159 while (!KeTestSpinLock(&KdpDebugLogSpinLock
));
161 /* Spinlock was free, raise IRQL */
162 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
164 /* Try to get the spinlock */
165 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock
))
168 /* Someone else got the spinlock, lower IRQL back */
169 KeLowerIrql(OldIrql
);
172 beg
= KdpCurrentPosition
;
174 if (StringLength
< num
)
179 end
= (beg
+ num
) % KdpBufferSize
;
180 KdpCurrentPosition
= end
;
185 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, num
);
189 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, KdpBufferSize
- beg
);
190 RtlCopyMemory(KdpDebugBuffer
, String
+ KdpBufferSize
- beg
, end
);
194 /* Release spinlock */
195 KiReleaseSpinLock(&KdpDebugLogSpinLock
);
198 KeLowerIrql(OldIrql
);
200 /* Signal the logger thread */
201 if (OldIrql
<= DISPATCH_LEVEL
&& KdpLoggingEnabled
)
202 KeSetEvent(&KdpLoggerThreadEvent
, 0, FALSE
);
208 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable
,
212 UNICODE_STRING FileName
;
213 OBJECT_ATTRIBUTES ObjectAttributes
;
214 IO_STATUS_BLOCK Iosb
;
219 if (!KdpDebugMode
.File
) return;
223 KdComPortInUse
= NULL
;
225 /* Write out the functions that we support for now */
226 DispatchTable
->KdpInitRoutine
= KdpInitDebugLog
;
227 DispatchTable
->KdpPrintRoutine
= KdpPrintToLogFile
;
229 /* Register as a Provider */
230 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
233 else if (BootPhase
== 1)
235 /* Allocate a buffer for debug log */
236 KdpDebugBuffer
= ExAllocatePool(NonPagedPool
, KdpBufferSize
);
237 KdpFreeBytes
= KdpBufferSize
;
239 /* Initialize spinlock */
240 KeInitializeSpinLock(&KdpDebugLogSpinLock
);
242 /* Display separator + ReactOS version at start of the debug log */
243 DPRINT1("---------------------------------------------------------------\n");
244 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
245 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
246 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
248 else if (BootPhase
== 2)
250 HalDisplayString("\r\n File log debugging enabled\r\n\r\n");
252 else if (BootPhase
== 3)
254 /* Setup the log name */
255 Status
= RtlAnsiStringToUnicodeString(&FileName
, &KdpLogFileName
, TRUE
);
256 if (!NT_SUCCESS(Status
)) return;
258 InitializeObjectAttributes(&ObjectAttributes
,
264 /* Create the log file */
265 Status
= NtCreateFile(&KdpLogFileHandle
,
266 FILE_APPEND_DATA
| SYNCHRONIZE
,
270 FILE_ATTRIBUTE_NORMAL
,
273 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
277 RtlFreeUnicodeString(&FileName
);
279 if (!NT_SUCCESS(Status
)) return;
281 KeInitializeEvent(&KdpLoggerThreadEvent
, SynchronizationEvent
, TRUE
);
283 /* Create the logger thread */
284 Status
= PsCreateSystemThread(&ThreadHandle
,
292 if (!NT_SUCCESS(Status
)) return;
295 NtSetInformationThread(ThreadHandle
,
302 /* SERIAL FUNCTIONS **********************************************************/
306 KdpSerialDebugPrint(LPSTR Message
,
310 PCHAR pch
= (PCHAR
) Message
;
312 /* Acquire the printing spinlock without waiting at raised IRQL */
315 /* Wait when the spinlock becomes available */
316 while (!KeTestSpinLock(&KdpSerialSpinLock
));
318 /* Spinlock was free, raise IRQL */
319 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
321 /* Try to get the spinlock */
322 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock
))
325 /* Someone else got the spinlock, lower IRQL back */
326 KeLowerIrql(OldIrql
);
329 /* Output the message */
334 KdPortPutByteEx(&SerialPortInfo
, '\r');
336 KdPortPutByteEx(&SerialPortInfo
, *pch
);
340 /* Release spinlock */
341 KiReleaseSpinLock(&KdpSerialSpinLock
);
344 KeLowerIrql(OldIrql
);
349 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable
,
353 if (!KdpDebugMode
.Serial
) return;
357 /* Write out the functions that we support for now */
358 DispatchTable
->KdpInitRoutine
= KdpSerialInit
;
359 DispatchTable
->KdpPrintRoutine
= KdpSerialDebugPrint
;
361 /* Initialize the Port */
362 if (!KdPortInitializeEx(&SerialPortInfo
, SerialPortNumber
))
364 KdpDebugMode
.Serial
= FALSE
;
367 KdComPortInUse
= SerialPortInfo
.Address
;
369 /* Initialize spinlock */
370 KeInitializeSpinLock(&KdpSerialSpinLock
);
372 /* Register as a Provider */
373 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
375 /* Display separator + ReactOS version at start of the debug log */
376 DPRINT1("-----------------------------------------------------\n");
377 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
378 MemSizeMBs
= KdpGetMemorySizeInMBs(KeLoaderBlock
);
379 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
380 DPRINT1("Command Line: %s\n", KeLoaderBlock
->LoadOptions
);
381 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock
->ArcBootDeviceName
,
382 KeLoaderBlock
->NtHalPathName
,
383 KeLoaderBlock
->ArcHalDeviceName
,
384 KeLoaderBlock
->NtBootPathName
);
386 else if (BootPhase
== 2)
388 HalDisplayString("\r\n Serial debugging enabled\r\n\r\n");
392 /* SCREEN FUNCTIONS **********************************************************/
395 * Screen debug logger function KdpScreenPrint() writes text messages into
396 * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
397 * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
398 * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
402 KdpScreenPrint(LPSTR Message
,
407 PCHAR pch
= (PCHAR
) Message
;
413 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
414 if(KdpScreenLineLength
> 0)
416 /* Remove last character from buffer */
417 KdpScreenLineBuffer
[--KdpScreenLineLength
] = '\0';
418 KdpScreenLineBufferPos
= KdpScreenLineLength
;
420 /* Clear row and print line again */
421 HalDisplayString("\r");
422 HalDisplayString(KdpScreenLineBuffer
);
427 KdpScreenLineBuffer
[KdpScreenLineLength
++] = *pch
;
428 KdpScreenLineBuffer
[KdpScreenLineLength
] = '\0';
431 if(*pch
== '\n' || KdpScreenLineLength
== KdpScreenLineLengthDefault
)
433 /* Print buffered characters */
434 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
435 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
437 /* Clear line buffer */
438 KdpScreenLineBuffer
[0] = '\0';
439 KdpScreenLineLength
= KdpScreenLineBufferPos
= 0;
445 /* Print buffered characters */
446 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
448 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
449 KdpScreenLineBufferPos
= KdpScreenLineLength
;
452 /* Dmesg: store Message in the buffer to show it later */
453 if (KdbpIsInDmesgMode
)
456 if (KdpDmesgBuffer
== NULL
)
459 /* Acquire the printing spinlock without waiting at raised IRQL */
462 /* Wait when the spinlock becomes available */
463 while (!KeTestSpinLock(&KdpDmesgLogSpinLock
));
465 /* Spinlock was free, raise IRQL */
466 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
468 /* Try to get the spinlock */
469 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock
))
472 /* Someone else got the spinlock, lower IRQL back */
473 KeLowerIrql(OldIrql
);
476 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
477 * set num to min(KdpDmesgFreeBytes, Length).
479 num
= (Length
< KdpDmesgFreeBytes
) ? Length
: KdpDmesgFreeBytes
;
480 beg
= KdpDmesgCurrentPosition
;
483 end
= (beg
+ num
) % KdpDmesgBufferSize
;
486 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, Length
);
490 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, KdpDmesgBufferSize
- beg
);
491 RtlCopyMemory(KdpDmesgBuffer
, Message
+ (KdpDmesgBufferSize
- beg
), end
);
493 KdpDmesgCurrentPosition
= end
;
495 /* Counting the total bytes written */
496 KdbDmesgTotalWritten
+= num
;
499 /* Release spinlock */
500 KiReleaseSpinLock(&KdpDmesgLogSpinLock
);
503 KeLowerIrql(OldIrql
);
505 /* Optional step(?): find out a way to notify about buffer exhaustion,
506 * and possibly fall into kbd to use dmesg command: user will read
507 * debug messages before they will be wiped over by next writes.
513 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable
,
517 if (!KdpDebugMode
.Screen
) return;
521 /* Write out the functions that we support for now */
522 DispatchTable
->KdpInitRoutine
= KdpScreenInit
;
523 DispatchTable
->KdpPrintRoutine
= KdpScreenPrint
;
525 /* Register as a Provider */
526 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
528 else if (BootPhase
== 1)
530 /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
531 * see kdbp_cli.c:KdbpCmdDmesg()/2
533 KdpDmesgBuffer
= ExAllocatePool(NonPagedPool
, KdpDmesgBufferSize
+ 1);
534 RtlZeroMemory(KdpDmesgBuffer
, KdpDmesgBufferSize
+ 1);
535 KdpDmesgFreeBytes
= KdpDmesgBufferSize
;
536 KdbDmesgTotalWritten
= 0;
538 /* Take control of the display */
539 InbvAcquireDisplayOwnership();
541 InbvSolidColorFill(0, 0, 639, 479, 0);
542 InbvSetTextColor(15);
543 InbvSetScrollRegion(0, 0, 639, 479);
544 InbvInstallDisplayStringFilter(NULL
);
545 InbvEnableDisplayString(TRUE
);
547 /* Initialize spinlock */
548 KeInitializeSpinLock(&KdpDmesgLogSpinLock
);
550 /* Display separator + ReactOS version at start of the debug log */
551 DPRINT1("-----------------------------------------------------\n");
552 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
553 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
554 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
555 DPRINT1("Command Line: %s\n", KeLoaderBlock
->LoadOptions
);
556 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock
->ArcBootDeviceName
,
557 KeLoaderBlock
->NtHalPathName
,
558 KeLoaderBlock
->ArcHalDeviceName
,
559 KeLoaderBlock
->NtBootPathName
);
561 else if (BootPhase
== 2)
563 HalDisplayString("\r\n Screen debugging enabled\r\n\r\n");
567 /* GENERAL FUNCTIONS *********************************************************/
571 KdpPrintString(LPSTR String
,
574 PLIST_ENTRY CurrentEntry
;
575 PKD_DISPATCH_TABLE CurrentTable
;
577 if (!KdpDebugMode
.Value
) return 0;
579 /* Call the registered handlers */
580 CurrentEntry
= KdProviders
.Flink
;
581 while (CurrentEntry
!= &KdProviders
)
583 /* Get the current table */
584 CurrentTable
= CONTAINING_RECORD(CurrentEntry
,
589 CurrentTable
->KdpPrintRoutine(String
, Length
);
592 CurrentEntry
= CurrentEntry
->Flink
;
595 /* Call the Wrapper Routine */
596 if (WrapperTable
.KdpPrintRoutine
)
597 WrapperTable
.KdpPrintRoutine(String
, Length
);
599 /* Return the Length */