xref: /reactos/ntoskrnl/kd/kdio.c (revision 64daf542)
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 /* See also: kd64\kdinit.c */
100 static VOID
101 INIT_FUNCTION
102 KdpPrintBanner(IN SIZE_T MemSizeMBs)
103 {
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);
109 }
110 
111 /* FILE DEBUG LOG FUNCTIONS **************************************************/
112 
113 VOID
114 NTAPI
115 KdpLoggerThread(PVOID Context)
116 {
117     ULONG beg, end, num;
118     IO_STATUS_BLOCK Iosb;
119 
120     KdpLoggingEnabled = TRUE;
121 
122     while (TRUE)
123     {
124         KeWaitForSingleObject(&KdpLoggerThreadEvent, 0, KernelMode, FALSE, NULL);
125 
126         /* Bug */
127         /* Keep KdpCurrentPosition and KdpFreeBytes values in local
128          * variables to avoid their possible change from Producer part,
129          * KdpPrintToLogFile function
130          */
131         end = KdpCurrentPosition;
132         num = KdpFreeBytes;
133 
134         /* Now securely calculate values, based on local variables */
135         beg = (end + num) % KdpBufferSize;
136         num = KdpBufferSize - num;
137 
138         /* Nothing to do? */
139         if (num == 0)
140             continue;
141 
142         if (end > beg)
143         {
144             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
145                         KdpDebugBuffer + beg, num, NULL, NULL);
146         }
147         else
148         {
149             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
150                         KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL);
151 
152             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
153                         KdpDebugBuffer, end, NULL, NULL);
154         }
155 
156         (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num);
157     }
158 }
159 
160 VOID
161 NTAPI
162 KdpPrintToLogFile(PCH String,
163                   ULONG StringLength)
164 {
165     ULONG beg, end, num;
166     KIRQL OldIrql;
167 
168     if (KdpDebugBuffer == NULL) return;
169 
170     /* Acquire the printing spinlock without waiting at raised IRQL */
171     while (TRUE)
172     {
173         /* Wait when the spinlock becomes available */
174         while (!KeTestSpinLock(&KdpDebugLogSpinLock));
175 
176         /* Spinlock was free, raise IRQL */
177         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
178 
179         /* Try to get the spinlock */
180         if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock))
181             break;
182 
183         /* Someone else got the spinlock, lower IRQL back */
184         KeLowerIrql(OldIrql);
185     }
186 
187     beg = KdpCurrentPosition;
188     num = KdpFreeBytes;
189     if (StringLength < num)
190         num = StringLength;
191 
192     if (num != 0)
193     {
194         end = (beg + num) % KdpBufferSize;
195         KdpCurrentPosition = end;
196         KdpFreeBytes -= num;
197 
198         if (end > beg)
199         {
200             RtlCopyMemory(KdpDebugBuffer + beg, String, num);
201         }
202         else
203         {
204             RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg);
205             RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end);
206         }
207     }
208 
209     /* Release spinlock */
210     KiReleaseSpinLock(&KdpDebugLogSpinLock);
211 
212     /* Lower IRQL */
213     KeLowerIrql(OldIrql);
214 
215     /* Signal the logger thread */
216     if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
217         KeSetEvent(&KdpLoggerThreadEvent, 0, FALSE);
218 }
219 
220 VOID
221 NTAPI
222 INIT_FUNCTION
223 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
224                 ULONG BootPhase)
225 {
226     NTSTATUS Status;
227     UNICODE_STRING FileName;
228     OBJECT_ATTRIBUTES ObjectAttributes;
229     IO_STATUS_BLOCK Iosb;
230     HANDLE ThreadHandle;
231     KPRIORITY Priority;
232     SIZE_T MemSizeMBs;
233 
234     if (!KdpDebugMode.File) return;
235 
236     if (BootPhase == 0)
237     {
238         KdComPortInUse = NULL;
239 
240         /* Write out the functions that we support for now */
241         DispatchTable->KdpInitRoutine = KdpInitDebugLog;
242         DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
243 
244         /* Register as a Provider */
245         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
246 
247     }
248     else if (BootPhase == 1)
249     {
250         /* Allocate a buffer for debug log */
251         KdpDebugBuffer = ExAllocatePool(NonPagedPool, KdpBufferSize);
252         KdpFreeBytes = KdpBufferSize;
253 
254         /* Initialize spinlock */
255         KeInitializeSpinLock(&KdpDebugLogSpinLock);
256 
257         /* Display separator + ReactOS version at start of the debug log */
258         MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
259         KdpPrintBanner(MemSizeMBs);
260     }
261     else if (BootPhase == 2)
262     {
263         HalDisplayString("\r\n   File log debugging enabled\r\n\r\n");
264     }
265     else if (BootPhase == 3)
266     {
267         /* Setup the log name */
268         Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
269         if (!NT_SUCCESS(Status)) return;
270 
271         InitializeObjectAttributes(&ObjectAttributes,
272                                    &FileName,
273                                    0,
274                                    NULL,
275                                    NULL);
276 
277         /* Create the log file */
278         Status = NtCreateFile(&KdpLogFileHandle,
279                               FILE_APPEND_DATA | SYNCHRONIZE,
280                               &ObjectAttributes,
281                               &Iosb,
282                               NULL,
283                               FILE_ATTRIBUTE_NORMAL,
284                               FILE_SHARE_READ,
285                               FILE_SUPERSEDE,
286                               FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
287                               NULL,
288                               0);
289 
290         RtlFreeUnicodeString(&FileName);
291 
292         if (!NT_SUCCESS(Status)) return;
293 
294         KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE);
295 
296         /* Create the logger thread */
297         Status = PsCreateSystemThread(&ThreadHandle,
298                                       THREAD_ALL_ACCESS,
299                                       NULL,
300                                       NULL,
301                                       NULL,
302                                       KdpLoggerThread,
303                                       NULL);
304 
305         if (!NT_SUCCESS(Status)) return;
306 
307         Priority = 7;
308         NtSetInformationThread(ThreadHandle,
309                                ThreadPriority,
310                                &Priority,
311                                sizeof(Priority));
312     }
313 }
314 
315 /* SERIAL FUNCTIONS **********************************************************/
316 
317 VOID
318 NTAPI
319 KdpSerialDebugPrint(LPSTR Message,
320                     ULONG Length)
321 {
322     KIRQL OldIrql;
323     PCHAR pch = (PCHAR) Message;
324 
325     /* Acquire the printing spinlock without waiting at raised IRQL */
326     while (TRUE)
327     {
328         /* Wait when the spinlock becomes available */
329         while (!KeTestSpinLock(&KdpSerialSpinLock));
330 
331         /* Spinlock was free, raise IRQL */
332         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
333 
334         /* Try to get the spinlock */
335         if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock))
336             break;
337 
338         /* Someone else got the spinlock, lower IRQL back */
339         KeLowerIrql(OldIrql);
340     }
341 
342     /* Output the message */
343     while (pch < Message + Length && *pch != '\0')
344     {
345         if (*pch == '\n')
346         {
347             KdPortPutByteEx(&SerialPortInfo, '\r');
348         }
349         KdPortPutByteEx(&SerialPortInfo, *pch);
350         pch++;
351     }
352 
353     /* Release spinlock */
354     KiReleaseSpinLock(&KdpSerialSpinLock);
355 
356     /* Lower IRQL */
357     KeLowerIrql(OldIrql);
358 }
359 
360 VOID
361 NTAPI
362 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
363               ULONG BootPhase)
364 {
365     SIZE_T MemSizeMBs;
366     if (!KdpDebugMode.Serial) return;
367 
368     if (BootPhase == 0)
369     {
370         /* Write out the functions that we support for now */
371         DispatchTable->KdpInitRoutine = KdpSerialInit;
372         DispatchTable->KdpPrintRoutine = KdpSerialDebugPrint;
373 
374         /* Initialize the Port */
375         if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber))
376         {
377             KdpDebugMode.Serial = FALSE;
378             return;
379         }
380         KdComPortInUse = SerialPortInfo.Address;
381 
382         /* Initialize spinlock */
383         KeInitializeSpinLock(&KdpSerialSpinLock);
384 
385         /* Register as a Provider */
386         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
387 
388         /* Display separator + ReactOS version at start of the debug log */
389         MemSizeMBs = KdpGetMemorySizeInMBs(KeLoaderBlock);
390         KdpPrintBanner(MemSizeMBs);
391     }
392     else if (BootPhase == 2)
393     {
394         HalDisplayString("\r\n   Serial debugging enabled\r\n\r\n");
395     }
396 }
397 
398 /* SCREEN FUNCTIONS **********************************************************/
399 
400 /*
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.
405  */
406 VOID
407 NTAPI
408 KdpScreenPrint(LPSTR Message,
409                ULONG Length)
410 {
411     ULONG beg, end, num;
412     KIRQL OldIrql;
413     PCHAR pch = (PCHAR) Message;
414 
415     while (pch < Message + Length && *pch)
416     {
417         if(*pch == '\b')
418         {
419             /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
420             if(KdpScreenLineLength > 0)
421             {
422                 /* Remove last character from buffer */
423                 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0';
424                 KdpScreenLineBufferPos = KdpScreenLineLength;
425 
426                 /* Clear row and print line again */
427                 HalDisplayString("\r");
428                 HalDisplayString(KdpScreenLineBuffer);
429             }
430         }
431         else
432         {
433             KdpScreenLineBuffer[KdpScreenLineLength++] = *pch;
434             KdpScreenLineBuffer[KdpScreenLineLength] = '\0';
435         }
436 
437         if(*pch == '\n' || KdpScreenLineLength == KdpScreenLineLengthDefault)
438         {
439             /* Print buffered characters */
440             if(KdpScreenLineBufferPos != KdpScreenLineLength)
441                 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
442 
443             /* Clear line buffer */
444             KdpScreenLineBuffer[0] = '\0';
445             KdpScreenLineLength = KdpScreenLineBufferPos = 0;
446         }
447 
448         ++pch;
449     }
450 
451     /* Print buffered characters */
452     if(KdpScreenLineBufferPos != KdpScreenLineLength)
453     {
454         HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
455         KdpScreenLineBufferPos = KdpScreenLineLength;
456     }
457 
458     /* Dmesg: store Message in the buffer to show it later */
459     if (KdbpIsInDmesgMode)
460        return;
461 
462     if (KdpDmesgBuffer == NULL)
463       return;
464 
465     /* Acquire the printing spinlock without waiting at raised IRQL */
466     while (TRUE)
467     {
468         /* Wait when the spinlock becomes available */
469         while (!KeTestSpinLock(&KdpDmesgLogSpinLock));
470 
471         /* Spinlock was free, raise IRQL */
472         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
473 
474         /* Try to get the spinlock */
475         if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock))
476             break;
477 
478         /* Someone else got the spinlock, lower IRQL back */
479         KeLowerIrql(OldIrql);
480     }
481 
482     /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
483      * set num to min(KdpDmesgFreeBytes, Length).
484      */
485     num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes;
486     beg = KdpDmesgCurrentPosition;
487     if (num != 0)
488     {
489         end = (beg + num) % KdpDmesgBufferSize;
490         if (end > beg)
491         {
492             RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length);
493         }
494         else
495         {
496             RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg);
497             RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end);
498         }
499         KdpDmesgCurrentPosition = end;
500 
501         /* Counting the total bytes written */
502         KdbDmesgTotalWritten += num;
503     }
504 
505     /* Release spinlock */
506     KiReleaseSpinLock(&KdpDmesgLogSpinLock);
507 
508     /* Lower IRQL */
509     KeLowerIrql(OldIrql);
510 
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.
514      */
515 }
516 
517 VOID
518 NTAPI
519 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
520               ULONG BootPhase)
521 {
522     SIZE_T MemSizeMBs;
523     if (!KdpDebugMode.Screen) return;
524 
525     if (BootPhase == 0)
526     {
527         /* Write out the functions that we support for now */
528         DispatchTable->KdpInitRoutine = KdpScreenInit;
529         DispatchTable->KdpPrintRoutine = KdpScreenPrint;
530 
531         /* Register as a Provider */
532         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
533     }
534     else if (BootPhase == 1)
535     {
536         /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
537          * see kdbp_cli.c:KdbpCmdDmesg()/2
538          */
539         KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1);
540         RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1);
541         KdpDmesgFreeBytes = KdpDmesgBufferSize;
542         KdbDmesgTotalWritten = 0;
543 
544         /* Take control of the display */
545         InbvAcquireDisplayOwnership();
546         InbvResetDisplay();
547         InbvSolidColorFill(0, 0, 639, 479, 0);
548         InbvSetTextColor(15);
549         InbvSetScrollRegion(0, 0, 639, 479);
550         InbvInstallDisplayStringFilter(NULL);
551         InbvEnableDisplayString(TRUE);
552 
553         /* Initialize spinlock */
554         KeInitializeSpinLock(&KdpDmesgLogSpinLock);
555 
556         /* Display separator + ReactOS version at start of the debug log */
557         MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
558         KdpPrintBanner(MemSizeMBs);
559     }
560     else if (BootPhase == 2)
561     {
562         HalDisplayString("\r\n   Screen debugging enabled\r\n\r\n");
563     }
564 }
565 
566 /* GENERAL FUNCTIONS *********************************************************/
567 
568 ULONG
569 NTAPI
570 KdpPrintString(
571     _In_reads_bytes_(Length) PCHAR UnsafeString,
572     _In_ ULONG Length,
573     _In_ KPROCESSOR_MODE PreviousMode)
574 {
575     PLIST_ENTRY CurrentEntry;
576     PKD_DISPATCH_TABLE CurrentTable;
577     PCHAR String;
578     CHAR StringBuffer[512];
579 
580     if (!KdpDebugMode.Value) return 0;
581 
582     Length = min(Length, sizeof(StringBuffer));
583 
584     if (PreviousMode != KernelMode)
585     {
586         _SEH2_TRY
587         {
588             ProbeForRead(UnsafeString, Length, 1);
589             String = StringBuffer;
590             RtlCopyMemory(String, UnsafeString, Length);
591         }
592         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
593         {
594             return 0;
595         }
596         _SEH2_END;
597     }
598     else
599     {
600         String = UnsafeString;
601     }
602 
603     /* Call the registered handlers */
604     CurrentEntry = KdProviders.Flink;
605     while (CurrentEntry != &KdProviders)
606     {
607         /* Get the current table */
608         CurrentTable = CONTAINING_RECORD(CurrentEntry,
609                                          KD_DISPATCH_TABLE,
610                                          KdProvidersList);
611 
612         /* Call it */
613         CurrentTable->KdpPrintRoutine(String, Length);
614 
615         /* Next Table */
616         CurrentEntry = CurrentEntry->Flink;
617     }
618 
619     /* Call the Wrapper Routine */
620     if (WrapperTable.KdpPrintRoutine)
621         WrapperTable.KdpPrintRoutine(String, Length);
622 
623     /* Return the Length */
624     return Length;
625 }
626 
627 /* EOF */
628