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 KD_PORT_INFORMATION SerialPortInfo
= { DEFAULT_DEBUG_PORT
, DEFAULT_DEBUG_BAUD_RATE
, 0 };
31 /* Current Port in use. FIXME: Do we support more then one? */
34 #define KdpScreenLineLenght 80
35 CHAR KdpScreenLineBuffer
[KdpScreenLineLenght
+ 1] = "";
36 ULONG KdpScreenLineBufferPos
= 0, KdpScreenLineLength
= 0;
38 const ULONG KdpDmesgBufferSize
= 128 * 1024; // 512*1024; // 5*1024*1024;
39 PCHAR KdpDmesgBuffer
= NULL
;
40 volatile ULONG KdpDmesgCurrentPosition
= 0;
41 volatile ULONG KdpDmesgFreeBytes
= 0;
42 volatile ULONG KdbDmesgTotalWritten
= 0;
43 KSPIN_LOCK KdpDmesgLogSpinLock
;
44 volatile BOOLEAN KdbpIsInDmesgMode
= FALSE
;
46 /* UTILITY FUNCTIONS *********************************************************/
49 * Get the total size of the memory before
50 * Mm is initialized, by counting the number
51 * of physical pages. Useful for debug logging.
53 * Strongly inspired by:
54 * mm\ARM3\mminit.c : MiScanMemoryDescriptors(...)
58 KdpGetMemorySizeInMBs(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
60 PLIST_ENTRY ListEntry
;
61 PMEMORY_ALLOCATION_DESCRIPTOR Descriptor
;
62 SIZE_T NumberOfPhysicalPages
= 0;
64 /* Loop the memory descriptors */
65 for (ListEntry
= LoaderBlock
->MemoryDescriptorListHead
.Flink
;
66 ListEntry
!= &LoaderBlock
->MemoryDescriptorListHead
;
67 ListEntry
= ListEntry
->Flink
)
69 /* Get the descriptor */
70 Descriptor
= CONTAINING_RECORD(ListEntry
,
71 MEMORY_ALLOCATION_DESCRIPTOR
,
74 /* Check if this is invisible memory */
75 if ((Descriptor
->MemoryType
== LoaderFirmwarePermanent
) ||
76 (Descriptor
->MemoryType
== LoaderSpecialMemory
) ||
77 (Descriptor
->MemoryType
== LoaderHALCachedMemory
) ||
78 (Descriptor
->MemoryType
== LoaderBBTMemory
))
80 /* Skip this descriptor */
84 /* Check if this is bad memory */
85 if (Descriptor
->MemoryType
!= LoaderBad
)
87 /* Count this in the total of pages */
88 NumberOfPhysicalPages
+= Descriptor
->PageCount
;
92 return NumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
95 /* FILE DEBUG LOG FUNCTIONS **************************************************/
99 KdpLoggerThread(PVOID Context
)
102 IO_STATUS_BLOCK Iosb
;
104 KdpLoggingEnabled
= TRUE
;
108 KeWaitForSingleObject(&KdpLoggerThreadEvent
, 0, KernelMode
, FALSE
, NULL
);
111 /* Keep KdpCurrentPosition and KdpFreeBytes values in local
112 * variables to avoid their possible change from Producer part,
113 * KdpPrintToLogFile function
115 end
= KdpCurrentPosition
;
118 /* Now securely calculate values, based on local variables */
119 beg
= (end
+ num
) % KdpBufferSize
;
120 num
= KdpBufferSize
- num
;
128 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
129 KdpDebugBuffer
+ beg
, num
, NULL
, NULL
);
133 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
134 KdpDebugBuffer
+ beg
, KdpBufferSize
- beg
, NULL
, NULL
);
136 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
137 KdpDebugBuffer
, end
, NULL
, NULL
);
140 (VOID
)InterlockedExchangeAddUL(&KdpFreeBytes
, num
);
146 KdpPrintToLogFile(PCH String
,
152 if (KdpDebugBuffer
== NULL
) return;
154 /* Acquire the printing spinlock without waiting at raised IRQL */
157 /* Wait when the spinlock becomes available */
158 while (!KeTestSpinLock(&KdpDebugLogSpinLock
));
160 /* Spinlock was free, raise IRQL */
161 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
163 /* Try to get the spinlock */
164 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock
))
167 /* Someone else got the spinlock, lower IRQL back */
168 KeLowerIrql(OldIrql
);
171 beg
= KdpCurrentPosition
;
173 if (StringLength
< num
)
178 end
= (beg
+ num
) % KdpBufferSize
;
179 KdpCurrentPosition
= end
;
184 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, num
);
188 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, KdpBufferSize
- beg
);
189 RtlCopyMemory(KdpDebugBuffer
, String
+ KdpBufferSize
- beg
, end
);
193 /* Release spinlock */
194 KiReleaseSpinLock(&KdpDebugLogSpinLock
);
197 KeLowerIrql(OldIrql
);
199 /* Signal the logger thread */
200 if (OldIrql
<= DISPATCH_LEVEL
&& KdpLoggingEnabled
)
201 KeSetEvent(&KdpLoggerThreadEvent
, 0, FALSE
);
207 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable
,
211 UNICODE_STRING FileName
;
212 OBJECT_ATTRIBUTES ObjectAttributes
;
213 IO_STATUS_BLOCK Iosb
;
218 if (!KdpDebugMode
.File
) return;
222 KdComPortInUse
= NULL
;
224 /* Write out the functions that we support for now */
225 DispatchTable
->KdpInitRoutine
= KdpInitDebugLog
;
226 DispatchTable
->KdpPrintRoutine
= KdpPrintToLogFile
;
228 /* Register as a Provider */
229 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
232 else if (BootPhase
== 1)
234 /* Allocate a buffer for debug log */
235 KdpDebugBuffer
= ExAllocatePool(NonPagedPool
, KdpBufferSize
);
236 KdpFreeBytes
= KdpBufferSize
;
238 /* Initialize spinlock */
239 KeInitializeSpinLock(&KdpDebugLogSpinLock
);
241 /* Display separator + ReactOS version at start of the debug log */
242 DPRINT1("---------------------------------------------------------------\n");
243 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
244 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
245 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
247 else if (BootPhase
== 2)
249 HalDisplayString("\n File log debugging enabled\n\n");
251 else if (BootPhase
== 3)
253 /* Setup the log name */
254 Status
= RtlAnsiStringToUnicodeString(&FileName
, &KdpLogFileName
, TRUE
);
255 if (!NT_SUCCESS(Status
)) return;
257 InitializeObjectAttributes(&ObjectAttributes
,
263 /* Create the log file */
264 Status
= NtCreateFile(&KdpLogFileHandle
,
265 FILE_APPEND_DATA
| SYNCHRONIZE
,
269 FILE_ATTRIBUTE_NORMAL
,
272 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
276 RtlFreeUnicodeString(&FileName
);
278 if (!NT_SUCCESS(Status
)) return;
280 KeInitializeEvent(&KdpLoggerThreadEvent
, SynchronizationEvent
, TRUE
);
282 /* Create the logger thread */
283 Status
= PsCreateSystemThread(&ThreadHandle
,
291 if (!NT_SUCCESS(Status
)) return;
294 NtSetInformationThread(ThreadHandle
,
301 /* SERIAL FUNCTIONS **********************************************************/
305 KdpSerialDebugPrint(LPSTR Message
,
309 PCHAR pch
= (PCHAR
) Message
;
311 /* Acquire the printing spinlock without waiting at raised IRQL */
314 /* Wait when the spinlock becomes available */
315 while (!KeTestSpinLock(&KdpSerialSpinLock
));
317 /* Spinlock was free, raise IRQL */
318 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
320 /* Try to get the spinlock */
321 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock
))
324 /* Someone else got the spinlock, lower IRQL back */
325 KeLowerIrql(OldIrql
);
328 /* Output the message */
333 KdPortPutByteEx(&SerialPortInfo
, '\r');
335 KdPortPutByteEx(&SerialPortInfo
, *pch
);
339 /* Release spinlock */
340 KiReleaseSpinLock(&KdpSerialSpinLock
);
343 KeLowerIrql(OldIrql
);
348 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable
,
352 if (!KdpDebugMode
.Serial
) return;
356 /* Write out the functions that we support for now */
357 DispatchTable
->KdpInitRoutine
= KdpSerialInit
;
358 DispatchTable
->KdpPrintRoutine
= KdpSerialDebugPrint
;
360 /* Initialize the Port */
361 if (!KdPortInitializeEx(&SerialPortInfo
, 0, 0))
363 KdpDebugMode
.Serial
= FALSE
;
366 KdComPortInUse
= (PUCHAR
)(ULONG_PTR
)SerialPortInfo
.BaseAddress
;
368 /* Initialize spinlock */
369 KeInitializeSpinLock(&KdpSerialSpinLock
);
371 /* Register as a Provider */
372 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
374 /* Display separator + ReactOS version at start of the debug log */
375 DPRINT1("-----------------------------------------------------\n");
376 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
377 MemSizeMBs
= KdpGetMemorySizeInMBs(KeLoaderBlock
);
378 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
379 DPRINT1("Command Line: %s\n", KeLoaderBlock
->LoadOptions
);
380 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock
->ArcBootDeviceName
,
381 KeLoaderBlock
->NtHalPathName
,
382 KeLoaderBlock
->ArcHalDeviceName
,
383 KeLoaderBlock
->NtBootPathName
);
385 else if (BootPhase
== 2)
387 HalDisplayString("\n Serial debugging enabled\n\n");
391 /* SCREEN FUNCTIONS **********************************************************/
394 * Screen debug logger function KdpScreenPrint() writes text messages into
395 * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
396 * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
397 * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
401 KdpScreenPrint(LPSTR Message
,
406 PCHAR pch
= (PCHAR
) Message
;
412 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
413 if(KdpScreenLineLength
> 0)
415 /* Remove last character from buffer */
416 KdpScreenLineBuffer
[--KdpScreenLineLength
] = '\0';
417 KdpScreenLineBufferPos
= KdpScreenLineLength
;
419 /* Clear row and print line again */
420 HalDisplayString("\r");
421 HalDisplayString(KdpScreenLineBuffer
);
426 KdpScreenLineBuffer
[KdpScreenLineLength
++] = *pch
;
427 KdpScreenLineBuffer
[KdpScreenLineLength
] = '\0';
430 if(*pch
== '\n' || KdpScreenLineLength
== KdpScreenLineLenght
)
432 /* Print buffered characters */
433 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
434 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
436 /* Clear line buffer */
437 KdpScreenLineBuffer
[0] = '\0';
438 KdpScreenLineLength
= KdpScreenLineBufferPos
= 0;
444 /* Print buffered characters */
445 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
447 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
448 KdpScreenLineBufferPos
= KdpScreenLineLength
;
451 /* Dmesg: store Message in the buffer to show it later */
452 if (KdbpIsInDmesgMode
)
455 if (KdpDmesgBuffer
== NULL
)
458 /* Acquire the printing spinlock without waiting at raised IRQL */
461 /* Wait when the spinlock becomes available */
462 while (!KeTestSpinLock(&KdpDmesgLogSpinLock
));
464 /* Spinlock was free, raise IRQL */
465 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
467 /* Try to get the spinlock */
468 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock
))
471 /* Someone else got the spinlock, lower IRQL back */
472 KeLowerIrql(OldIrql
);
475 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
476 * set num to min(KdpDmesgFreeBytes, Length).
478 num
= (Length
< KdpDmesgFreeBytes
) ? Length
: KdpDmesgFreeBytes
;
479 beg
= KdpDmesgCurrentPosition
;
482 end
= (beg
+ num
) % KdpDmesgBufferSize
;
485 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, Length
);
489 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, KdpDmesgBufferSize
- beg
);
490 RtlCopyMemory(KdpDmesgBuffer
, Message
+ (KdpDmesgBufferSize
- beg
), end
);
492 KdpDmesgCurrentPosition
= end
;
494 /* Counting the total bytes written */
495 KdbDmesgTotalWritten
+= num
;
498 /* Release spinlock */
499 KiReleaseSpinLock(&KdpDmesgLogSpinLock
);
502 KeLowerIrql(OldIrql
);
504 /* Optional step(?): find out a way to notify about buffer exhaustion,
505 * and possibly fall into kbd to use dmesg command: user will read
506 * debug messages before they will be wiped over by next writes.
512 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable
,
516 if (!KdpDebugMode
.Screen
) return;
520 /* Write out the functions that we support for now */
521 DispatchTable
->KdpInitRoutine
= KdpScreenInit
;
522 DispatchTable
->KdpPrintRoutine
= KdpScreenPrint
;
524 /* Register as a Provider */
525 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
527 else if (BootPhase
== 1)
529 /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
530 * see kdbp_cli.c:KdbpCmdDmesg()/2
532 KdpDmesgBuffer
= ExAllocatePool(NonPagedPool
, KdpDmesgBufferSize
+ 1);
533 RtlZeroMemory(KdpDmesgBuffer
, KdpDmesgBufferSize
+ 1);
534 KdpDmesgFreeBytes
= KdpDmesgBufferSize
;
535 KdbDmesgTotalWritten
= 0;
537 /* Take control of the display */
538 InbvAcquireDisplayOwnership();
540 InbvSolidColorFill(0, 0, 639, 479, 0);
541 InbvSetTextColor(15);
542 InbvSetScrollRegion(0, 0, 639, 479);
543 InbvInstallDisplayStringFilter(NULL
);
544 InbvEnableDisplayString(TRUE
);
546 /* Initialize spinlock */
547 KeInitializeSpinLock(&KdpDmesgLogSpinLock
);
549 /* Display separator + ReactOS version at start of the debug log */
550 DPRINT1("-----------------------------------------------------\n");
551 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
552 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
553 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
554 DPRINT1("Command Line: %s\n", KeLoaderBlock
->LoadOptions
);
555 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock
->ArcBootDeviceName
,
556 KeLoaderBlock
->NtHalPathName
,
557 KeLoaderBlock
->ArcHalDeviceName
,
558 KeLoaderBlock
->NtBootPathName
);
560 else if (BootPhase
== 2)
562 HalDisplayString("\n Screen debugging enabled\n\n");
566 /* GENERAL FUNCTIONS *********************************************************/
570 KdpPrintString(LPSTR String
,
573 PLIST_ENTRY CurrentEntry
;
574 PKD_DISPATCH_TABLE CurrentTable
;
576 if (!KdpDebugMode
.Value
) return 0;
578 /* Call the registered handlers */
579 CurrentEntry
= KdProviders
.Flink
;
580 while (CurrentEntry
!= &KdProviders
)
582 /* Get the current table */
583 CurrentTable
= CONTAINING_RECORD(CurrentEntry
,
588 CurrentTable
->KdpPrintRoutine(String
, Length
);
591 CurrentEntry
= CurrentEntry
->Flink
;
594 /* Call the Wrapper Routine */
595 if (WrapperTable
.KdpPrintRoutine
)
596 WrapperTable
.KdpPrintRoutine(String
, Length
);
598 /* Return the Length */