xref: /reactos/ntoskrnl/kd64/kdprint.c (revision 8a92b556)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/kd64/kdprint.c
5  * PURPOSE:         KD64 Trap Handler Routines
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Stefan Ginsberg (stefan.ginsberg@reactos.org)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define KD_PRINT_MAX_BYTES 512
17 
18 /* FUNCTIONS *****************************************************************/
19 
20 BOOLEAN
21 NTAPI
22 KdpPrintString(
23     _In_ PSTRING Output)
24 {
25     STRING Data, Header;
26     DBGKD_DEBUG_IO DebugIo;
27     USHORT Length;
28 
29     /* Copy the string */
30     KdpMoveMemory(KdpMessageBuffer,
31                   Output->Buffer,
32                   Output->Length);
33 
34     /* Make sure we don't exceed the KD Packet size */
35     Length = Output->Length;
36     if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE)
37     {
38         /* Normalize length */
39         Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
40     }
41 
42     /* Build the packet header */
43     DebugIo.ApiNumber = DbgKdPrintStringApi;
44     DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel;
45     DebugIo.Processor = KeGetCurrentPrcb()->Number;
46     DebugIo.u.PrintString.LengthOfString = Length;
47     Header.Length = sizeof(DBGKD_DEBUG_IO);
48     Header.Buffer = (PCHAR)&DebugIo;
49 
50     /* Build the data */
51     Data.Length = Length;
52     Data.Buffer = KdpMessageBuffer;
53 
54     /* Send the packet */
55     KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext);
56 
57     /* Check if the user pressed CTRL+C */
58     return KdpPollBreakInWithPortLock();
59 }
60 
61 BOOLEAN
62 NTAPI
63 KdpPromptString(
64     _In_ PSTRING PromptString,
65     _In_ PSTRING ResponseString)
66 {
67     STRING Data, Header;
68     DBGKD_DEBUG_IO DebugIo;
69     ULONG Length;
70     KDSTATUS Status;
71 
72     /* Copy the string to the message buffer */
73     KdpMoveMemory(KdpMessageBuffer,
74                   PromptString->Buffer,
75                   PromptString->Length);
76 
77     /* Make sure we don't exceed the KD Packet size */
78     Length = PromptString->Length;
79     if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE)
80     {
81         /* Normalize length */
82         Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
83     }
84 
85     /* Build the packet header */
86     DebugIo.ApiNumber = DbgKdGetStringApi;
87     DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel;
88     DebugIo.Processor = KeGetCurrentPrcb()->Number;
89     DebugIo.u.GetString.LengthOfPromptString = Length;
90     DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength;
91     Header.Length = sizeof(DBGKD_DEBUG_IO);
92     Header.Buffer = (PCHAR)&DebugIo;
93 
94     /* Build the data */
95     Data.Length = Length;
96     Data.Buffer = KdpMessageBuffer;
97 
98     /* Send the packet */
99     KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext);
100 
101     /* Set the maximum lengths for the receive */
102     Header.MaximumLength = sizeof(DBGKD_DEBUG_IO);
103     Data.MaximumLength = sizeof(KdpMessageBuffer);
104 
105     /* Enter receive loop */
106     do
107     {
108         /* Get our reply */
109         Status = KdReceivePacket(PACKET_TYPE_KD_DEBUG_IO,
110                                  &Header,
111                                  &Data,
112                                  &Length,
113                                  &KdpContext);
114 
115         /* Return TRUE if we need to resend */
116         if (Status == KdPacketNeedsResend) return TRUE;
117 
118     /* Loop until we succeed */
119     } while (Status != KdPacketReceived);
120 
121     /* Don't copy back a larger response than there is room for */
122     Length = min(Length,
123                  ResponseString->MaximumLength);
124 
125     /* Copy back the string and return the length */
126     KdpMoveMemory(ResponseString->Buffer,
127                   KdpMessageBuffer,
128                   Length);
129     ResponseString->Length = (USHORT)Length;
130 
131     /* Success; we don't need to resend */
132     return FALSE;
133 }
134 
135 VOID
136 NTAPI
137 KdpCommandString(IN PSTRING NameString,
138                  IN PSTRING CommandString,
139                  IN KPROCESSOR_MODE PreviousMode,
140                  IN PCONTEXT ContextRecord,
141                  IN PKTRAP_FRAME TrapFrame,
142                  IN PKEXCEPTION_FRAME ExceptionFrame)
143 {
144     BOOLEAN Enable;
145     PKPRCB Prcb = KeGetCurrentPrcb();
146 
147     /* Check if we need to do anything */
148     if ((PreviousMode != KernelMode) || (KdDebuggerNotPresent)) return;
149 
150     /* Enter the debugger */
151     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
152 
153     /* Save the CPU Control State and save the context */
154     KiSaveProcessorControlState(&Prcb->ProcessorState);
155     KdpMoveMemory(&Prcb->ProcessorState.ContextFrame,
156                   ContextRecord,
157                   sizeof(CONTEXT));
158 
159     /* Send the command string to the debugger */
160     KdpReportCommandStringStateChange(NameString,
161                                       CommandString,
162                                       &Prcb->ProcessorState.ContextFrame);
163 
164     /* Restore the processor state */
165     KdpMoveMemory(ContextRecord,
166                   &Prcb->ProcessorState.ContextFrame,
167                   sizeof(CONTEXT));
168     KiRestoreProcessorControlState(&Prcb->ProcessorState);
169 
170     /* Exit the debugger and return */
171     KdExitDebugger(Enable);
172 }
173 
174 VOID
175 NTAPI
176 KdpSymbol(IN PSTRING DllPath,
177           IN PKD_SYMBOLS_INFO SymbolInfo,
178           IN BOOLEAN Unload,
179           IN KPROCESSOR_MODE PreviousMode,
180           IN PCONTEXT ContextRecord,
181           IN PKTRAP_FRAME TrapFrame,
182           IN PKEXCEPTION_FRAME ExceptionFrame)
183 {
184     BOOLEAN Enable;
185     PKPRCB Prcb = KeGetCurrentPrcb();
186 
187     /* Check if we need to do anything */
188     if ((PreviousMode != KernelMode) || (KdDebuggerNotPresent)) return;
189 
190     /* Enter the debugger */
191     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
192 
193     /* Save the CPU Control State and save the context */
194     KiSaveProcessorControlState(&Prcb->ProcessorState);
195     KdpMoveMemory(&Prcb->ProcessorState.ContextFrame,
196                   ContextRecord,
197                   sizeof(CONTEXT));
198 
199     /* Report the new state */
200     KdpReportLoadSymbolsStateChange(DllPath,
201                                     SymbolInfo,
202                                     Unload,
203                                     &Prcb->ProcessorState.ContextFrame);
204 
205     /* Restore the processor state */
206     KdpMoveMemory(ContextRecord,
207                   &Prcb->ProcessorState.ContextFrame,
208                   sizeof(CONTEXT));
209     KiRestoreProcessorControlState(&Prcb->ProcessorState);
210 
211     /* Exit the debugger and return */
212     KdExitDebugger(Enable);
213 }
214 
215 USHORT
216 NTAPI
217 KdpPrompt(
218     _In_reads_bytes_(PromptLength) PCHAR PromptString,
219     _In_ USHORT PromptLength,
220     _Out_writes_bytes_(MaximumResponseLength) PCHAR ResponseString,
221     _In_ USHORT MaximumResponseLength,
222     _In_ KPROCESSOR_MODE PreviousMode,
223     _In_ PKTRAP_FRAME TrapFrame,
224     _In_ PKEXCEPTION_FRAME ExceptionFrame)
225 {
226     STRING PromptBuffer, ResponseBuffer;
227     BOOLEAN Enable, Resend;
228     PCHAR SafeResponseString;
229     CHAR CapturedPrompt[KD_PRINT_MAX_BYTES];
230     CHAR SafeResponseBuffer[KD_PRINT_MAX_BYTES];
231 
232     /* Normalize the lengths */
233     PromptLength = min(PromptLength,
234                        sizeof(CapturedPrompt));
235     MaximumResponseLength = min(MaximumResponseLength,
236                                 sizeof(SafeResponseBuffer));
237 
238     /* Check if we need to verify the string */
239     if (PreviousMode != KernelMode)
240     {
241         /* Handle user-mode buffers safely */
242         _SEH2_TRY
243         {
244             /* Probe and capture the prompt */
245             ProbeForRead(PromptString, PromptLength, 1);
246             KdpMoveMemory(CapturedPrompt, PromptString, PromptLength);
247             PromptString = CapturedPrompt;
248 
249             /* Probe and make room for the response */
250             ProbeForWrite(ResponseString, MaximumResponseLength, 1);
251             SafeResponseString = SafeResponseBuffer;
252         }
253         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
254         {
255             /* Bad string pointer, bail out */
256             _SEH2_YIELD(return 0);
257         }
258         _SEH2_END;
259     }
260     else
261     {
262         SafeResponseString = ResponseString;
263     }
264 
265     /* Setup the prompt and response buffers */
266     PromptBuffer.Buffer = PromptString;
267     PromptBuffer.Length = PromptBuffer.MaximumLength = PromptLength;
268     ResponseBuffer.Buffer = SafeResponseString;
269     ResponseBuffer.Length = 0;
270     ResponseBuffer.MaximumLength = MaximumResponseLength;
271 
272     /* Log the print */
273     //KdLogDbgPrint(&PromptBuffer);
274 
275     /* Enter the debugger */
276     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
277 
278     /* Enter prompt loop */
279     do
280     {
281         /* Send the prompt and receive the response */
282         Resend = KdpPromptString(&PromptBuffer, &ResponseBuffer);
283 
284     /* Loop while we need to resend */
285     } while (Resend);
286 
287     /* Exit the debugger */
288     KdExitDebugger(Enable);
289 
290     /* Copy back the response if required */
291     if (PreviousMode != KernelMode)
292     {
293         _SEH2_TRY
294         {
295             /* Safely copy back the response to user mode */
296             KdpMoveMemory(ResponseString,
297                           ResponseBuffer.Buffer,
298                           ResponseBuffer.Length);
299         }
300         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
301         {
302             /* String became invalid after we exited, fail */
303             _SEH2_YIELD(return 0);
304         }
305         _SEH2_END;
306     }
307 
308     /* Return the number of characters received */
309     return ResponseBuffer.Length;
310 }
311 
312 static
313 NTSTATUS
314 NTAPI
315 KdpPrintFromUser(
316     _In_ ULONG ComponentId,
317     _In_ ULONG Level,
318     _In_reads_bytes_(Length) PCHAR String,
319     _In_ USHORT Length,
320     _In_ KPROCESSOR_MODE PreviousMode,
321     _In_ PKTRAP_FRAME TrapFrame,
322     _In_ PKEXCEPTION_FRAME ExceptionFrame,
323     _Out_ PBOOLEAN Handled)
324 {
325     CHAR CapturedString[KD_PRINT_MAX_BYTES];
326 
327     ASSERT(PreviousMode == UserMode);
328     ASSERT(Length <= sizeof(CapturedString));
329 
330     /* Capture user-mode buffers */
331     _SEH2_TRY
332     {
333         /* Probe and capture the string */
334         ProbeForRead(String, Length, 1);
335         KdpMoveMemory(CapturedString, String, Length);
336         String = CapturedString;
337     }
338     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
339     {
340         /* Bad string pointer, bail out */
341         _SEH2_YIELD(return STATUS_ACCESS_VIOLATION);
342     }
343     _SEH2_END;
344 
345     /* Now go through the kernel-mode code path */
346     return KdpPrint(ComponentId,
347                     Level,
348                     String,
349                     Length,
350                     KernelMode,
351                     TrapFrame,
352                     ExceptionFrame,
353                     Handled);
354 }
355 
356 NTSTATUS
357 NTAPI
358 KdpPrint(
359     _In_ ULONG ComponentId,
360     _In_ ULONG Level,
361     _In_reads_bytes_(Length) PCHAR String,
362     _In_ USHORT Length,
363     _In_ KPROCESSOR_MODE PreviousMode,
364     _In_ PKTRAP_FRAME TrapFrame,
365     _In_ PKEXCEPTION_FRAME ExceptionFrame,
366     _Out_ PBOOLEAN Handled)
367 {
368     NTSTATUS Status;
369     BOOLEAN Enable;
370     STRING OutputString;
371 
372     if (NtQueryDebugFilterState(ComponentId, Level) == (NTSTATUS)FALSE)
373     {
374         /* Mask validation failed */
375         *Handled = TRUE;
376         return STATUS_SUCCESS;
377     }
378 
379     /* Assume failure */
380     *Handled = FALSE;
381 
382     /* Normalize the length */
383     Length = min(Length, KD_PRINT_MAX_BYTES);
384 
385     /* Check if we need to verify the string */
386     if (PreviousMode != KernelMode)
387     {
388         /* This case requires a 512 byte stack buffer.
389          * We don't want to use that much stack in the kernel case, but we
390          * can't use _alloca due to PSEH. So the buffer exists in this
391          * helper function instead.
392          */
393         return KdpPrintFromUser(ComponentId,
394                                 Level,
395                                 String,
396                                 Length,
397                                 PreviousMode,
398                                 TrapFrame,
399                                 ExceptionFrame,
400                                 Handled);
401     }
402 
403     /* Setup the output string */
404     OutputString.Buffer = String;
405     OutputString.Length = OutputString.MaximumLength = Length;
406 
407     /* Log the print */
408     //KdLogDbgPrint(&OutputString);
409 
410     /* Check for a debugger */
411     if (KdDebuggerNotPresent)
412     {
413         /* Fail */
414         *Handled = TRUE;
415         return STATUS_DEVICE_NOT_CONNECTED;
416     }
417 
418     /* Enter the debugger */
419     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
420 
421     /* Print the string */
422     if (KdpPrintString(&OutputString))
423     {
424         /* User pressed CTRL-C, breakpoint on return */
425         Status = STATUS_BREAKPOINT;
426     }
427     else
428     {
429         /* String was printed */
430         Status = STATUS_SUCCESS;
431     }
432 
433     /* Exit the debugger and return */
434     KdExitDebugger(Enable);
435     *Handled = TRUE;
436     return Status;
437 }
438 
439 VOID
440 __cdecl
441 KdpDprintf(
442     _In_ PCHAR Format,
443     ...)
444 {
445     STRING String;
446     USHORT Length;
447     va_list ap;
448     CHAR Buffer[100];
449 
450     /* Format the string */
451     va_start(ap, Format);
452     Length = (USHORT)_vsnprintf(Buffer,
453                                 sizeof(Buffer),
454                                 Format,
455                                 ap);
456     va_end(ap);
457 
458     /* Set it up */
459     String.Buffer = Buffer;
460     String.Length = String.MaximumLength = Length;
461 
462     /* Send it to the debugger directly */
463     KdpPrintString(&String);
464 }
465