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>
17 /* GLOBALS *******************************************************************/
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");
29 KSPIN_LOCK KdpSerialSpinLock
;
30 ULONG SerialPortNumber
= DEFAULT_DEBUG_PORT
;
31 CPPORT SerialPortInfo
= {0, DEFAULT_DEBUG_BAUD_RATE
, 0};
33 /* Current Port in use. FIXME: Do we support more then one? */
36 #define KdpScreenLineLengthDefault 80
37 CHAR KdpScreenLineBuffer
[KdpScreenLineLengthDefault
+ 1] = "";
38 ULONG KdpScreenLineBufferPos
= 0, KdpScreenLineLength
= 0;
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
;
48 /* UTILITY FUNCTIONS *********************************************************/
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.
55 * Strongly inspired by:
56 * mm\ARM3\mminit.c : MiScanMemoryDescriptors(...)
58 * See also: kd64\kdinit.c
62 KdpGetMemorySizeInMBs(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
64 PLIST_ENTRY ListEntry
;
65 PMEMORY_ALLOCATION_DESCRIPTOR Descriptor
;
66 SIZE_T NumberOfPhysicalPages
= 0;
68 /* Loop the memory descriptors */
69 for (ListEntry
= LoaderBlock
->MemoryDescriptorListHead
.Flink
;
70 ListEntry
!= &LoaderBlock
->MemoryDescriptorListHead
;
71 ListEntry
= ListEntry
->Flink
)
73 /* Get the descriptor */
74 Descriptor
= CONTAINING_RECORD(ListEntry
,
75 MEMORY_ALLOCATION_DESCRIPTOR
,
78 /* Check if this is invisible memory */
79 if ((Descriptor
->MemoryType
== LoaderFirmwarePermanent
) ||
80 (Descriptor
->MemoryType
== LoaderSpecialMemory
) ||
81 (Descriptor
->MemoryType
== LoaderHALCachedMemory
) ||
82 (Descriptor
->MemoryType
== LoaderBBTMemory
))
84 /* Skip this descriptor */
88 /* Check if this is bad memory */
89 if (Descriptor
->MemoryType
!= LoaderBad
)
91 /* Count this in the total of pages */
92 NumberOfPhysicalPages
+= Descriptor
->PageCount
;
96 return NumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
99 /* See also: kd64\kdinit.c */
102 KdpPrintBanner(IN SIZE_T MemSizeMBs
)
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
);
111 /* FILE DEBUG LOG FUNCTIONS **************************************************/
115 KdpLoggerThread(PVOID Context
)
118 IO_STATUS_BLOCK Iosb
;
120 KdpLoggingEnabled
= TRUE
;
124 KeWaitForSingleObject(&KdpLoggerThreadEvent
, 0, KernelMode
, FALSE
, NULL
);
127 /* Keep KdpCurrentPosition and KdpFreeBytes values in local
128 * variables to avoid their possible change from Producer part,
129 * KdpPrintToLogFile function
131 end
= KdpCurrentPosition
;
134 /* Now securely calculate values, based on local variables */
135 beg
= (end
+ num
) % KdpBufferSize
;
136 num
= KdpBufferSize
- num
;
144 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
145 KdpDebugBuffer
+ beg
, num
, NULL
, NULL
);
149 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
150 KdpDebugBuffer
+ beg
, KdpBufferSize
- beg
, NULL
, NULL
);
152 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
153 KdpDebugBuffer
, end
, NULL
, NULL
);
156 (VOID
)InterlockedExchangeAddUL(&KdpFreeBytes
, num
);
162 KdpPrintToLogFile(PCH String
,
168 if (KdpDebugBuffer
== NULL
) return;
170 /* Acquire the printing spinlock without waiting at raised IRQL */
173 /* Wait when the spinlock becomes available */
174 while (!KeTestSpinLock(&KdpDebugLogSpinLock
));
176 /* Spinlock was free, raise IRQL */
177 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
179 /* Try to get the spinlock */
180 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock
))
183 /* Someone else got the spinlock, lower IRQL back */
184 KeLowerIrql(OldIrql
);
187 beg
= KdpCurrentPosition
;
189 if (StringLength
< num
)
194 end
= (beg
+ num
) % KdpBufferSize
;
195 KdpCurrentPosition
= end
;
200 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, num
);
204 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, KdpBufferSize
- beg
);
205 RtlCopyMemory(KdpDebugBuffer
, String
+ KdpBufferSize
- beg
, end
);
209 /* Release spinlock */
210 KiReleaseSpinLock(&KdpDebugLogSpinLock
);
213 KeLowerIrql(OldIrql
);
215 /* Signal the logger thread */
216 if (OldIrql
<= DISPATCH_LEVEL
&& KdpLoggingEnabled
)
217 KeSetEvent(&KdpLoggerThreadEvent
, 0, FALSE
);
223 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable
,
227 UNICODE_STRING FileName
;
228 OBJECT_ATTRIBUTES ObjectAttributes
;
229 IO_STATUS_BLOCK Iosb
;
234 if (!KdpDebugMode
.File
) return;
238 KdComPortInUse
= NULL
;
240 /* Write out the functions that we support for now */
241 DispatchTable
->KdpInitRoutine
= KdpInitDebugLog
;
242 DispatchTable
->KdpPrintRoutine
= KdpPrintToLogFile
;
244 /* Register as a Provider */
245 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
248 else if (BootPhase
== 1)
250 /* Allocate a buffer for debug log */
251 KdpDebugBuffer
= ExAllocatePool(NonPagedPool
, KdpBufferSize
);
252 KdpFreeBytes
= KdpBufferSize
;
254 /* Initialize spinlock */
255 KeInitializeSpinLock(&KdpDebugLogSpinLock
);
257 /* Display separator + ReactOS version at start of the debug log */
258 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
259 KdpPrintBanner(MemSizeMBs
);
261 else if (BootPhase
== 2)
263 HalDisplayString("\r\n File log debugging enabled\r\n\r\n");
265 else if (BootPhase
== 3)
267 /* Setup the log name */
268 Status
= RtlAnsiStringToUnicodeString(&FileName
, &KdpLogFileName
, TRUE
);
269 if (!NT_SUCCESS(Status
)) return;
271 InitializeObjectAttributes(&ObjectAttributes
,
277 /* Create the log file */
278 Status
= NtCreateFile(&KdpLogFileHandle
,
279 FILE_APPEND_DATA
| SYNCHRONIZE
,
283 FILE_ATTRIBUTE_NORMAL
,
286 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
290 RtlFreeUnicodeString(&FileName
);
292 if (!NT_SUCCESS(Status
)) return;
294 KeInitializeEvent(&KdpLoggerThreadEvent
, SynchronizationEvent
, TRUE
);
296 /* Create the logger thread */
297 Status
= PsCreateSystemThread(&ThreadHandle
,
305 if (!NT_SUCCESS(Status
)) return;
308 NtSetInformationThread(ThreadHandle
,
315 /* SERIAL FUNCTIONS **********************************************************/
319 KdpSerialDebugPrint(LPSTR Message
,
323 PCHAR pch
= (PCHAR
) Message
;
325 /* Acquire the printing spinlock without waiting at raised IRQL */
328 /* Wait when the spinlock becomes available */
329 while (!KeTestSpinLock(&KdpSerialSpinLock
));
331 /* Spinlock was free, raise IRQL */
332 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
334 /* Try to get the spinlock */
335 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock
))
338 /* Someone else got the spinlock, lower IRQL back */
339 KeLowerIrql(OldIrql
);
342 /* Output the message */
343 while (pch
< Message
+ Length
&& *pch
!= '\0')
347 KdPortPutByteEx(&SerialPortInfo
, '\r');
349 KdPortPutByteEx(&SerialPortInfo
, *pch
);
353 /* Release spinlock */
354 KiReleaseSpinLock(&KdpSerialSpinLock
);
357 KeLowerIrql(OldIrql
);
362 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable
,
366 if (!KdpDebugMode
.Serial
) return;
370 /* Write out the functions that we support for now */
371 DispatchTable
->KdpInitRoutine
= KdpSerialInit
;
372 DispatchTable
->KdpPrintRoutine
= KdpSerialDebugPrint
;
374 /* Initialize the Port */
375 if (!KdPortInitializeEx(&SerialPortInfo
, SerialPortNumber
))
377 KdpDebugMode
.Serial
= FALSE
;
380 KdComPortInUse
= SerialPortInfo
.Address
;
382 /* Initialize spinlock */
383 KeInitializeSpinLock(&KdpSerialSpinLock
);
385 /* Register as a Provider */
386 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
388 /* Display separator + ReactOS version at start of the debug log */
389 MemSizeMBs
= KdpGetMemorySizeInMBs(KeLoaderBlock
);
390 KdpPrintBanner(MemSizeMBs
);
392 else if (BootPhase
== 2)
394 HalDisplayString("\r\n Serial debugging enabled\r\n\r\n");
398 /* SCREEN FUNCTIONS **********************************************************/
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.
408 KdpScreenPrint(LPSTR Message
,
413 PCHAR pch
= (PCHAR
) Message
;
415 while (pch
< Message
+ Length
&& *pch
)
419 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
420 if(KdpScreenLineLength
> 0)
422 /* Remove last character from buffer */
423 KdpScreenLineBuffer
[--KdpScreenLineLength
] = '\0';
424 KdpScreenLineBufferPos
= KdpScreenLineLength
;
426 /* Clear row and print line again */
427 HalDisplayString("\r");
428 HalDisplayString(KdpScreenLineBuffer
);
433 KdpScreenLineBuffer
[KdpScreenLineLength
++] = *pch
;
434 KdpScreenLineBuffer
[KdpScreenLineLength
] = '\0';
437 if(*pch
== '\n' || KdpScreenLineLength
== KdpScreenLineLengthDefault
)
439 /* Print buffered characters */
440 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
441 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
443 /* Clear line buffer */
444 KdpScreenLineBuffer
[0] = '\0';
445 KdpScreenLineLength
= KdpScreenLineBufferPos
= 0;
451 /* Print buffered characters */
452 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
454 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
455 KdpScreenLineBufferPos
= KdpScreenLineLength
;
458 /* Dmesg: store Message in the buffer to show it later */
459 if (KdbpIsInDmesgMode
)
462 if (KdpDmesgBuffer
== NULL
)
465 /* Acquire the printing spinlock without waiting at raised IRQL */
468 /* Wait when the spinlock becomes available */
469 while (!KeTestSpinLock(&KdpDmesgLogSpinLock
));
471 /* Spinlock was free, raise IRQL */
472 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
474 /* Try to get the spinlock */
475 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock
))
478 /* Someone else got the spinlock, lower IRQL back */
479 KeLowerIrql(OldIrql
);
482 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
483 * set num to min(KdpDmesgFreeBytes, Length).
485 num
= (Length
< KdpDmesgFreeBytes
) ? Length
: KdpDmesgFreeBytes
;
486 beg
= KdpDmesgCurrentPosition
;
489 end
= (beg
+ num
) % KdpDmesgBufferSize
;
492 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, Length
);
496 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, KdpDmesgBufferSize
- beg
);
497 RtlCopyMemory(KdpDmesgBuffer
, Message
+ (KdpDmesgBufferSize
- beg
), end
);
499 KdpDmesgCurrentPosition
= end
;
501 /* Counting the total bytes written */
502 KdbDmesgTotalWritten
+= num
;
505 /* Release spinlock */
506 KiReleaseSpinLock(&KdpDmesgLogSpinLock
);
509 KeLowerIrql(OldIrql
);
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.
519 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable
,
523 if (!KdpDebugMode
.Screen
) return;
527 /* Write out the functions that we support for now */
528 DispatchTable
->KdpInitRoutine
= KdpScreenInit
;
529 DispatchTable
->KdpPrintRoutine
= KdpScreenPrint
;
531 /* Register as a Provider */
532 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
534 else if (BootPhase
== 1)
536 /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
537 * see kdbp_cli.c:KdbpCmdDmesg()/2
539 KdpDmesgBuffer
= ExAllocatePool(NonPagedPool
, KdpDmesgBufferSize
+ 1);
540 RtlZeroMemory(KdpDmesgBuffer
, KdpDmesgBufferSize
+ 1);
541 KdpDmesgFreeBytes
= KdpDmesgBufferSize
;
542 KdbDmesgTotalWritten
= 0;
544 /* Take control of the display */
545 InbvAcquireDisplayOwnership();
547 InbvSolidColorFill(0, 0, 639, 479, 0);
548 InbvSetTextColor(15);
549 InbvSetScrollRegion(0, 0, 639, 479);
550 InbvInstallDisplayStringFilter(NULL
);
551 InbvEnableDisplayString(TRUE
);
553 /* Initialize spinlock */
554 KeInitializeSpinLock(&KdpDmesgLogSpinLock
);
556 /* Display separator + ReactOS version at start of the debug log */
557 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
558 KdpPrintBanner(MemSizeMBs
);
560 else if (BootPhase
== 2)
562 HalDisplayString("\r\n Screen debugging enabled\r\n\r\n");
566 /* GENERAL FUNCTIONS *********************************************************/
571 _In_reads_bytes_(Length
) PCHAR UnsafeString
,
573 _In_ KPROCESSOR_MODE PreviousMode
)
575 PLIST_ENTRY CurrentEntry
;
576 PKD_DISPATCH_TABLE CurrentTable
;
578 CHAR StringBuffer
[512];
580 if (!KdpDebugMode
.Value
) return 0;
582 Length
= min(Length
, sizeof(StringBuffer
));
584 if (PreviousMode
!= KernelMode
)
588 ProbeForRead(UnsafeString
, Length
, 1);
589 String
= StringBuffer
;
590 RtlCopyMemory(String
, UnsafeString
, Length
);
592 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
600 String
= UnsafeString
;
603 /* Call the registered handlers */
604 CurrentEntry
= KdProviders
.Flink
;
605 while (CurrentEntry
!= &KdProviders
)
607 /* Get the current table */
608 CurrentTable
= CONTAINING_RECORD(CurrentEntry
,
613 CurrentTable
->KdpPrintRoutine(String
, Length
);
616 CurrentEntry
= CurrentEntry
->Flink
;
619 /* Call the Wrapper Routine */
620 if (WrapperTable
.KdpPrintRoutine
)
621 WrapperTable
.KdpPrintRoutine(String
, Length
);
623 /* Return the Length */