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