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