xref: /reactos/ntoskrnl/kd/kdio.c (revision fc3ccb39)
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 #define KdpScreenLineLengthDefault 80
35 static CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = "";
36 static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
37 
38 const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024;
39 PCHAR KdpDmesgBuffer = NULL;
40 volatile ULONG KdpDmesgCurrentPosition = 0;
41 volatile ULONG KdpDmesgFreeBytes = 0;
42 volatile ULONG KdbDmesgTotalWritten = 0;
43 volatile BOOLEAN KdbpIsInDmesgMode = FALSE;
44 static KSPIN_LOCK KdpDmesgLogSpinLock;
45 
46 KDP_DEBUG_MODE KdpDebugMode;
47 LIST_ENTRY KdProviders = {&KdProviders, &KdProviders};
48 KD_DISPATCH_TABLE DispatchTable[KdMax];
49 
50 PKDP_INIT_ROUTINE InitRoutines[KdMax] = {KdpScreenInit,
51                                          KdpSerialInit,
52                                          KdpDebugLogInit,
53                                          KdpKdbgInit};
54 
55 static ULONG KdbgNextApiNumber = DbgKdContinueApi;
56 static CONTEXT KdbgContext;
57 static EXCEPTION_RECORD64 KdbgExceptionRecord;
58 static BOOLEAN KdbgFirstChanceException;
59 static KPROCESSOR_MODE KdbgPreviousMode;
60 static NTSTATUS KdbgContinueStatus = STATUS_SUCCESS;
61 
62 /* LOCKING FUNCTIONS *********************************************************/
63 
64 KIRQL
65 NTAPI
66 KdpAcquireLock(IN PKSPIN_LOCK SpinLock)
67 {
68     KIRQL OldIrql;
69 
70     /* Acquire the spinlock without waiting at raised IRQL */
71     while (TRUE)
72     {
73         /* Loop until the spinlock becomes available */
74         while (!KeTestSpinLock(SpinLock));
75 
76         /* Spinlock is free, raise IRQL to high level */
77         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
78 
79         /* Try to get the spinlock */
80         if (KeTryToAcquireSpinLockAtDpcLevel(SpinLock))
81             break;
82 
83         /* Someone else got the spinlock, lower IRQL back */
84         KeLowerIrql(OldIrql);
85     }
86 
87     return OldIrql;
88 }
89 
90 VOID
91 NTAPI
92 KdpReleaseLock(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
107 KdpLoggerThread(PVOID Context)
108 {
109     ULONG beg, end, num;
110     IO_STATUS_BLOCK Iosb;
111 
112     KdpLoggingEnabled = TRUE;
113 
114     while (TRUE)
115     {
116         KeWaitForSingleObject(&KdpLoggerThreadEvent, Executive, KernelMode, FALSE, NULL);
117 
118         /* Bug */
119         /* Keep KdpCurrentPosition and KdpFreeBytes values in local
120          * variables to avoid their possible change from Producer part,
121          * KdpPrintToLogFile function
122          */
123         end = KdpCurrentPosition;
124         num = KdpFreeBytes;
125 
126         /* Now securely calculate values, based on local variables */
127         beg = (end + num) % KdpBufferSize;
128         num = KdpBufferSize - num;
129 
130         /* Nothing to do? */
131         if (num == 0)
132             continue;
133 
134         if (end > beg)
135         {
136             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
137                         KdpDebugBuffer + beg, num, NULL, NULL);
138         }
139         else
140         {
141             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
142                         KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL);
143 
144             NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
145                         KdpDebugBuffer, end, NULL, NULL);
146         }
147 
148         (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num);
149     }
150 }
151 
152 static VOID
153 NTAPI
154 KdpPrintToLogFile(PCHAR String,
155                   ULONG StringLength)
156 {
157     KIRQL OldIrql;
158     ULONG beg, end, num;
159 
160     if (KdpDebugBuffer == NULL) return;
161 
162     /* Acquire the printing spinlock without waiting at raised IRQL */
163     OldIrql = KdpAcquireLock(&KdpDebugLogSpinLock);
164 
165     beg = KdpCurrentPosition;
166     num = KdpFreeBytes;
167     if (StringLength < num)
168         num = StringLength;
169 
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     KdpReleaseLock(&KdpDebugLogSpinLock, OldIrql);
189 
190     /* Signal the logger thread */
191     if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
192         KeSetEvent(&KdpLoggerThreadEvent, IO_NO_INCREMENT, FALSE);
193 }
194 
195 VOID
196 NTAPI
197 KdpDebugLogInit(PKD_DISPATCH_TABLE DispatchTable,
198                 ULONG BootPhase)
199 {
200     NTSTATUS Status;
201     UNICODE_STRING FileName;
202     OBJECT_ATTRIBUTES ObjectAttributes;
203     IO_STATUS_BLOCK Iosb;
204     HANDLE ThreadHandle;
205     KPRIORITY Priority;
206 
207     if (!KdpDebugMode.File) return;
208 
209     if (BootPhase == 0)
210     {
211         KdComPortInUse = NULL;
212 
213         /* Write out the functions that we support for now */
214         DispatchTable->KdpInitRoutine = KdpDebugLogInit;
215         DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
216 
217         /* Register as a Provider */
218         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
219     }
220     else if (BootPhase == 1)
221     {
222         /* Allocate a buffer for debug log */
223         KdpDebugBuffer = ExAllocatePool(NonPagedPool, KdpBufferSize);
224         KdpFreeBytes = KdpBufferSize;
225 
226         /* Initialize spinlock */
227         KeInitializeSpinLock(&KdpDebugLogSpinLock);
228     }
229     else if (BootPhase == 2)
230     {
231         HalDisplayString("\r\n   File log debugging enabled\r\n\r\n");
232     }
233     else if (BootPhase == 3)
234     {
235         /* Setup the log name */
236         Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
237         if (!NT_SUCCESS(Status)) return;
238 
239         InitializeObjectAttributes(&ObjectAttributes,
240                                    &FileName,
241                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
242                                    NULL,
243                                    NULL);
244 
245         /* Create the log file */
246         Status = NtCreateFile(&KdpLogFileHandle,
247                               FILE_APPEND_DATA | SYNCHRONIZE,
248                               &ObjectAttributes,
249                               &Iosb,
250                               NULL,
251                               FILE_ATTRIBUTE_NORMAL,
252                               FILE_SHARE_READ,
253                               FILE_SUPERSEDE,
254                               FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
255                               NULL,
256                               0);
257 
258         RtlFreeUnicodeString(&FileName);
259 
260         if (!NT_SUCCESS(Status))
261             return;
262 
263         KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE);
264 
265         /* Create the logger thread */
266         Status = PsCreateSystemThread(&ThreadHandle,
267                                       THREAD_ALL_ACCESS,
268                                       NULL,
269                                       NULL,
270                                       NULL,
271                                       KdpLoggerThread,
272                                       NULL);
273         if (!NT_SUCCESS(Status))
274         {
275             NtClose(KdpLogFileHandle);
276             return;
277         }
278 
279         Priority = 7;
280         NtSetInformationThread(ThreadHandle,
281                                ThreadPriority,
282                                &Priority,
283                                sizeof(Priority));
284     }
285 }
286 
287 /* SERIAL FUNCTIONS **********************************************************/
288 
289 VOID
290 NTAPI
291 KdpSerialDebugPrint(PCHAR Message,
292                     ULONG Length)
293 {
294     PCHAR pch = (PCHAR)Message;
295     KIRQL OldIrql;
296 
297     /* Acquire the printing spinlock without waiting at raised IRQL */
298     OldIrql = KdpAcquireLock(&KdpSerialSpinLock);
299 
300     /* Output the message */
301     while (pch < Message + Length && *pch != '\0')
302     {
303         if (*pch == '\n')
304         {
305             KdPortPutByteEx(&SerialPortInfo, '\r');
306         }
307         KdPortPutByteEx(&SerialPortInfo, *pch);
308         pch++;
309     }
310 
311     /* Release the spinlock */
312     KdpReleaseLock(&KdpSerialSpinLock, OldIrql);
313 }
314 
315 VOID
316 NTAPI
317 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
318               ULONG BootPhase)
319 {
320     if (!KdpDebugMode.Serial) return;
321 
322     if (BootPhase == 0)
323     {
324         /* Write out the functions that we support for now */
325         DispatchTable->KdpInitRoutine = KdpSerialInit;
326         DispatchTable->KdpPrintRoutine = KdpSerialDebugPrint;
327 
328         /* Initialize the Port */
329         if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber))
330         {
331             KdpDebugMode.Serial = FALSE;
332             return;
333         }
334         KdComPortInUse = SerialPortInfo.Address;
335 
336         /* Initialize spinlock */
337         KeInitializeSpinLock(&KdpSerialSpinLock);
338 
339         /* Register as a Provider */
340         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
341     }
342     else if (BootPhase == 2)
343     {
344         HalDisplayString("\r\n   Serial debugging enabled\r\n\r\n");
345     }
346 }
347 
348 /* SCREEN FUNCTIONS **********************************************************/
349 
350 VOID
351 KdpScreenAcquire(VOID)
352 {
353     if (InbvIsBootDriverInstalled() /* &&
354         !InbvCheckDisplayOwnership() */)
355     {
356         /* Acquire ownership and reset the display */
357         InbvAcquireDisplayOwnership();
358         InbvResetDisplay();
359         InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
360         InbvSetTextColor(BV_COLOR_WHITE);
361         InbvInstallDisplayStringFilter(NULL);
362         InbvEnableDisplayString(TRUE);
363         InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
364     }
365 }
366 
367 // extern VOID NTAPI InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned);
368 
369 VOID
370 KdpScreenRelease(VOID)
371 {
372     if (InbvIsBootDriverInstalled()&&
373         InbvCheckDisplayOwnership())
374     {
375         /* Release the display */
376         // InbvSetDisplayOwnership(FALSE);
377         InbvNotifyDisplayOwnershipLost(NULL);
378     }
379 }
380 
381 /*
382  * Screen debug logger function KdpScreenPrint() writes text messages into
383  * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
384  * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
385  * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
386  */
387 static VOID
388 NTAPI
389 KdpScreenPrint(PCHAR Message,
390                ULONG Length)
391 {
392     PCHAR pch = (PCHAR)Message;
393     KIRQL OldIrql;
394     ULONG beg, end, num;
395 
396     while (pch < Message + Length && *pch)
397     {
398         if (*pch == '\b')
399         {
400             /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
401             if (KdpScreenLineLength > 0)
402             {
403                 /* Remove last character from buffer */
404                 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0';
405                 KdpScreenLineBufferPos = KdpScreenLineLength;
406 
407                 /* Clear row and print line again */
408                 HalDisplayString("\r");
409                 HalDisplayString(KdpScreenLineBuffer);
410             }
411         }
412         else
413         {
414             KdpScreenLineBuffer[KdpScreenLineLength++] = *pch;
415             KdpScreenLineBuffer[KdpScreenLineLength] = '\0';
416         }
417 
418         if (*pch == '\n' || KdpScreenLineLength == KdpScreenLineLengthDefault)
419         {
420             /* Print buffered characters */
421             if (KdpScreenLineBufferPos != KdpScreenLineLength)
422                 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
423 
424             /* Clear line buffer */
425             KdpScreenLineBuffer[0] = '\0';
426             KdpScreenLineLength = KdpScreenLineBufferPos = 0;
427         }
428 
429         ++pch;
430     }
431 
432     /* Print buffered characters */
433     if (KdpScreenLineBufferPos != KdpScreenLineLength)
434     {
435         HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
436         KdpScreenLineBufferPos = KdpScreenLineLength;
437     }
438 
439     /* Dmesg: store Message in the buffer to show it later */
440     if (KdbpIsInDmesgMode)
441        return;
442 
443     if (KdpDmesgBuffer == NULL)
444       return;
445 
446     /* Acquire the printing spinlock without waiting at raised IRQL */
447     OldIrql = KdpAcquireLock(&KdpDmesgLogSpinLock);
448 
449     /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
450      * set num to min(KdpDmesgFreeBytes, Length).
451      */
452     num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes;
453     beg = KdpDmesgCurrentPosition;
454     if (num != 0)
455     {
456         end = (beg + num) % KdpDmesgBufferSize;
457         if (end > beg)
458         {
459             RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length);
460         }
461         else
462         {
463             RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg);
464             RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end);
465         }
466         KdpDmesgCurrentPosition = end;
467 
468         /* Counting the total bytes written */
469         KdbDmesgTotalWritten += num;
470     }
471 
472     /* Release the spinlock */
473     KdpReleaseLock(&KdpDmesgLogSpinLock, OldIrql);
474 
475     /* Optional step(?): find out a way to notify about buffer exhaustion,
476      * and possibly fall into kbd to use dmesg command: user will read
477      * debug messages before they will be wiped over by next writes.
478      */
479 }
480 
481 VOID
482 NTAPI
483 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
484               ULONG BootPhase)
485 {
486     if (!KdpDebugMode.Screen) return;
487 
488     if (BootPhase == 0)
489     {
490         /* Write out the functions that we support for now */
491         DispatchTable->KdpInitRoutine = KdpScreenInit;
492         DispatchTable->KdpPrintRoutine = KdpScreenPrint;
493 
494         /* Register as a Provider */
495         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
496     }
497     else if (BootPhase == 1)
498     {
499         /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
500          * see kdbp_cli.c:KdbpCmdDmesg()/2
501          */
502         KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1);
503         RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1);
504         KdpDmesgFreeBytes = KdpDmesgBufferSize;
505         KdbDmesgTotalWritten = 0;
506 
507         /* Take control of the display */
508         KdpScreenAcquire();
509 
510         /* Initialize spinlock */
511         KeInitializeSpinLock(&KdpDmesgLogSpinLock);
512     }
513     else if (BootPhase == 2)
514     {
515         HalDisplayString("\r\n   Screen debugging enabled\r\n\r\n");
516     }
517 }
518 
519 /* GENERAL FUNCTIONS *********************************************************/
520 
521 BOOLEAN
522 NTAPI
523 KdpPrintString(
524     _In_ PSTRING Output);
525 
526 extern STRING KdbPromptString;
527 
528 VOID
529 NTAPI
530 KdSendPacket(
531     IN ULONG PacketType,
532     IN PSTRING MessageHeader,
533     IN PSTRING MessageData,
534     IN OUT PKD_CONTEXT Context)
535 {
536     if (PacketType == PACKET_TYPE_KD_DEBUG_IO)
537     {
538         PSTRING Output = MessageData;
539         PLIST_ENTRY CurrentEntry;
540         PKD_DISPATCH_TABLE CurrentTable;
541 
542         if (!KdpDebugMode.Value) return;
543 
544         /* Call the registered handlers */
545         CurrentEntry = KdProviders.Flink;
546         while (CurrentEntry != &KdProviders)
547         {
548             /* Get the current table */
549             CurrentTable = CONTAINING_RECORD(CurrentEntry,
550                                              KD_DISPATCH_TABLE,
551                                              KdProvidersList);
552 
553             /* Call it */
554             CurrentTable->KdpPrintRoutine(Output->Buffer, Output->Length);
555 
556             /* Next Table */
557             CurrentEntry = CurrentEntry->Flink;
558         }
559         return;
560     }
561     else if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64)
562     {
563         PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange = (PDBGKD_ANY_WAIT_STATE_CHANGE)MessageHeader->Buffer;
564         if (WaitStateChange->NewState == DbgKdLoadSymbolsStateChange)
565         {
566 #ifdef KDBG
567             PLDR_DATA_TABLE_ENTRY LdrEntry;
568             if (!WaitStateChange->u.LoadSymbols.UnloadSymbols)
569             {
570                 /* Load symbols. Currently implemented only for KDBG! */
571                 if (KdbpSymFindModule((PVOID)(ULONG_PTR)WaitStateChange->u.LoadSymbols.BaseOfDll, NULL, -1, &LdrEntry))
572                 {
573                     KdbSymProcessSymbols(LdrEntry);
574                 }
575             }
576 #endif
577             return;
578         }
579         else if (WaitStateChange->NewState == DbgKdExceptionStateChange)
580         {
581             KdbgNextApiNumber = DbgKdGetContextApi;
582             KdbgExceptionRecord = WaitStateChange->u.Exception.ExceptionRecord;
583             KdbgFirstChanceException = WaitStateChange->u.Exception.FirstChance;
584             KdbgPreviousMode = ((PKTHREAD)(ULONG_PTR)WaitStateChange->Thread)->PreviousMode;
585             return;
586         }
587     }
588     else if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE)
589     {
590         PDBGKD_MANIPULATE_STATE64 ManipulateState = (PDBGKD_MANIPULATE_STATE64)MessageHeader->Buffer;
591         if (ManipulateState->ApiNumber == DbgKdGetContextApi)
592         {
593             KD_CONTINUE_TYPE Result;
594 
595 #ifdef KDBG
596             /* Check if this is an assertion failure */
597             if (KdbgExceptionRecord.ExceptionCode == STATUS_ASSERTION_FAILURE)
598             {
599                 /* Bump EIP to the instruction following the int 2C */
600                 KdbgContext.Eip += 2;
601             }
602 
603             Result = KdbEnterDebuggerException(&KdbgExceptionRecord,
604                                                KdbgPreviousMode,
605                                                &KdbgContext,
606                                                KdbgFirstChanceException);
607 #else
608             /* We'll manually dump the stack for the user... */
609             KeRosDumpStackFrames(NULL, 0);
610             Result = kdHandleException;
611 #endif
612             if (Result != kdHandleException)
613                 KdbgContinueStatus = STATUS_SUCCESS;
614             else
615                 KdbgContinueStatus = STATUS_UNSUCCESSFUL;
616             KdbgNextApiNumber = DbgKdSetContextApi;
617             return;
618         }
619         else if (ManipulateState->ApiNumber == DbgKdSetContextApi)
620         {
621             KdbgNextApiNumber = DbgKdContinueApi;
622             return;
623         }
624     }
625     UNIMPLEMENTED;
626 }
627 
628 KDSTATUS
629 NTAPI
630 KdReceivePacket(
631     IN ULONG PacketType,
632     OUT PSTRING MessageHeader,
633     OUT PSTRING MessageData,
634     OUT PULONG DataLength,
635     IN OUT PKD_CONTEXT Context)
636 {
637 #ifdef KDBG
638     KIRQL OldIrql;
639     STRING StringChar;
640     CHAR Response;
641     USHORT i;
642     ULONG DummyScanCode;
643     CHAR MessageBuffer[100];
644     STRING ResponseString;
645 #endif
646 
647     if (PacketType == PACKET_TYPE_KD_STATE_MANIPULATE)
648     {
649         PDBGKD_MANIPULATE_STATE64 ManipulateState = (PDBGKD_MANIPULATE_STATE64)MessageHeader->Buffer;
650         RtlZeroMemory(MessageHeader->Buffer, MessageHeader->MaximumLength);
651         if (KdbgNextApiNumber == DbgKdGetContextApi)
652         {
653             ManipulateState->ApiNumber = DbgKdGetContextApi;
654             MessageData->Length = 0;
655             MessageData->Buffer = (PCHAR)&KdbgContext;
656             return KdPacketReceived;
657         }
658         else if (KdbgNextApiNumber == DbgKdSetContextApi)
659         {
660             ManipulateState->ApiNumber = DbgKdSetContextApi;
661             MessageData->Length = sizeof(KdbgContext);
662             MessageData->Buffer = (PCHAR)&KdbgContext;
663             return KdPacketReceived;
664         }
665         else if (KdbgNextApiNumber != DbgKdContinueApi)
666         {
667             UNIMPLEMENTED;
668         }
669         ManipulateState->ApiNumber = DbgKdContinueApi;
670         ManipulateState->u.Continue.ContinueStatus = KdbgContinueStatus;
671 
672         /* Prepare for next time */
673         KdbgNextApiNumber = DbgKdContinueApi;
674         KdbgContinueStatus = STATUS_SUCCESS;
675 
676         return KdPacketReceived;
677     }
678 
679     if (PacketType != PACKET_TYPE_KD_DEBUG_IO)
680         return KdPacketTimedOut;
681 
682 #ifdef KDBG
683     ResponseString.Buffer = MessageBuffer;
684     ResponseString.Length = 0;
685     ResponseString.MaximumLength = min(sizeof(MessageBuffer), MessageData->MaximumLength);
686     StringChar.Buffer = &Response;
687     StringChar.Length = StringChar.MaximumLength = sizeof(Response);
688 
689     /* Display the string and print a new line for log neatness */
690     *StringChar.Buffer = '\n';
691     KdpPrintString(&StringChar);
692 
693     /* Print the kdb prompt */
694     KdpPrintString(&KdbPromptString);
695 
696     // TODO: Use an improved KdbpReadCommand() function for our purposes.
697 
698     /* Acquire the printing spinlock without waiting at raised IRQL */
699     OldIrql = KdpAcquireLock(&KdpSerialSpinLock);
700 
701     if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
702         KbdDisableMouse();
703 
704     /* Loop the whole string */
705     for (i = 0; i < ResponseString.MaximumLength; i++)
706     {
707         /* Check if this is serial debugging mode */
708         if (KdbDebugState & KD_DEBUG_KDSERIAL)
709         {
710             /* Get the character from serial */
711             do
712             {
713                 Response = KdbpTryGetCharSerial(MAXULONG);
714             } while (Response == -1);
715         }
716         else
717         {
718             /* Get the response from the keyboard */
719             do
720             {
721                 Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG);
722             } while (Response == -1);
723         }
724 
725         /* Check for return */
726         if (Response == '\r')
727         {
728             /*
729              * We might need to discard the next '\n'.
730              * Wait a bit to make sure we receive it.
731              */
732             KeStallExecutionProcessor(100000);
733 
734             /* Check the mode */
735             if (KdbDebugState & KD_DEBUG_KDSERIAL)
736             {
737                 /* Read and discard the next character, if any */
738                 KdbpTryGetCharSerial(5);
739             }
740             else
741             {
742                 /* Read and discard the next character, if any */
743                 KdbpTryGetCharKeyboard(&DummyScanCode, 5);
744             }
745 
746             /*
747              * Null terminate the output string -- documentation states that
748              * DbgPrompt does not null terminate, but it does
749              */
750             *(PCHAR)(ResponseString.Buffer + i) = 0;
751             break;
752         }
753 
754         /* Write it back and print it to the log */
755         *(PCHAR)(ResponseString.Buffer + i) = Response;
756         KdpReleaseLock(&KdpSerialSpinLock, OldIrql);
757         KdpPrintString(&StringChar);
758         OldIrql = KdpAcquireLock(&KdpSerialSpinLock);
759     }
760 
761     /* Print a new line */
762     *StringChar.Buffer = '\n';
763     KdpPrintString(&StringChar);
764 
765     /* Return the length */
766     RtlCopyMemory(MessageData->Buffer, ResponseString.Buffer, i);
767     *DataLength = i;
768 
769     if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
770         KbdEnableMouse();
771 
772     /* Release the spinlock */
773     KdpReleaseLock(&KdpSerialSpinLock, OldIrql);
774 
775 #endif
776     return KdPacketReceived;
777 }
778 
779 /* EOF */
780