xref: /reactos/ntoskrnl/kd/kdio.c (revision b2294b4c)
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 #include "kd.h"
15 #include "kdterminal.h"
16 #ifdef KDBG
17 #include "../kdbg/kdb.h"
18 #endif
19 
20 #define NDEBUG
21 #include <debug.h>
22 
23 #undef KdSendPacket
24 #undef KdReceivePacket
25 
26 /* GLOBALS *******************************************************************/
27 
28 #define KdpBufferSize  (1024 * 512)
29 static BOOLEAN KdpLoggingEnabled = FALSE;
30 static PCHAR KdpDebugBuffer = NULL;
31 static volatile ULONG KdpCurrentPosition = 0;
32 static volatile ULONG KdpFreeBytes = 0;
33 static KSPIN_LOCK KdpDebugLogSpinLock;
34 static KEVENT KdpLoggerThreadEvent;
35 static HANDLE KdpLogFileHandle;
36 ANSI_STRING KdpLogFileName = RTL_CONSTANT_STRING("\\SystemRoot\\debug.log");
37 
38 static KSPIN_LOCK KdpSerialSpinLock;
39 ULONG  SerialPortNumber = DEFAULT_DEBUG_PORT;
40 CPPORT SerialPortInfo   = {0, DEFAULT_DEBUG_BAUD_RATE, 0};
41 
42 #define KdpScreenLineLengthDefault 80
43 static CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = "";
44 static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
45 
46 KDP_DEBUG_MODE KdpDebugMode;
47 LIST_ENTRY KdProviders = {&KdProviders, &KdProviders};
48 KD_DISPATCH_TABLE DispatchTable[KdMax] = {0};
49 
50 PKDP_INIT_ROUTINE InitRoutines[KdMax] =
51 {
52     KdpScreenInit,
53     KdpSerialInit,
54     KdpDebugLogInit,
55 #ifdef KDBG // See kdb_cli.c
56     KdpKdbgInit
57 #endif
58 };
59 
60 /* LOCKING FUNCTIONS *********************************************************/
61 
62 KIRQL
63 NTAPI
KdbpAcquireLock(_In_ PKSPIN_LOCK SpinLock)64 KdbpAcquireLock(
65     _In_ PKSPIN_LOCK SpinLock)
66 {
67     KIRQL OldIrql;
68 
69     /* Acquire the spinlock without waiting at raised IRQL */
70     while (TRUE)
71     {
72         /* Loop until the spinlock becomes available */
73         while (!KeTestSpinLock(SpinLock));
74 
75         /* Spinlock is free, raise IRQL to high level */
76         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
77 
78         /* Try to get the spinlock */
79         if (KeTryToAcquireSpinLockAtDpcLevel(SpinLock))
80             break;
81 
82         /* Someone else got the spinlock, lower IRQL back */
83         KeLowerIrql(OldIrql);
84     }
85 
86     return OldIrql;
87 }
88 
89 VOID
90 NTAPI
KdbpReleaseLock(_In_ PKSPIN_LOCK SpinLock,_In_ KIRQL OldIrql)91 KdbpReleaseLock(
92     _In_ PKSPIN_LOCK SpinLock,
93     _In_ KIRQL OldIrql)
94 {
95     /* Release the spinlock */
96     KiReleaseSpinLock(SpinLock);
97     // KeReleaseSpinLockFromDpcLevel(SpinLock);
98 
99     /* Restore the old IRQL */
100     KeLowerIrql(OldIrql);
101 }
102 
103 /* FILE DEBUG LOG FUNCTIONS **************************************************/
104 
105 static VOID
106 NTAPI
KdpLoggerThread(PVOID Context)107 KdpLoggerThread(PVOID Context)
108 {
109     ULONG beg, end, num;
110     IO_STATUS_BLOCK Iosb;
111 
112     ASSERT(ExGetPreviousMode() == KernelMode);
113 
114     KdpLoggingEnabled = TRUE;
115 
116     while (TRUE)
117     {
118         KeWaitForSingleObject(&KdpLoggerThreadEvent, Executive, KernelMode, FALSE, NULL);
119 
120         /* Bug */
121         /* Keep KdpCurrentPosition and KdpFreeBytes values in local
122          * variables to avoid their possible change from Producer part,
123          * KdpPrintToLogFile function
124          */
125         end = KdpCurrentPosition;
126         num = KdpFreeBytes;
127 
128         /* Now securely calculate values, based on local variables */
129         beg = (end + num) % KdpBufferSize;
130         num = KdpBufferSize - num;
131 
132         /* Nothing to do? */
133         if (num == 0)
134             continue;
135 
136         if (end > beg)
137         {
138             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
139                         KdpDebugBuffer + beg, num, NULL, NULL);
140         }
141         else
142         {
143             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
144                         KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL);
145 
146             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
147                         KdpDebugBuffer, end, NULL, NULL);
148         }
149 
150         (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num);
151     }
152 }
153 
154 static VOID
155 NTAPI
KdpPrintToLogFile(_In_ PCCH String,_In_ ULONG Length)156 KdpPrintToLogFile(
157     _In_ PCCH String,
158     _In_ ULONG Length)
159 {
160     KIRQL OldIrql;
161     ULONG beg, end, num;
162 
163     if (KdpDebugBuffer == NULL) return;
164 
165     /* Acquire the printing spinlock without waiting at raised IRQL */
166     OldIrql = KdbpAcquireLock(&KdpDebugLogSpinLock);
167 
168     beg = KdpCurrentPosition;
169     num = min(Length, KdpFreeBytes);
170     if (num != 0)
171     {
172         end = (beg + num) % KdpBufferSize;
173         KdpCurrentPosition = end;
174         KdpFreeBytes -= num;
175 
176         if (end > beg)
177         {
178             RtlCopyMemory(KdpDebugBuffer + beg, String, num);
179         }
180         else
181         {
182             RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg);
183             RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end);
184         }
185     }
186 
187     /* Release the spinlock */
188     KdbpReleaseLock(&KdpDebugLogSpinLock, OldIrql);
189 
190     /* Signal the logger thread */
191     if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
192         KeSetEvent(&KdpLoggerThreadEvent, IO_NO_INCREMENT, FALSE);
193 }
194 
195 NTSTATUS
196 NTAPI
KdpDebugLogInit(_In_ PKD_DISPATCH_TABLE DispatchTable,_In_ ULONG BootPhase)197 KdpDebugLogInit(
198     _In_ PKD_DISPATCH_TABLE DispatchTable,
199     _In_ ULONG BootPhase)
200 {
201     NTSTATUS Status = STATUS_SUCCESS;
202 
203     if (!KdpDebugMode.File)
204         return STATUS_PORT_DISCONNECTED;
205 
206     if (BootPhase == 0)
207     {
208         /* Write out the functions that we support for now */
209         DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
210 
211         /* Register for BootPhase 1 initialization and as a Provider */
212         DispatchTable->KdpInitRoutine = KdpDebugLogInit;
213         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
214     }
215     else if (BootPhase == 1)
216     {
217         /* Allocate a buffer for debug log */
218         KdpDebugBuffer = ExAllocatePoolZero(NonPagedPool,
219                                             KdpBufferSize,
220                                             TAG_KDBG);
221         if (!KdpDebugBuffer)
222         {
223             KdpDebugMode.File = FALSE;
224             RemoveEntryList(&DispatchTable->KdProvidersList);
225             return STATUS_NO_MEMORY;
226         }
227         KdpFreeBytes = KdpBufferSize;
228 
229         /* Initialize spinlock */
230         KeInitializeSpinLock(&KdpDebugLogSpinLock);
231 
232         /* Register for later BootPhase 2 reinitialization */
233         DispatchTable->KdpInitRoutine = KdpDebugLogInit;
234 
235         /* Announce ourselves */
236         HalDisplayString("   File log debugging enabled\r\n");
237     }
238     else if (BootPhase >= 2)
239     {
240         UNICODE_STRING FileName;
241         OBJECT_ATTRIBUTES ObjectAttributes;
242         IO_STATUS_BLOCK Iosb;
243         HANDLE ThreadHandle;
244         KPRIORITY Priority;
245 
246         /* If we have already successfully opened the log file, bail out */
247         if (KdpLogFileHandle != NULL)
248             return STATUS_SUCCESS;
249 
250         /* Setup the log name */
251         Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
252         if (!NT_SUCCESS(Status))
253             goto Failure;
254 
255         InitializeObjectAttributes(&ObjectAttributes,
256                                    &FileName,
257                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
258                                    NULL,
259                                    NULL);
260 
261         /* Create the log file */
262         Status = ZwCreateFile(&KdpLogFileHandle,
263                               FILE_APPEND_DATA | SYNCHRONIZE,
264                               &ObjectAttributes,
265                               &Iosb,
266                               NULL,
267                               FILE_ATTRIBUTE_NORMAL,
268                               FILE_SHARE_READ,
269                               FILE_OPEN_IF,
270                               FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
271                                 FILE_SEQUENTIAL_ONLY | FILE_WRITE_THROUGH,
272                               NULL,
273                               0);
274 
275         RtlFreeUnicodeString(&FileName);
276 
277         if (!NT_SUCCESS(Status))
278         {
279             DPRINT1("Failed to open log file: 0x%08lx\n", Status);
280 
281             /* Schedule an I/O reinitialization if needed */
282             if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
283                 Status == STATUS_OBJECT_PATH_NOT_FOUND)
284             {
285                 DispatchTable->KdpInitRoutine = KdpDebugLogInit;
286                 return Status;
287             }
288             goto Failure;
289         }
290 
291         /**    HACK for FILE_APPEND_DATA     **
292          ** Remove once CORE-18789 is fixed. **
293          ** Enforce to go to the end of file **/
294         {
295             FILE_STANDARD_INFORMATION FileInfo;
296             FILE_POSITION_INFORMATION FilePosInfo;
297 
298             Status = ZwQueryInformationFile(KdpLogFileHandle,
299                                             &Iosb,
300                                             &FileInfo,
301                                             sizeof(FileInfo),
302                                             FileStandardInformation);
303             DPRINT("Status: 0x%08lx - EOF offset: %I64d\n",
304                     Status, FileInfo.EndOfFile.QuadPart);
305 
306             Status = ZwQueryInformationFile(KdpLogFileHandle,
307                                             &Iosb,
308                                             &FilePosInfo,
309                                             sizeof(FilePosInfo),
310                                             FilePositionInformation);
311             DPRINT("Status: 0x%08lx - Position: %I64d\n",
312                     Status, FilePosInfo.CurrentByteOffset.QuadPart);
313 
314             FilePosInfo.CurrentByteOffset.QuadPart = FileInfo.EndOfFile.QuadPart;
315             Status = ZwSetInformationFile(KdpLogFileHandle,
316                                           &Iosb,
317                                           &FilePosInfo,
318                                           sizeof(FilePosInfo),
319                                           FilePositionInformation);
320             DPRINT("ZwSetInformationFile(FilePositionInfo) returned: 0x%08lx\n", Status);
321         }
322         /** END OF HACK **/
323 
324         KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE);
325 
326         /* Create the logger thread */
327         Status = PsCreateSystemThread(&ThreadHandle,
328                                       THREAD_ALL_ACCESS,
329                                       NULL,
330                                       NULL,
331                                       NULL,
332                                       KdpLoggerThread,
333                                       NULL);
334         if (!NT_SUCCESS(Status))
335         {
336             DPRINT1("Failed to create log file thread: 0x%08lx\n", Status);
337             ZwClose(KdpLogFileHandle);
338             goto Failure;
339         }
340 
341         Priority = HIGH_PRIORITY;
342         ZwSetInformationThread(ThreadHandle,
343                                ThreadPriority,
344                                &Priority,
345                                sizeof(Priority));
346 
347         ZwClose(ThreadHandle);
348         return Status;
349 
350 Failure:
351         KdpFreeBytes = 0;
352         ExFreePoolWithTag(KdpDebugBuffer, TAG_KDBG);
353         KdpDebugBuffer = NULL;
354         KdpDebugMode.File = FALSE;
355         RemoveEntryList(&DispatchTable->KdProvidersList);
356     }
357 
358     return Status;
359 }
360 
361 /* SERIAL FUNCTIONS **********************************************************/
362 
363 static VOID
364 NTAPI
KdpSerialPrint(_In_ PCCH String,_In_ ULONG Length)365 KdpSerialPrint(
366     _In_ PCCH String,
367     _In_ ULONG Length)
368 {
369     PCCH pch = String;
370     KIRQL OldIrql;
371 
372     /* Acquire the printing spinlock without waiting at raised IRQL */
373     OldIrql = KdbpAcquireLock(&KdpSerialSpinLock);
374 
375     /* Output the string */
376     while (pch < String + Length && *pch)
377     {
378         if (*pch == '\n')
379         {
380             KdPortPutByteEx(&SerialPortInfo, '\r');
381         }
382         KdPortPutByteEx(&SerialPortInfo, *pch);
383         ++pch;
384     }
385 
386     /* Release the spinlock */
387     KdbpReleaseLock(&KdpSerialSpinLock, OldIrql);
388 }
389 
390 NTSTATUS
391 NTAPI
KdpSerialInit(_In_ PKD_DISPATCH_TABLE DispatchTable,_In_ ULONG BootPhase)392 KdpSerialInit(
393     _In_ PKD_DISPATCH_TABLE DispatchTable,
394     _In_ ULONG BootPhase)
395 {
396     if (!KdpDebugMode.Serial)
397         return STATUS_PORT_DISCONNECTED;
398 
399     if (BootPhase == 0)
400     {
401         /* Write out the functions that we support for now */
402         DispatchTable->KdpPrintRoutine = KdpSerialPrint;
403 
404         /* Initialize the Port */
405         if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber))
406         {
407             KdpDebugMode.Serial = FALSE;
408             return STATUS_DEVICE_DOES_NOT_EXIST;
409         }
410         KdComPortInUse = SerialPortInfo.Address;
411 
412         /* Initialize spinlock */
413         KeInitializeSpinLock(&KdpSerialSpinLock);
414 
415         /* Register for BootPhase 1 initialization and as a Provider */
416         DispatchTable->KdpInitRoutine = KdpSerialInit;
417         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
418     }
419     else if (BootPhase == 1)
420     {
421         /* Announce ourselves */
422         HalDisplayString("   Serial debugging enabled\r\n");
423     }
424 
425     return STATUS_SUCCESS;
426 }
427 
428 /* SCREEN FUNCTIONS **********************************************************/
429 
430 VOID
KdpScreenAcquire(VOID)431 KdpScreenAcquire(VOID)
432 {
433     if (InbvIsBootDriverInstalled() /* &&
434         !InbvCheckDisplayOwnership() */)
435     {
436         /* Acquire ownership and reset the display */
437         InbvAcquireDisplayOwnership();
438         InbvResetDisplay();
439         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
440         InbvSetTextColor(BV_COLOR_WHITE);
441         InbvInstallDisplayStringFilter(NULL);
442         InbvEnableDisplayString(TRUE);
443         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
444     }
445 }
446 
447 // extern VOID NTAPI InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned);
448 
449 VOID
KdpScreenRelease(VOID)450 KdpScreenRelease(VOID)
451 {
452     if (InbvIsBootDriverInstalled()&&
453         InbvCheckDisplayOwnership())
454     {
455         /* Release the display */
456         // InbvSetDisplayOwnership(FALSE);
457         InbvNotifyDisplayOwnershipLost(NULL);
458     }
459 }
460 
461 static VOID
462 NTAPI
KdpScreenPrint(_In_ PCCH String,_In_ ULONG Length)463 KdpScreenPrint(
464     _In_ PCCH String,
465     _In_ ULONG Length)
466 {
467     PCCH pch = String;
468 
469     while (pch < String + Length && *pch)
470     {
471         if (*pch == '\b')
472         {
473             /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
474             if (KdpScreenLineLength > 0)
475             {
476                 /* Remove last character from buffer */
477                 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0';
478                 KdpScreenLineBufferPos = KdpScreenLineLength;
479 
480                 /* Clear row and print line again */
481                 HalDisplayString("\r");
482                 HalDisplayString(KdpScreenLineBuffer);
483             }
484         }
485         else
486         {
487             KdpScreenLineBuffer[KdpScreenLineLength++] = *pch;
488             KdpScreenLineBuffer[KdpScreenLineLength] = '\0';
489         }
490 
491         if (*pch == '\n' || KdpScreenLineLength == KdpScreenLineLengthDefault)
492         {
493             /* Print buffered characters */
494             if (KdpScreenLineBufferPos != KdpScreenLineLength)
495                 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
496 
497             /* Clear line buffer */
498             KdpScreenLineBuffer[0] = '\0';
499             KdpScreenLineLength = KdpScreenLineBufferPos = 0;
500         }
501 
502         ++pch;
503     }
504 
505     /* Print buffered characters */
506     if (KdpScreenLineBufferPos != KdpScreenLineLength)
507     {
508         HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
509         KdpScreenLineBufferPos = KdpScreenLineLength;
510     }
511 }
512 
513 NTSTATUS
514 NTAPI
KdpScreenInit(_In_ PKD_DISPATCH_TABLE DispatchTable,_In_ ULONG BootPhase)515 KdpScreenInit(
516     _In_ PKD_DISPATCH_TABLE DispatchTable,
517     _In_ ULONG BootPhase)
518 {
519     if (!KdpDebugMode.Screen)
520         return STATUS_PORT_DISCONNECTED;
521 
522     if (BootPhase == 0)
523     {
524         /* Write out the functions that we support for now */
525         DispatchTable->KdpPrintRoutine = KdpScreenPrint;
526 
527         /* Register for BootPhase 1 initialization and as a Provider */
528         DispatchTable->KdpInitRoutine = KdpScreenInit;
529         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
530     }
531     else if (BootPhase == 1)
532     {
533         /* Take control of the display */
534         KdpScreenAcquire();
535 
536         /* Announce ourselves */
537         HalDisplayString("   Screen debugging enabled\r\n");
538     }
539 
540     return STATUS_SUCCESS;
541 }
542 
543 
544 /* GENERAL FUNCTIONS *********************************************************/
545 
546 static VOID
KdIoPrintString(_In_ PCCH String,_In_ ULONG Length)547 KdIoPrintString(
548     _In_ PCCH String,
549     _In_ ULONG Length)
550 {
551     PLIST_ENTRY CurrentEntry;
552     PKD_DISPATCH_TABLE CurrentTable;
553 
554     /* Call the registered providers */
555     for (CurrentEntry = KdProviders.Flink;
556          CurrentEntry != &KdProviders;
557          CurrentEntry = CurrentEntry->Flink)
558     {
559         CurrentTable = CONTAINING_RECORD(CurrentEntry,
560                                          KD_DISPATCH_TABLE,
561                                          KdProvidersList);
562 
563         CurrentTable->KdpPrintRoutine(String, Length);
564     }
565 }
566 
567 VOID
KdIoPuts(_In_ PCSTR String)568 KdIoPuts(
569     _In_ PCSTR String)
570 {
571     KdIoPrintString(String, (ULONG)strlen(String));
572 }
573 
574 VOID
575 __cdecl
KdIoPrintf(_In_ PCSTR Format,...)576 KdIoPrintf(
577     _In_ PCSTR Format,
578     ...)
579 {
580     va_list ap;
581     ULONG Length;
582     CHAR Buffer[512];
583 
584     /* Format the string */
585     va_start(ap, Format);
586     Length = (ULONG)_vsnprintf(Buffer,
587                                sizeof(Buffer),
588                                Format,
589                                ap);
590     va_end(ap);
591 
592     /* Send it to the display providers */
593     KdIoPrintString(Buffer, Length);
594 }
595 
596 #ifdef KDBG
597 extern const CSTRING KdbPromptStr;
598 #endif
599 
600 VOID
601 NTAPI
KdSendPacket(_In_ ULONG PacketType,_In_ PSTRING MessageHeader,_In_opt_ PSTRING MessageData,_Inout_ PKD_CONTEXT Context)602 KdSendPacket(
603     _In_ ULONG PacketType,
604     _In_ PSTRING MessageHeader,
605     _In_opt_ PSTRING MessageData,
606     _Inout_ PKD_CONTEXT Context)
607 {
608     PDBGKD_DEBUG_IO DebugIo;
609 
610     if (PacketType != PACKET_TYPE_KD_DEBUG_IO)
611     {
612         KdIoPrintf("%s: PacketType %d is UNIMPLEMENTED\n", __FUNCTION__, PacketType);
613         return;
614     }
615 
616     DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer;
617 
618     /* Validate API call */
619     if (MessageHeader->Length != sizeof(DBGKD_DEBUG_IO))
620         return;
621     if ((DebugIo->ApiNumber != DbgKdPrintStringApi) &&
622         (DebugIo->ApiNumber != DbgKdGetStringApi))
623     {
624         return;
625     }
626     if (!MessageData)
627         return;
628 
629     /* NOTE: MessageData->Length should be equal to
630      * DebugIo.u.PrintString.LengthOfString, or to
631      * DebugIo.u.GetString.LengthOfPromptString */
632 
633     if (!KdpDebugMode.Value)
634         return;
635 
636     /* Print the string proper */
637     KdIoPrintString(MessageData->Buffer, MessageData->Length);
638 }
639 
640 KDSTATUS
641 NTAPI
KdReceivePacket(_In_ ULONG PacketType,_Out_ PSTRING MessageHeader,_Out_ PSTRING MessageData,_Out_ PULONG DataLength,_Inout_ PKD_CONTEXT Context)642 KdReceivePacket(
643     _In_ ULONG PacketType,
644     _Out_ PSTRING MessageHeader,
645     _Out_ PSTRING MessageData,
646     _Out_ PULONG DataLength,
647     _Inout_ PKD_CONTEXT Context)
648 {
649 #ifdef KDBG
650     PDBGKD_DEBUG_IO DebugIo;
651     STRING ResponseString;
652     CHAR MessageBuffer[512];
653 #endif
654 
655     if (PacketType != PACKET_TYPE_KD_DEBUG_IO)
656     {
657         KdIoPrintf("%s: PacketType %d is UNIMPLEMENTED\n", __FUNCTION__, PacketType);
658         return KdPacketTimedOut;
659     }
660 
661 #ifdef KDBG
662     DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer;
663 
664     /* Validate API call */
665     if (MessageHeader->MaximumLength != sizeof(DBGKD_DEBUG_IO))
666         return KdPacketNeedsResend;
667     if (DebugIo->ApiNumber != DbgKdGetStringApi)
668         return KdPacketNeedsResend;
669 
670     /* NOTE: We cannot use directly MessageData->Buffer here as it points
671      * to the temporary KdpMessageBuffer scratch buffer that is being
672      * shared with all the possible I/O KD operations that may happen. */
673     ResponseString.Buffer = MessageBuffer;
674     ResponseString.Length = 0;
675     ResponseString.MaximumLength = min(sizeof(MessageBuffer),
676                                        MessageData->MaximumLength);
677     ResponseString.MaximumLength = min(ResponseString.MaximumLength,
678                                        DebugIo->u.GetString.LengthOfStringRead);
679 
680     /* The prompt string has been printed by KdSendPacket; go to
681      * new line and print the kdb prompt -- for SYSREG2 support. */
682     KdIoPrintString("\n", 1);
683     KdIoPuts(KdbPromptStr.Buffer); // Alternatively, use "Input> "
684 
685     if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
686         KbdDisableMouse();
687 
688     /*
689      * Read a NULL-terminated line of user input and retrieve its length.
690      * Official documentation states that DbgPrompt() includes a terminating
691      * newline character but does not NULL-terminate. However, experiments
692      * show that this behaviour is left at the discretion of WinDbg itself.
693      * WinDbg NULL-terminates the string unless its buffer is too short,
694      * in which case the string is simply truncated without NULL-termination.
695      */
696     ResponseString.Length =
697         (USHORT)KdIoReadLine(ResponseString.Buffer,
698                              ResponseString.MaximumLength);
699 
700     if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
701         KbdEnableMouse();
702 
703     /* Adjust and return the string length */
704     *DataLength = min(ResponseString.Length + sizeof(ANSI_NULL),
705                       DebugIo->u.GetString.LengthOfStringRead);
706     MessageData->Length = DebugIo->u.GetString.LengthOfStringRead = *DataLength;
707 
708     /* Only now we can copy back the data into MessageData->Buffer */
709     RtlCopyMemory(MessageData->Buffer, ResponseString.Buffer, *DataLength);
710 #endif
711 
712     return KdPacketReceived;
713 }
714 
715 /* EOF */
716