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