xref: /reactos/ntoskrnl/kd/kdio.c (revision 8a978a17)
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 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* GLOBALS *******************************************************************/
19 
20 #define KdpBufferSize  (1024 * 512)
21 static BOOLEAN KdpLoggingEnabled = FALSE;
22 static PCHAR KdpDebugBuffer = NULL;
23 static volatile ULONG KdpCurrentPosition = 0;
24 static volatile ULONG KdpFreeBytes = 0;
25 static KSPIN_LOCK KdpDebugLogSpinLock;
26 static KEVENT KdpLoggerThreadEvent;
27 static HANDLE KdpLogFileHandle;
28 ANSI_STRING KdpLogFileName = RTL_CONSTANT_STRING("\\SystemRoot\\debug.log");
29 
30 static KSPIN_LOCK KdpSerialSpinLock;
31 ULONG  SerialPortNumber = DEFAULT_DEBUG_PORT;
32 CPPORT SerialPortInfo   = {0, DEFAULT_DEBUG_BAUD_RATE, 0};
33 
34 /* Current Port in use. FIXME: Do we support more than one? */
35 ULONG KdpPort;
36 
37 #define KdpScreenLineLengthDefault 80
38 static CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = "";
39 static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
40 
41 const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024;
42 PCHAR KdpDmesgBuffer = NULL;
43 volatile ULONG KdpDmesgCurrentPosition = 0;
44 volatile ULONG KdpDmesgFreeBytes = 0;
45 volatile ULONG KdbDmesgTotalWritten = 0;
46 volatile BOOLEAN KdbpIsInDmesgMode = FALSE;
47 static KSPIN_LOCK KdpDmesgLogSpinLock;
48 
49 /* UTILITY FUNCTIONS *********************************************************/
50 
51 /*
52  * Get the total size of the memory before
53  * Mm is initialized, by counting the number
54  * of physical pages. Useful for debug logging.
55  *
56  * Strongly inspired by:
57  * mm\ARM3\mminit.c : MiScanMemoryDescriptors(...)
58  *
59  * See also: kd64\kdinit.c
60  */
61 static CODE_SEG("INIT")
62 SIZE_T
63 KdpGetMemorySizeInMBs(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
64 {
65     PLIST_ENTRY ListEntry;
66     PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
67     SIZE_T NumberOfPhysicalPages = 0;
68 
69     /* Loop the memory descriptors */
70     for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
71          ListEntry != &LoaderBlock->MemoryDescriptorListHead;
72          ListEntry = ListEntry->Flink)
73     {
74         /* Get the descriptor */
75         Descriptor = CONTAINING_RECORD(ListEntry,
76                                        MEMORY_ALLOCATION_DESCRIPTOR,
77                                        ListEntry);
78 
79         /* Check if this is invisible memory */
80         if ((Descriptor->MemoryType == LoaderFirmwarePermanent) ||
81             (Descriptor->MemoryType == LoaderSpecialMemory) ||
82             (Descriptor->MemoryType == LoaderHALCachedMemory) ||
83             (Descriptor->MemoryType == LoaderBBTMemory))
84         {
85             /* Skip this descriptor */
86             continue;
87         }
88 
89         /* Check if this is bad memory */
90         if (Descriptor->MemoryType != LoaderBad)
91         {
92             /* Count this in the total of pages */
93             NumberOfPhysicalPages += Descriptor->PageCount;
94         }
95     }
96 
97     /* Round size up. Assumed to better match actual physical RAM size */
98     return ALIGN_UP_BY(NumberOfPhysicalPages * PAGE_SIZE, 1024 * 1024) / (1024 * 1024);
99 }
100 
101 /* See also: kd64\kdinit.c */
102 static CODE_SEG("INIT")
103 VOID
104 KdpPrintBanner(IN SIZE_T MemSizeMBs)
105 {
106     DPRINT1("-----------------------------------------------------\n");
107     DPRINT1("ReactOS " KERNEL_VERSION_STR " (Build " KERNEL_VERSION_BUILD_STR ") (Commit " KERNEL_VERSION_COMMIT_HASH ")\n");
108     DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
109     DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
110     DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName, KeLoaderBlock->NtHalPathName, KeLoaderBlock->ArcHalDeviceName, KeLoaderBlock->NtBootPathName);
111 }
112 
113 /* LOCKING FUNCTIONS *********************************************************/
114 
115 KIRQL
116 NTAPI
117 KdpAcquireLock(IN PKSPIN_LOCK SpinLock)
118 {
119     KIRQL OldIrql;
120 
121     /* Acquire the spinlock without waiting at raised IRQL */
122     while (TRUE)
123     {
124         /* Loop until the spinlock becomes available */
125         while (!KeTestSpinLock(SpinLock));
126 
127         /* Spinlock is free, raise IRQL to high level */
128         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
129 
130         /* Try to get the spinlock */
131         if (KeTryToAcquireSpinLockAtDpcLevel(SpinLock))
132             break;
133 
134         /* Someone else got the spinlock, lower IRQL back */
135         KeLowerIrql(OldIrql);
136     }
137 
138     return OldIrql;
139 }
140 
141 VOID
142 NTAPI
143 KdpReleaseLock(IN PKSPIN_LOCK SpinLock,
144                IN KIRQL OldIrql)
145 {
146     /* Release the spinlock */
147     KiReleaseSpinLock(SpinLock);
148     // KeReleaseSpinLockFromDpcLevel(SpinLock);
149 
150     /* Restore the old IRQL */
151     KeLowerIrql(OldIrql);
152 }
153 
154 /* FILE DEBUG LOG FUNCTIONS **************************************************/
155 
156 static VOID
157 NTAPI
158 KdpLoggerThread(PVOID Context)
159 {
160     ULONG beg, end, num;
161     IO_STATUS_BLOCK Iosb;
162 
163     KdpLoggingEnabled = TRUE;
164 
165     while (TRUE)
166     {
167         KeWaitForSingleObject(&KdpLoggerThreadEvent, Executive, KernelMode, FALSE, NULL);
168 
169         /* Bug */
170         /* Keep KdpCurrentPosition and KdpFreeBytes values in local
171          * variables to avoid their possible change from Producer part,
172          * KdpPrintToLogFile function
173          */
174         end = KdpCurrentPosition;
175         num = KdpFreeBytes;
176 
177         /* Now securely calculate values, based on local variables */
178         beg = (end + num) % KdpBufferSize;
179         num = KdpBufferSize - num;
180 
181         /* Nothing to do? */
182         if (num == 0)
183             continue;
184 
185         if (end > beg)
186         {
187             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
188                         KdpDebugBuffer + beg, num, NULL, NULL);
189         }
190         else
191         {
192             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
193                         KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL);
194 
195             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
196                         KdpDebugBuffer, end, NULL, NULL);
197         }
198 
199         (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num);
200     }
201 }
202 
203 static VOID
204 NTAPI
205 KdpPrintToLogFile(PCHAR String,
206                   ULONG StringLength)
207 {
208     KIRQL OldIrql;
209     ULONG beg, end, num;
210 
211     if (KdpDebugBuffer == NULL) return;
212 
213     /* Acquire the printing spinlock without waiting at raised IRQL */
214     OldIrql = KdpAcquireLock(&KdpDebugLogSpinLock);
215 
216     beg = KdpCurrentPosition;
217     num = KdpFreeBytes;
218     if (StringLength < num)
219         num = StringLength;
220 
221     if (num != 0)
222     {
223         end = (beg + num) % KdpBufferSize;
224         KdpCurrentPosition = end;
225         KdpFreeBytes -= num;
226 
227         if (end > beg)
228         {
229             RtlCopyMemory(KdpDebugBuffer + beg, String, num);
230         }
231         else
232         {
233             RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg);
234             RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end);
235         }
236     }
237 
238     /* Release the spinlock */
239     KdpReleaseLock(&KdpDebugLogSpinLock, OldIrql);
240 
241     /* Signal the logger thread */
242     if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
243         KeSetEvent(&KdpLoggerThreadEvent, IO_NO_INCREMENT, FALSE);
244 }
245 
246 VOID
247 NTAPI
248 KdpDebugLogInit(PKD_DISPATCH_TABLE DispatchTable,
249                 ULONG BootPhase)
250 {
251     NTSTATUS Status;
252     UNICODE_STRING FileName;
253     OBJECT_ATTRIBUTES ObjectAttributes;
254     IO_STATUS_BLOCK Iosb;
255     HANDLE ThreadHandle;
256     KPRIORITY Priority;
257     SIZE_T MemSizeMBs;
258 
259     if (!KdpDebugMode.File) return;
260 
261     if (BootPhase == 0)
262     {
263         KdComPortInUse = NULL;
264 
265         /* Write out the functions that we support for now */
266         DispatchTable->KdpInitRoutine = KdpDebugLogInit;
267         DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
268 
269         /* Register as a Provider */
270         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
271     }
272     else if (BootPhase == 1)
273     {
274         /* Allocate a buffer for debug log */
275         KdpDebugBuffer = ExAllocatePool(NonPagedPool, KdpBufferSize);
276         KdpFreeBytes = KdpBufferSize;
277 
278         /* Initialize spinlock */
279         KeInitializeSpinLock(&KdpDebugLogSpinLock);
280 
281         /* Display separator + ReactOS version at start of the debug log */
282         /* Round size up. Assumed to better match actual physical RAM size */
283         MemSizeMBs = ALIGN_UP_BY(MmNumberOfPhysicalPages * PAGE_SIZE, 1024 * 1024) / (1024 * 1024);
284         KdpPrintBanner(MemSizeMBs);
285     }
286     else if (BootPhase == 2)
287     {
288         HalDisplayString("\r\n   File log debugging enabled\r\n\r\n");
289     }
290     else if (BootPhase == 3)
291     {
292         /* Setup the log name */
293         Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
294         if (!NT_SUCCESS(Status)) return;
295 
296         InitializeObjectAttributes(&ObjectAttributes,
297                                    &FileName,
298                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
299                                    NULL,
300                                    NULL);
301 
302         /* Create the log file */
303         Status = NtCreateFile(&KdpLogFileHandle,
304                               FILE_APPEND_DATA | SYNCHRONIZE,
305                               &ObjectAttributes,
306                               &Iosb,
307                               NULL,
308                               FILE_ATTRIBUTE_NORMAL,
309                               FILE_SHARE_READ,
310                               FILE_SUPERSEDE,
311                               FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
312                               NULL,
313                               0);
314 
315         RtlFreeUnicodeString(&FileName);
316 
317         if (!NT_SUCCESS(Status))
318             return;
319 
320         KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE);
321 
322         /* Create the logger thread */
323         Status = PsCreateSystemThread(&ThreadHandle,
324                                       THREAD_ALL_ACCESS,
325                                       NULL,
326                                       NULL,
327                                       NULL,
328                                       KdpLoggerThread,
329                                       NULL);
330         if (!NT_SUCCESS(Status))
331         {
332             NtClose(KdpLogFileHandle);
333             return;
334         }
335 
336         Priority = 7;
337         NtSetInformationThread(ThreadHandle,
338                                ThreadPriority,
339                                &Priority,
340                                sizeof(Priority));
341     }
342 }
343 
344 /* SERIAL FUNCTIONS **********************************************************/
345 
346 VOID
347 NTAPI
348 KdpSerialDebugPrint(PCHAR Message,
349                     ULONG Length)
350 {
351     PCHAR pch = (PCHAR)Message;
352     KIRQL OldIrql;
353 
354     /* Acquire the printing spinlock without waiting at raised IRQL */
355     OldIrql = KdpAcquireLock(&KdpSerialSpinLock);
356 
357     /* Output the message */
358     while (pch < Message + Length && *pch != '\0')
359     {
360         if (*pch == '\n')
361         {
362             KdPortPutByteEx(&SerialPortInfo, '\r');
363         }
364         KdPortPutByteEx(&SerialPortInfo, *pch);
365         pch++;
366     }
367 
368     /* Release the spinlock */
369     KdpReleaseLock(&KdpSerialSpinLock, OldIrql);
370 }
371 
372 VOID
373 NTAPI
374 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
375               ULONG BootPhase)
376 {
377     SIZE_T MemSizeMBs;
378     if (!KdpDebugMode.Serial) return;
379 
380     if (BootPhase == 0)
381     {
382         /* Write out the functions that we support for now */
383         DispatchTable->KdpInitRoutine = KdpSerialInit;
384         DispatchTable->KdpPrintRoutine = KdpSerialDebugPrint;
385 
386         /* Initialize the Port */
387         if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber))
388         {
389             KdpDebugMode.Serial = FALSE;
390             return;
391         }
392         KdComPortInUse = SerialPortInfo.Address;
393 
394         /* Initialize spinlock */
395         KeInitializeSpinLock(&KdpSerialSpinLock);
396 
397         /* Register as a Provider */
398         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
399 
400         /* Display separator + ReactOS version at start of the debug log */
401         MemSizeMBs = KdpGetMemorySizeInMBs(KeLoaderBlock);
402         KdpPrintBanner(MemSizeMBs);
403     }
404     else if (BootPhase == 2)
405     {
406         HalDisplayString("\r\n   Serial debugging enabled\r\n\r\n");
407     }
408 }
409 
410 /* SCREEN FUNCTIONS **********************************************************/
411 
412 VOID
413 KdpScreenAcquire(VOID)
414 {
415     if (InbvIsBootDriverInstalled() /* &&
416         !InbvCheckDisplayOwnership() */)
417     {
418         /* Acquire ownership and reset the display */
419         InbvAcquireDisplayOwnership();
420         InbvResetDisplay();
421         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
422         InbvSetTextColor(BV_COLOR_WHITE);
423         InbvInstallDisplayStringFilter(NULL);
424         InbvEnableDisplayString(TRUE);
425         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
426     }
427 }
428 
429 // extern VOID NTAPI InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned);
430 
431 VOID
432 KdpScreenRelease(VOID)
433 {
434     if (InbvIsBootDriverInstalled()&&
435         InbvCheckDisplayOwnership())
436     {
437         /* Release the display */
438         // InbvSetDisplayOwnership(FALSE);
439         InbvNotifyDisplayOwnershipLost(NULL);
440     }
441 }
442 
443 /*
444  * Screen debug logger function KdpScreenPrint() writes text messages into
445  * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
446  * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
447  * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
448  */
449 static VOID
450 NTAPI
451 KdpScreenPrint(PCHAR Message,
452                ULONG Length)
453 {
454     PCHAR pch = (PCHAR)Message;
455     KIRQL OldIrql;
456     ULONG beg, end, num;
457 
458     while (pch < Message + Length && *pch)
459     {
460         if (*pch == '\b')
461         {
462             /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
463             if (KdpScreenLineLength > 0)
464             {
465                 /* Remove last character from buffer */
466                 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0';
467                 KdpScreenLineBufferPos = KdpScreenLineLength;
468 
469                 /* Clear row and print line again */
470                 HalDisplayString("\r");
471                 HalDisplayString(KdpScreenLineBuffer);
472             }
473         }
474         else
475         {
476             KdpScreenLineBuffer[KdpScreenLineLength++] = *pch;
477             KdpScreenLineBuffer[KdpScreenLineLength] = '\0';
478         }
479 
480         if (*pch == '\n' || KdpScreenLineLength == KdpScreenLineLengthDefault)
481         {
482             /* Print buffered characters */
483             if (KdpScreenLineBufferPos != KdpScreenLineLength)
484                 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
485 
486             /* Clear line buffer */
487             KdpScreenLineBuffer[0] = '\0';
488             KdpScreenLineLength = KdpScreenLineBufferPos = 0;
489         }
490 
491         ++pch;
492     }
493 
494     /* Print buffered characters */
495     if (KdpScreenLineBufferPos != KdpScreenLineLength)
496     {
497         HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
498         KdpScreenLineBufferPos = KdpScreenLineLength;
499     }
500 
501     /* Dmesg: store Message in the buffer to show it later */
502     if (KdbpIsInDmesgMode)
503        return;
504 
505     if (KdpDmesgBuffer == NULL)
506       return;
507 
508     /* Acquire the printing spinlock without waiting at raised IRQL */
509     OldIrql = KdpAcquireLock(&KdpDmesgLogSpinLock);
510 
511     /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
512      * set num to min(KdpDmesgFreeBytes, Length).
513      */
514     num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes;
515     beg = KdpDmesgCurrentPosition;
516     if (num != 0)
517     {
518         end = (beg + num) % KdpDmesgBufferSize;
519         if (end > beg)
520         {
521             RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length);
522         }
523         else
524         {
525             RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg);
526             RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end);
527         }
528         KdpDmesgCurrentPosition = end;
529 
530         /* Counting the total bytes written */
531         KdbDmesgTotalWritten += num;
532     }
533 
534     /* Release the spinlock */
535     KdpReleaseLock(&KdpDmesgLogSpinLock, OldIrql);
536 
537     /* Optional step(?): find out a way to notify about buffer exhaustion,
538      * and possibly fall into kbd to use dmesg command: user will read
539      * debug messages before they will be wiped over by next writes.
540      */
541 }
542 
543 VOID
544 NTAPI
545 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
546               ULONG BootPhase)
547 {
548     SIZE_T MemSizeMBs;
549     if (!KdpDebugMode.Screen) return;
550 
551     if (BootPhase == 0)
552     {
553         /* Write out the functions that we support for now */
554         DispatchTable->KdpInitRoutine = KdpScreenInit;
555         DispatchTable->KdpPrintRoutine = KdpScreenPrint;
556 
557         /* Register as a Provider */
558         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
559     }
560     else if (BootPhase == 1)
561     {
562         /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
563          * see kdbp_cli.c:KdbpCmdDmesg()/2
564          */
565         KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1);
566         RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1);
567         KdpDmesgFreeBytes = KdpDmesgBufferSize;
568         KdbDmesgTotalWritten = 0;
569 
570         /* Take control of the display */
571         KdpScreenAcquire();
572 
573         /* Initialize spinlock */
574         KeInitializeSpinLock(&KdpDmesgLogSpinLock);
575 
576         /* Display separator + ReactOS version at start of the debug log */
577         /* Round size up. Assumed to better match actual physical RAM size */
578         MemSizeMBs = ALIGN_UP_BY(MmNumberOfPhysicalPages * PAGE_SIZE, 1024 * 1024) / (1024 * 1024);
579         KdpPrintBanner(MemSizeMBs);
580     }
581     else if (BootPhase == 2)
582     {
583         HalDisplayString("\r\n   Screen debugging enabled\r\n\r\n");
584     }
585 }
586 
587 /* GENERAL FUNCTIONS *********************************************************/
588 
589 BOOLEAN
590 NTAPI
591 KdpPrintString(
592     _In_ PSTRING Output)
593 {
594     PLIST_ENTRY CurrentEntry;
595     PKD_DISPATCH_TABLE CurrentTable;
596 
597     if (!KdpDebugMode.Value) return FALSE;
598 
599     /* Call the registered handlers */
600     CurrentEntry = KdProviders.Flink;
601     while (CurrentEntry != &KdProviders)
602     {
603         /* Get the current table */
604         CurrentTable = CONTAINING_RECORD(CurrentEntry,
605                                          KD_DISPATCH_TABLE,
606                                          KdProvidersList);
607 
608         /* Call it */
609         CurrentTable->KdpPrintRoutine(Output->Buffer, Output->Length);
610 
611         /* Next Table */
612         CurrentEntry = CurrentEntry->Flink;
613     }
614 
615     /* Call the Wrapper Routine */
616     if (WrapperTable.KdpPrintRoutine)
617         WrapperTable.KdpPrintRoutine(Output->Buffer, Output->Length);
618 
619     return FALSE;
620 }
621 
622 extern STRING KdbPromptString;
623 
624 BOOLEAN
625 NTAPI
626 KdpPromptString(
627     _In_ PSTRING PromptString,
628     _In_ PSTRING ResponseString)
629 {
630 #ifdef KDBG
631     KIRQL OldIrql;
632     STRING StringChar;
633     CHAR Response;
634     USHORT i;
635     ULONG DummyScanCode;
636 
637     StringChar.Buffer = &Response;
638     StringChar.Length = StringChar.MaximumLength = sizeof(Response);
639 
640     /* Display the string and print a new line for log neatness */
641     KdpPrintString(PromptString);
642     *StringChar.Buffer = '\n';
643     KdpPrintString(&StringChar);
644 
645     /* Print the kdb prompt */
646     KdpPrintString(&KdbPromptString);
647 
648     // TODO: Use an improved KdbpReadCommand() function for our purposes.
649 
650     /* Acquire the printing spinlock without waiting at raised IRQL */
651     OldIrql = KdpAcquireLock(&KdpSerialSpinLock);
652 
653     if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
654         KbdDisableMouse();
655 
656     /* Loop the whole string */
657     for (i = 0; i < ResponseString->MaximumLength; i++)
658     {
659         /* Check if this is serial debugging mode */
660         if (KdbDebugState & KD_DEBUG_KDSERIAL)
661         {
662             /* Get the character from serial */
663             do
664             {
665                 Response = KdbpTryGetCharSerial(MAXULONG);
666             } while (Response == -1);
667         }
668         else
669         {
670             /* Get the response from the keyboard */
671             do
672             {
673                 Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG);
674             } while (Response == -1);
675         }
676 
677         /* Check for return */
678         if (Response == '\r')
679         {
680             /*
681              * We might need to discard the next '\n'.
682              * Wait a bit to make sure we receive it.
683              */
684             KeStallExecutionProcessor(100000);
685 
686             /* Check the mode */
687             if (KdbDebugState & KD_DEBUG_KDSERIAL)
688             {
689                 /* Read and discard the next character, if any */
690                 KdbpTryGetCharSerial(5);
691             }
692             else
693             {
694                 /* Read and discard the next character, if any */
695                 KdbpTryGetCharKeyboard(&DummyScanCode, 5);
696             }
697 
698             /*
699              * Null terminate the output string -- documentation states that
700              * DbgPrompt does not null terminate, but it does
701              */
702             *(PCHAR)(ResponseString->Buffer + i) = 0;
703             break;
704         }
705 
706         /* Write it back and print it to the log */
707         *(PCHAR)(ResponseString->Buffer + i) = Response;
708         KdpReleaseLock(&KdpSerialSpinLock, OldIrql);
709         KdpPrintString(&StringChar);
710         OldIrql = KdpAcquireLock(&KdpSerialSpinLock);
711     }
712 
713     /* Return the length */
714     ResponseString->Length = i;
715 
716     if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
717         KbdEnableMouse();
718 
719     /* Release the spinlock */
720     KdpReleaseLock(&KdpSerialSpinLock, OldIrql);
721 
722     /* Print a new line */
723     *StringChar.Buffer = '\n';
724     KdpPrintString(&StringChar);
725 #endif
726 
727     /* Success; we don't need to resend */
728     return FALSE;
729 }
730 
731 /* EOF */
732