[KD] Output the long commit hash as well at the start of the debug log.
[reactos.git] / ntoskrnl / kd / kdio.c
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 return NumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
97 }
98
99 /* FILE DEBUG LOG FUNCTIONS **************************************************/
100
101 VOID
102 NTAPI
103 KdpLoggerThread(PVOID Context)
104 {
105 ULONG beg, end, num;
106 IO_STATUS_BLOCK Iosb;
107
108 KdpLoggingEnabled = TRUE;
109
110 while (TRUE)
111 {
112 KeWaitForSingleObject(&KdpLoggerThreadEvent, 0, KernelMode, FALSE, NULL);
113
114 /* Bug */
115 /* Keep KdpCurrentPosition and KdpFreeBytes values in local
116 * variables to avoid their possible change from Producer part,
117 * KdpPrintToLogFile function
118 */
119 end = KdpCurrentPosition;
120 num = KdpFreeBytes;
121
122 /* Now securely calculate values, based on local variables */
123 beg = (end + num) % KdpBufferSize;
124 num = KdpBufferSize - num;
125
126 /* Nothing to do? */
127 if (num == 0)
128 continue;
129
130 if (end > beg)
131 {
132 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
133 KdpDebugBuffer + beg, num, NULL, NULL);
134 }
135 else
136 {
137 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
138 KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL);
139
140 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
141 KdpDebugBuffer, end, NULL, NULL);
142 }
143
144 (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num);
145 }
146 }
147
148 VOID
149 NTAPI
150 KdpPrintToLogFile(PCH String,
151 ULONG StringLength)
152 {
153 ULONG beg, end, num;
154 KIRQL OldIrql;
155
156 if (KdpDebugBuffer == NULL) return;
157
158 /* Acquire the printing spinlock without waiting at raised IRQL */
159 while (TRUE)
160 {
161 /* Wait when the spinlock becomes available */
162 while (!KeTestSpinLock(&KdpDebugLogSpinLock));
163
164 /* Spinlock was free, raise IRQL */
165 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
166
167 /* Try to get the spinlock */
168 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock))
169 break;
170
171 /* Someone else got the spinlock, lower IRQL back */
172 KeLowerIrql(OldIrql);
173 }
174
175 beg = KdpCurrentPosition;
176 num = KdpFreeBytes;
177 if (StringLength < num)
178 num = StringLength;
179
180 if (num != 0)
181 {
182 end = (beg + num) % KdpBufferSize;
183 KdpCurrentPosition = end;
184 KdpFreeBytes -= num;
185
186 if (end > beg)
187 {
188 RtlCopyMemory(KdpDebugBuffer + beg, String, num);
189 }
190 else
191 {
192 RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg);
193 RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end);
194 }
195 }
196
197 /* Release spinlock */
198 KiReleaseSpinLock(&KdpDebugLogSpinLock);
199
200 /* Lower IRQL */
201 KeLowerIrql(OldIrql);
202
203 /* Signal the logger thread */
204 if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
205 KeSetEvent(&KdpLoggerThreadEvent, 0, FALSE);
206 }
207
208 VOID
209 NTAPI
210 INIT_FUNCTION
211 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
212 ULONG BootPhase)
213 {
214 NTSTATUS Status;
215 UNICODE_STRING FileName;
216 OBJECT_ATTRIBUTES ObjectAttributes;
217 IO_STATUS_BLOCK Iosb;
218 HANDLE ThreadHandle;
219 KPRIORITY Priority;
220 SIZE_T MemSizeMBs;
221
222 if (!KdpDebugMode.File) return;
223
224 if (BootPhase == 0)
225 {
226 KdComPortInUse = NULL;
227
228 /* Write out the functions that we support for now */
229 DispatchTable->KdpInitRoutine = KdpInitDebugLog;
230 DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
231
232 /* Register as a Provider */
233 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
234
235 }
236 else if (BootPhase == 1)
237 {
238 /* Allocate a buffer for debug log */
239 KdpDebugBuffer = ExAllocatePool(NonPagedPool, KdpBufferSize);
240 KdpFreeBytes = KdpBufferSize;
241
242 /* Initialize spinlock */
243 KeInitializeSpinLock(&KdpDebugLogSpinLock);
244
245 /* Display separator + ReactOS version at start of the debug log */
246 DPRINT1("-----------------------------------------------------\n");
247 DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
248 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
249 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
250 DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
251 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
252 KeLoaderBlock->NtHalPathName,
253 KeLoaderBlock->ArcHalDeviceName,
254 KeLoaderBlock->NtBootPathName);
255 }
256 else if (BootPhase == 2)
257 {
258 HalDisplayString("\r\n File log debugging enabled\r\n\r\n");
259 }
260 else if (BootPhase == 3)
261 {
262 /* Setup the log name */
263 Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
264 if (!NT_SUCCESS(Status)) return;
265
266 InitializeObjectAttributes(&ObjectAttributes,
267 &FileName,
268 0,
269 NULL,
270 NULL);
271
272 /* Create the log file */
273 Status = NtCreateFile(&KdpLogFileHandle,
274 FILE_APPEND_DATA | SYNCHRONIZE,
275 &ObjectAttributes,
276 &Iosb,
277 NULL,
278 FILE_ATTRIBUTE_NORMAL,
279 FILE_SHARE_READ,
280 FILE_SUPERSEDE,
281 FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
282 NULL,
283 0);
284
285 RtlFreeUnicodeString(&FileName);
286
287 if (!NT_SUCCESS(Status)) return;
288
289 KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE);
290
291 /* Create the logger thread */
292 Status = PsCreateSystemThread(&ThreadHandle,
293 THREAD_ALL_ACCESS,
294 NULL,
295 NULL,
296 NULL,
297 KdpLoggerThread,
298 NULL);
299
300 if (!NT_SUCCESS(Status)) return;
301
302 Priority = 7;
303 NtSetInformationThread(ThreadHandle,
304 ThreadPriority,
305 &Priority,
306 sizeof(Priority));
307 }
308 }
309
310 /* SERIAL FUNCTIONS **********************************************************/
311
312 VOID
313 NTAPI
314 KdpSerialDebugPrint(LPSTR Message,
315 ULONG Length)
316 {
317 KIRQL OldIrql;
318 PCHAR pch = (PCHAR) Message;
319
320 /* Acquire the printing spinlock without waiting at raised IRQL */
321 while (TRUE)
322 {
323 /* Wait when the spinlock becomes available */
324 while (!KeTestSpinLock(&KdpSerialSpinLock));
325
326 /* Spinlock was free, raise IRQL */
327 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
328
329 /* Try to get the spinlock */
330 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock))
331 break;
332
333 /* Someone else got the spinlock, lower IRQL back */
334 KeLowerIrql(OldIrql);
335 }
336
337 /* Output the message */
338 while (*pch != 0)
339 {
340 if (*pch == '\n')
341 {
342 KdPortPutByteEx(&SerialPortInfo, '\r');
343 }
344 KdPortPutByteEx(&SerialPortInfo, *pch);
345 pch++;
346 }
347
348 /* Release spinlock */
349 KiReleaseSpinLock(&KdpSerialSpinLock);
350
351 /* Lower IRQL */
352 KeLowerIrql(OldIrql);
353 }
354
355 VOID
356 NTAPI
357 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
358 ULONG BootPhase)
359 {
360 SIZE_T MemSizeMBs;
361 if (!KdpDebugMode.Serial) return;
362
363 if (BootPhase == 0)
364 {
365 /* Write out the functions that we support for now */
366 DispatchTable->KdpInitRoutine = KdpSerialInit;
367 DispatchTable->KdpPrintRoutine = KdpSerialDebugPrint;
368
369 /* Initialize the Port */
370 if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber))
371 {
372 KdpDebugMode.Serial = FALSE;
373 return;
374 }
375 KdComPortInUse = SerialPortInfo.Address;
376
377 /* Initialize spinlock */
378 KeInitializeSpinLock(&KdpSerialSpinLock);
379
380 /* Register as a Provider */
381 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
382
383 /* Display separator + ReactOS version at start of the debug log */
384 DPRINT1("-----------------------------------------------------\n");
385 DPRINT1("ReactOS " KERNEL_VERSION_STR " (Build " KERNEL_VERSION_BUILD_STR ") (Commit " KERNEL_VERSION_COMMIT_HASH "\n");
386 MemSizeMBs = KdpGetMemorySizeInMBs(KeLoaderBlock);
387 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
388 DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
389 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
390 KeLoaderBlock->NtHalPathName,
391 KeLoaderBlock->ArcHalDeviceName,
392 KeLoaderBlock->NtBootPathName);
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)
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 DPRINT1("-----------------------------------------------------\n");
560 DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
561 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
562 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
563 DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
564 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
565 KeLoaderBlock->NtHalPathName,
566 KeLoaderBlock->ArcHalDeviceName,
567 KeLoaderBlock->NtBootPathName);
568 }
569 else if (BootPhase == 2)
570 {
571 HalDisplayString("\r\n Screen debugging enabled\r\n\r\n");
572 }
573 }
574
575 /* GENERAL FUNCTIONS *********************************************************/
576
577 ULONG
578 NTAPI
579 KdpPrintString(LPSTR String,
580 ULONG Length)
581 {
582 PLIST_ENTRY CurrentEntry;
583 PKD_DISPATCH_TABLE CurrentTable;
584
585 if (!KdpDebugMode.Value) return 0;
586
587 /* Call the registered handlers */
588 CurrentEntry = KdProviders.Flink;
589 while (CurrentEntry != &KdProviders)
590 {
591 /* Get the current table */
592 CurrentTable = CONTAINING_RECORD(CurrentEntry,
593 KD_DISPATCH_TABLE,
594 KdProvidersList);
595
596 /* Call it */
597 CurrentTable->KdpPrintRoutine(String, Length);
598
599 /* Next Table */
600 CurrentEntry = CurrentEntry->Flink;
601 }
602
603 /* Call the Wrapper Routine */
604 if (WrapperTable.KdpPrintRoutine)
605 WrapperTable.KdpPrintRoutine(String, Length);
606
607 /* Return the Length */
608 return Length;
609 }
610
611 /* EOF */