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 /* FILE DEBUG LOG FUNCTIONS **************************************************/
50 KdpLoggerThread(PVOID Context
)
55 KdpLoggingEnabled
= TRUE
;
59 KeWaitForSingleObject(&KdpLoggerThreadEvent
, 0, KernelMode
, FALSE
, NULL
);
62 /* Keep KdpCurrentPosition and KdpFreeBytes values in local
63 * variables to avoid their possible change from Producer part,
64 * KdpPrintToLogFile function
66 end
= KdpCurrentPosition
;
69 /* Now securely calculate values, based on local variables */
70 beg
= (end
+ num
) % KdpBufferSize
;
71 num
= KdpBufferSize
- num
;
79 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
80 KdpDebugBuffer
+ beg
, num
, NULL
, NULL
);
84 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
85 KdpDebugBuffer
+ beg
, KdpBufferSize
- beg
, NULL
, NULL
);
87 NtWriteFile(KdpLogFileHandle
, NULL
, NULL
, NULL
, &Iosb
,
88 KdpDebugBuffer
, end
, NULL
, NULL
);
91 (VOID
)InterlockedExchangeAddUL(&KdpFreeBytes
, num
);
97 KdpPrintToLogFile(PCH String
,
103 if (KdpDebugBuffer
== NULL
) return;
105 /* Acquire the printing spinlock without waiting at raised IRQL */
108 /* Wait when the spinlock becomes available */
109 while (!KeTestSpinLock(&KdpDebugLogSpinLock
));
111 /* Spinlock was free, raise IRQL */
112 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
114 /* Try to get the spinlock */
115 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock
))
118 /* Someone else got the spinlock, lower IRQL back */
119 KeLowerIrql(OldIrql
);
122 beg
= KdpCurrentPosition
;
124 if (StringLength
< num
)
129 end
= (beg
+ num
) % KdpBufferSize
;
130 KdpCurrentPosition
= end
;
135 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, num
);
139 RtlCopyMemory(KdpDebugBuffer
+ beg
, String
, KdpBufferSize
- beg
);
140 RtlCopyMemory(KdpDebugBuffer
, String
+ KdpBufferSize
- beg
, end
);
144 /* Release spinlock */
145 KiReleaseSpinLock(&KdpDebugLogSpinLock
);
148 KeLowerIrql(OldIrql
);
150 /* Signal the logger thread */
151 if (OldIrql
<= DISPATCH_LEVEL
&& KdpLoggingEnabled
)
152 KeSetEvent(&KdpLoggerThreadEvent
, 0, FALSE
);
158 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable
,
162 UNICODE_STRING FileName
;
163 OBJECT_ATTRIBUTES ObjectAttributes
;
164 IO_STATUS_BLOCK Iosb
;
169 if (!KdpDebugMode
.File
) return;
173 KdComPortInUse
= NULL
;
175 /* Write out the functions that we support for now */
176 DispatchTable
->KdpInitRoutine
= KdpInitDebugLog
;
177 DispatchTable
->KdpPrintRoutine
= KdpPrintToLogFile
;
179 /* Register as a Provider */
180 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
183 else if (BootPhase
== 1)
185 /* Allocate a buffer for debug log */
186 KdpDebugBuffer
= ExAllocatePool(NonPagedPool
, KdpBufferSize
);
187 KdpFreeBytes
= KdpBufferSize
;
189 /* Initialize spinlock */
190 KeInitializeSpinLock(&KdpDebugLogSpinLock
);
192 /* Display separator + ReactOS version at start of the debug log */
193 DPRINT1("---------------------------------------------------------------\n");
194 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
195 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
196 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
198 else if (BootPhase
== 2)
200 HalDisplayString("\n File log debugging enabled\n\n");
202 else if (BootPhase
== 3)
204 /* Setup the log name */
205 Status
= RtlAnsiStringToUnicodeString(&FileName
, &KdpLogFileName
, TRUE
);
206 if (!NT_SUCCESS(Status
)) return;
208 InitializeObjectAttributes(&ObjectAttributes
,
214 /* Create the log file */
215 Status
= NtCreateFile(&KdpLogFileHandle
,
216 FILE_APPEND_DATA
| SYNCHRONIZE
,
220 FILE_ATTRIBUTE_NORMAL
,
223 FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_NONALERT
,
227 RtlFreeUnicodeString(&FileName
);
229 if (!NT_SUCCESS(Status
)) return;
231 KeInitializeEvent(&KdpLoggerThreadEvent
, SynchronizationEvent
, TRUE
);
233 /* Create the logger thread */
234 Status
= PsCreateSystemThread(&ThreadHandle
,
242 if (!NT_SUCCESS(Status
)) return;
245 NtSetInformationThread(ThreadHandle
,
252 /* SERIAL FUNCTIONS **********************************************************/
256 KdpSerialDebugPrint(LPSTR Message
,
260 PCHAR pch
= (PCHAR
) Message
;
262 /* Acquire the printing spinlock without waiting at raised IRQL */
265 /* Wait when the spinlock becomes available */
266 while (!KeTestSpinLock(&KdpSerialSpinLock
));
268 /* Spinlock was free, raise IRQL */
269 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
271 /* Try to get the spinlock */
272 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock
))
275 /* Someone else got the spinlock, lower IRQL back */
276 KeLowerIrql(OldIrql
);
279 /* Output the message */
284 KdPortPutByteEx(&SerialPortInfo
, '\r');
286 KdPortPutByteEx(&SerialPortInfo
, *pch
);
290 /* Release spinlock */
291 KiReleaseSpinLock(&KdpSerialSpinLock
);
294 KeLowerIrql(OldIrql
);
299 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable
,
303 if (!KdpDebugMode
.Serial
) return;
307 /* Write out the functions that we support for now */
308 DispatchTable
->KdpInitRoutine
= KdpSerialInit
;
309 DispatchTable
->KdpPrintRoutine
= KdpSerialDebugPrint
;
311 /* Initialize the Port */
312 if (!KdPortInitializeEx(&SerialPortInfo
, 0, 0))
314 KdpDebugMode
.Serial
= FALSE
;
317 KdComPortInUse
= (PUCHAR
)(ULONG_PTR
)SerialPortInfo
.BaseAddress
;
319 /* Initialize spinlock */
320 KeInitializeSpinLock(&KdpSerialSpinLock
);
322 /* Register as a Provider */
323 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
325 /* Display separator + ReactOS version at start of the debug log */
326 DPRINT1("-----------------------------------------------------\n");
327 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
328 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
329 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
330 DPRINT1("Command Line: %s\n", KeLoaderBlock
->LoadOptions
);
331 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock
->ArcBootDeviceName
,
332 KeLoaderBlock
->NtHalPathName
,
333 KeLoaderBlock
->ArcHalDeviceName
,
334 KeLoaderBlock
->NtBootPathName
);
336 else if (BootPhase
== 2)
338 HalDisplayString("\n Serial debugging enabled\n\n");
342 /* SCREEN FUNCTIONS **********************************************************/
345 * Screen debug logger function KdpScreenPrint() writes text messages into
346 * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
347 * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
348 * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
352 KdpScreenPrint(LPSTR Message
,
357 PCHAR pch
= (PCHAR
) Message
;
363 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
364 if(KdpScreenLineLength
> 0)
366 /* Remove last character from buffer */
367 KdpScreenLineBuffer
[--KdpScreenLineLength
] = '\0';
368 KdpScreenLineBufferPos
= KdpScreenLineLength
;
370 /* Clear row and print line again */
371 HalDisplayString("\r");
372 HalDisplayString(KdpScreenLineBuffer
);
377 KdpScreenLineBuffer
[KdpScreenLineLength
++] = *pch
;
378 KdpScreenLineBuffer
[KdpScreenLineLength
] = '\0';
381 if(*pch
== '\n' || KdpScreenLineLength
== KdpScreenLineLenght
)
383 /* Print buffered characters */
384 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
385 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
387 /* Clear line buffer */
388 KdpScreenLineBuffer
[0] = '\0';
389 KdpScreenLineLength
= KdpScreenLineBufferPos
= 0;
395 /* Print buffered characters */
396 if(KdpScreenLineBufferPos
!= KdpScreenLineLength
)
398 HalDisplayString(KdpScreenLineBuffer
+ KdpScreenLineBufferPos
);
399 KdpScreenLineBufferPos
= KdpScreenLineLength
;
402 /* Dmesg: store Message in the buffer to show it later */
403 if (KdbpIsInDmesgMode
)
406 if (KdpDmesgBuffer
== NULL
)
409 /* Acquire the printing spinlock without waiting at raised IRQL */
412 /* Wait when the spinlock becomes available */
413 while (!KeTestSpinLock(&KdpDmesgLogSpinLock
));
415 /* Spinlock was free, raise IRQL */
416 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
418 /* Try to get the spinlock */
419 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock
))
422 /* Someone else got the spinlock, lower IRQL back */
423 KeLowerIrql(OldIrql
);
426 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
427 * set num to min(KdpDmesgFreeBytes, Length).
429 num
= (Length
< KdpDmesgFreeBytes
) ? Length
: KdpDmesgFreeBytes
;
430 beg
= KdpDmesgCurrentPosition
;
433 end
= (beg
+ num
) % KdpDmesgBufferSize
;
436 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, Length
);
440 RtlCopyMemory(KdpDmesgBuffer
+ beg
, Message
, KdpDmesgBufferSize
- beg
);
441 RtlCopyMemory(KdpDmesgBuffer
, Message
+ (KdpDmesgBufferSize
- beg
), end
);
443 KdpDmesgCurrentPosition
= end
;
445 /* Counting the total bytes written */
446 KdbDmesgTotalWritten
+= num
;
449 /* Release spinlock */
450 KiReleaseSpinLock(&KdpDmesgLogSpinLock
);
453 KeLowerIrql(OldIrql
);
455 /* Optional step(?): find out a way to notify about buffer exhaustion,
456 * and possibly fall into kbd to use dmesg command: user will read
457 * debug messages before they will be wiped over by next writes.
463 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable
,
467 if (!KdpDebugMode
.Screen
) return;
471 /* Write out the functions that we support for now */
472 DispatchTable
->KdpInitRoutine
= KdpScreenInit
;
473 DispatchTable
->KdpPrintRoutine
= KdpScreenPrint
;
475 /* Register as a Provider */
476 InsertTailList(&KdProviders
, &DispatchTable
->KdProvidersList
);
478 else if (BootPhase
== 1)
480 /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
481 * see kdbp_cli.c:KdbpCmdDmesg()/2
483 KdpDmesgBuffer
= ExAllocatePool(NonPagedPool
, KdpDmesgBufferSize
+ 1);
484 RtlZeroMemory(KdpDmesgBuffer
, KdpDmesgBufferSize
+ 1);
485 KdpDmesgFreeBytes
= KdpDmesgBufferSize
;
486 KdbDmesgTotalWritten
= 0;
488 /* Initialize spinlock */
489 KeInitializeSpinLock(&KdpDmesgLogSpinLock
);
491 /* Display separator + ReactOS version at start of the debug log */
492 DPRINT1("-----------------------------------------------------\n");
493 DPRINT1("ReactOS "KERNEL_VERSION_STR
" (Build "KERNEL_VERSION_BUILD_STR
")\n");
494 MemSizeMBs
= MmNumberOfPhysicalPages
* PAGE_SIZE
/ 1024 / 1024;
495 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors
, MemSizeMBs
);
496 DPRINT1("Command Line: %s\n", KeLoaderBlock
->LoadOptions
);
497 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock
->ArcBootDeviceName
,
498 KeLoaderBlock
->NtHalPathName
,
499 KeLoaderBlock
->ArcHalDeviceName
,
500 KeLoaderBlock
->NtBootPathName
);
502 else if (BootPhase
== 2)
504 HalDisplayString("\n Screen debugging enabled\n\n");
508 /* GENERAL FUNCTIONS *********************************************************/
512 KdpPrintString(LPSTR String
,
515 PLIST_ENTRY CurrentEntry
;
516 PKD_DISPATCH_TABLE CurrentTable
;
518 if (!KdpDebugMode
.Value
) return 0;
520 /* Call the registered handlers */
521 CurrentEntry
= KdProviders
.Flink
;
522 while (CurrentEntry
!= &KdProviders
)
524 /* Get the current table */
525 CurrentTable
= CONTAINING_RECORD(CurrentEntry
,
530 CurrentTable
->KdpPrintRoutine(String
, Length
);
533 CurrentEntry
= CurrentEntry
->Flink
;
536 /* Call the Wrapper Routine */
537 if (WrapperTable
.KdpPrintRoutine
)
538 WrapperTable
.KdpPrintRoutine(String
, Length
);
540 /* Return the Length */