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