xref: /reactos/ntoskrnl/kd64/kdprint.c (revision 14d3b53c)
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 KIRQL
21 NTAPI
22 KdpAcquireLock(
23     _In_ PKSPIN_LOCK SpinLock)
24 {
25     KIRQL OldIrql;
26 
27     /* Acquire the spinlock without waiting at raised IRQL */
28     while (TRUE)
29     {
30         /* Loop until the spinlock becomes available */
31         while (!KeTestSpinLock(SpinLock));
32 
33         /* Spinlock is free, raise IRQL to high level */
34         KeRaiseIrql(HIGH_LEVEL, &OldIrql);
35 
36         /* Try to get the spinlock */
37         if (KeTryToAcquireSpinLockAtDpcLevel(SpinLock))
38             break;
39 
40         /* Someone else got the spinlock, lower IRQL back */
41         KeLowerIrql(OldIrql);
42     }
43 
44     return OldIrql;
45 }
46 
47 VOID
48 NTAPI
49 KdpReleaseLock(
50     _In_ PKSPIN_LOCK SpinLock,
51     _In_ KIRQL OldIrql)
52 {
53     /* Release the spinlock */
54     KiReleaseSpinLock(SpinLock);
55     // KeReleaseSpinLockFromDpcLevel(SpinLock);
56 
57     /* Restore the old IRQL */
58     KeLowerIrql(OldIrql);
59 }
60 
61 VOID
62 NTAPI
63 KdLogDbgPrint(
64     _In_ PSTRING String)
65 {
66     SIZE_T Length, Remaining;
67     KIRQL OldIrql;
68 
69     /* If the string is empty, bail out */
70     if (!String->Buffer || (String->Length == 0))
71         return;
72 
73     /* If no log buffer available, bail out */
74     if (!KdPrintCircularBuffer /*|| (KdPrintBufferSize == 0)*/)
75         return;
76 
77     /* Acquire the log spinlock without waiting at raised IRQL */
78     OldIrql = KdpAcquireLock(&KdpPrintSpinLock);
79 
80     Length = min(String->Length, KdPrintBufferSize);
81     Remaining = KdPrintCircularBuffer + KdPrintBufferSize - KdPrintWritePointer;
82 
83     if (Length < Remaining)
84     {
85         KdpMoveMemory(KdPrintWritePointer, String->Buffer, Length);
86         KdPrintWritePointer += Length;
87     }
88     else
89     {
90         KdpMoveMemory(KdPrintWritePointer, String->Buffer, Remaining);
91         Length -= Remaining;
92         if (Length > 0)
93             KdpMoveMemory(KdPrintCircularBuffer, String->Buffer + Remaining, Length);
94 
95         KdPrintWritePointer = KdPrintCircularBuffer + Length;
96 
97         /* Got a rollover, update count (handle wrapping, must always be >= 1) */
98         ++KdPrintRolloverCount;
99         if (KdPrintRolloverCount == 0)
100             ++KdPrintRolloverCount;
101     }
102 
103     /* Release the spinlock */
104     KdpReleaseLock(&KdpPrintSpinLock, OldIrql);
105 }
106 
107 BOOLEAN
108 NTAPI
109 KdpPrintString(
110     _In_ PSTRING Output)
111 {
112     STRING Data, Header;
113     DBGKD_DEBUG_IO DebugIo;
114     USHORT Length;
115 
116     /* Copy the string */
117     KdpMoveMemory(KdpMessageBuffer,
118                   Output->Buffer,
119                   Output->Length);
120 
121     /* Make sure we don't exceed the KD Packet size */
122     Length = Output->Length;
123     if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE)
124     {
125         /* Normalize length */
126         Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
127     }
128 
129     /* Build the packet header */
130     DebugIo.ApiNumber = DbgKdPrintStringApi;
131     DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel;
132     DebugIo.Processor = KeGetCurrentPrcb()->Number;
133     DebugIo.u.PrintString.LengthOfString = Length;
134     Header.Length = sizeof(DBGKD_DEBUG_IO);
135     Header.Buffer = (PCHAR)&DebugIo;
136 
137     /* Build the data */
138     Data.Length = Length;
139     Data.Buffer = KdpMessageBuffer;
140 
141     /* Send the packet */
142     KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext);
143 
144     /* Check if the user pressed CTRL+C */
145     return KdpPollBreakInWithPortLock();
146 }
147 
148 BOOLEAN
149 NTAPI
150 KdpPromptString(
151     _In_ PSTRING PromptString,
152     _In_ PSTRING ResponseString)
153 {
154     STRING Data, Header;
155     DBGKD_DEBUG_IO DebugIo;
156     ULONG Length;
157     KDSTATUS Status;
158 
159     /* Copy the string to the message buffer */
160     KdpMoveMemory(KdpMessageBuffer,
161                   PromptString->Buffer,
162                   PromptString->Length);
163 
164     /* Make sure we don't exceed the KD Packet size */
165     Length = PromptString->Length;
166     if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE)
167     {
168         /* Normalize length */
169         Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
170     }
171 
172     /* Build the packet header */
173     DebugIo.ApiNumber = DbgKdGetStringApi;
174     DebugIo.ProcessorLevel = (USHORT)KeProcessorLevel;
175     DebugIo.Processor = KeGetCurrentPrcb()->Number;
176     DebugIo.u.GetString.LengthOfPromptString = Length;
177     DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength;
178     Header.Length = sizeof(DBGKD_DEBUG_IO);
179     Header.Buffer = (PCHAR)&DebugIo;
180 
181     /* Build the data */
182     Data.Length = Length;
183     Data.Buffer = KdpMessageBuffer;
184 
185     /* Send the packet */
186     KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &Header, &Data, &KdpContext);
187 
188     /* Set the maximum lengths for the receive */
189     Header.MaximumLength = sizeof(DBGKD_DEBUG_IO);
190     Data.MaximumLength = sizeof(KdpMessageBuffer);
191 
192     /* Enter receive loop */
193     do
194     {
195         /* Get our reply */
196         Status = KdReceivePacket(PACKET_TYPE_KD_DEBUG_IO,
197                                  &Header,
198                                  &Data,
199                                  &Length,
200                                  &KdpContext);
201 
202         /* Return TRUE if we need to resend */
203         if (Status == KdPacketNeedsResend) return TRUE;
204 
205     /* Loop until we succeed */
206     } while (Status != KdPacketReceived);
207 
208     /* Don't copy back a larger response than there is room for */
209     Length = min(Length,
210                  ResponseString->MaximumLength);
211 
212     /* Copy back the string and return the length */
213     KdpMoveMemory(ResponseString->Buffer,
214                   KdpMessageBuffer,
215                   Length);
216     ResponseString->Length = (USHORT)Length;
217 
218     /* Success; we don't need to resend */
219     return FALSE;
220 }
221 
222 VOID
223 NTAPI
224 KdpCommandString(IN PSTRING NameString,
225                  IN PSTRING CommandString,
226                  IN KPROCESSOR_MODE PreviousMode,
227                  IN PCONTEXT ContextRecord,
228                  IN PKTRAP_FRAME TrapFrame,
229                  IN PKEXCEPTION_FRAME ExceptionFrame)
230 {
231     BOOLEAN Enable;
232     PKPRCB Prcb = KeGetCurrentPrcb();
233 
234     /* Check if we need to do anything */
235     if ((PreviousMode != KernelMode) || (KdDebuggerNotPresent)) return;
236 
237     /* Enter the debugger */
238     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
239 
240     /* Save the CPU Control State and save the context */
241     KiSaveProcessorControlState(&Prcb->ProcessorState);
242     KdpMoveMemory(&Prcb->ProcessorState.ContextFrame,
243                   ContextRecord,
244                   sizeof(CONTEXT));
245 
246     /* Send the command string to the debugger */
247     KdpReportCommandStringStateChange(NameString,
248                                       CommandString,
249                                       &Prcb->ProcessorState.ContextFrame);
250 
251     /* Restore the processor state */
252     KdpMoveMemory(ContextRecord,
253                   &Prcb->ProcessorState.ContextFrame,
254                   sizeof(CONTEXT));
255     KiRestoreProcessorControlState(&Prcb->ProcessorState);
256 
257     /* Exit the debugger and return */
258     KdExitDebugger(Enable);
259 }
260 
261 VOID
262 NTAPI
263 KdpSymbol(IN PSTRING DllPath,
264           IN PKD_SYMBOLS_INFO SymbolInfo,
265           IN BOOLEAN Unload,
266           IN KPROCESSOR_MODE PreviousMode,
267           IN PCONTEXT ContextRecord,
268           IN PKTRAP_FRAME TrapFrame,
269           IN PKEXCEPTION_FRAME ExceptionFrame)
270 {
271     BOOLEAN Enable;
272     PKPRCB Prcb = KeGetCurrentPrcb();
273 
274     /* Check if we need to do anything */
275     if ((PreviousMode != KernelMode) || (KdDebuggerNotPresent)) return;
276 
277     /* Enter the debugger */
278     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
279 
280     /* Save the CPU Control State and save the context */
281     KiSaveProcessorControlState(&Prcb->ProcessorState);
282     KdpMoveMemory(&Prcb->ProcessorState.ContextFrame,
283                   ContextRecord,
284                   sizeof(CONTEXT));
285 
286     /* Report the new state */
287     KdpReportLoadSymbolsStateChange(DllPath,
288                                     SymbolInfo,
289                                     Unload,
290                                     &Prcb->ProcessorState.ContextFrame);
291 
292     /* Restore the processor state */
293     KdpMoveMemory(ContextRecord,
294                   &Prcb->ProcessorState.ContextFrame,
295                   sizeof(CONTEXT));
296     KiRestoreProcessorControlState(&Prcb->ProcessorState);
297 
298     /* Exit the debugger and return */
299     KdExitDebugger(Enable);
300 }
301 
302 USHORT
303 NTAPI
304 KdpPrompt(
305     _In_reads_bytes_(PromptLength) PCHAR PromptString,
306     _In_ USHORT PromptLength,
307     _Out_writes_bytes_(MaximumResponseLength) PCHAR ResponseString,
308     _In_ USHORT MaximumResponseLength,
309     _In_ KPROCESSOR_MODE PreviousMode,
310     _In_ PKTRAP_FRAME TrapFrame,
311     _In_ PKEXCEPTION_FRAME ExceptionFrame)
312 {
313     STRING PromptBuffer, ResponseBuffer;
314     BOOLEAN Enable, Resend;
315     PCHAR SafeResponseString;
316     CHAR CapturedPrompt[KD_PRINT_MAX_BYTES];
317     CHAR SafeResponseBuffer[KD_PRINT_MAX_BYTES];
318 
319     /* Normalize the lengths */
320     PromptLength = min(PromptLength,
321                        sizeof(CapturedPrompt));
322     MaximumResponseLength = min(MaximumResponseLength,
323                                 sizeof(SafeResponseBuffer));
324 
325     /* Check if we need to verify the string */
326     if (PreviousMode != KernelMode)
327     {
328         /* Handle user-mode buffers safely */
329         _SEH2_TRY
330         {
331             /* Probe and capture the prompt */
332             ProbeForRead(PromptString, PromptLength, 1);
333             KdpMoveMemory(CapturedPrompt, PromptString, PromptLength);
334             PromptString = CapturedPrompt;
335 
336             /* Probe and make room for the response */
337             ProbeForWrite(ResponseString, MaximumResponseLength, 1);
338             SafeResponseString = SafeResponseBuffer;
339         }
340         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
341         {
342             /* Bad string pointer, bail out */
343             _SEH2_YIELD(return 0);
344         }
345         _SEH2_END;
346     }
347     else
348     {
349         SafeResponseString = ResponseString;
350     }
351 
352     /* Setup the prompt and response buffers */
353     PromptBuffer.Buffer = PromptString;
354     PromptBuffer.Length = PromptBuffer.MaximumLength = PromptLength;
355     ResponseBuffer.Buffer = SafeResponseString;
356     ResponseBuffer.Length = 0;
357     ResponseBuffer.MaximumLength = MaximumResponseLength;
358 
359     /* Log the print */
360     KdLogDbgPrint(&PromptBuffer);
361 
362     /* Enter the debugger */
363     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
364 
365     /* Enter prompt loop */
366     do
367     {
368         /* Send the prompt and receive the response */
369         Resend = KdpPromptString(&PromptBuffer, &ResponseBuffer);
370 
371     /* Loop while we need to resend */
372     } while (Resend);
373 
374     /* Exit the debugger */
375     KdExitDebugger(Enable);
376 
377     /* Copy back the response if required */
378     if (PreviousMode != KernelMode)
379     {
380         _SEH2_TRY
381         {
382             /* Safely copy back the response to user mode */
383             KdpMoveMemory(ResponseString,
384                           ResponseBuffer.Buffer,
385                           ResponseBuffer.Length);
386         }
387         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
388         {
389             /* String became invalid after we exited, fail */
390             _SEH2_YIELD(return 0);
391         }
392         _SEH2_END;
393     }
394 
395     /* Return the number of characters received */
396     return ResponseBuffer.Length;
397 }
398 
399 static
400 NTSTATUS
401 NTAPI
402 KdpPrintFromUser(
403     _In_ ULONG ComponentId,
404     _In_ ULONG Level,
405     _In_reads_bytes_(Length) PCHAR String,
406     _In_ USHORT Length,
407     _In_ KPROCESSOR_MODE PreviousMode,
408     _In_ PKTRAP_FRAME TrapFrame,
409     _In_ PKEXCEPTION_FRAME ExceptionFrame,
410     _Out_ PBOOLEAN Handled)
411 {
412     CHAR CapturedString[KD_PRINT_MAX_BYTES];
413 
414     ASSERT(PreviousMode == UserMode);
415     ASSERT(Length <= sizeof(CapturedString));
416 
417     /* Capture user-mode buffers */
418     _SEH2_TRY
419     {
420         /* Probe and capture the string */
421         ProbeForRead(String, Length, 1);
422         KdpMoveMemory(CapturedString, String, Length);
423         String = CapturedString;
424     }
425     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
426     {
427         /* Bad string pointer, bail out */
428         _SEH2_YIELD(return STATUS_ACCESS_VIOLATION);
429     }
430     _SEH2_END;
431 
432     /* Now go through the kernel-mode code path */
433     return KdpPrint(ComponentId,
434                     Level,
435                     String,
436                     Length,
437                     KernelMode,
438                     TrapFrame,
439                     ExceptionFrame,
440                     Handled);
441 }
442 
443 NTSTATUS
444 NTAPI
445 KdpPrint(
446     _In_ ULONG ComponentId,
447     _In_ ULONG Level,
448     _In_reads_bytes_(Length) PCHAR String,
449     _In_ USHORT Length,
450     _In_ KPROCESSOR_MODE PreviousMode,
451     _In_ PKTRAP_FRAME TrapFrame,
452     _In_ PKEXCEPTION_FRAME ExceptionFrame,
453     _Out_ PBOOLEAN Handled)
454 {
455     NTSTATUS Status;
456     BOOLEAN Enable;
457     STRING OutputString;
458 
459     if (NtQueryDebugFilterState(ComponentId, Level) == (NTSTATUS)FALSE)
460     {
461         /* Mask validation failed */
462         *Handled = TRUE;
463         return STATUS_SUCCESS;
464     }
465 
466     /* Assume failure */
467     *Handled = FALSE;
468 
469     /* Normalize the length */
470     Length = min(Length, KD_PRINT_MAX_BYTES);
471 
472     /* Check if we need to verify the string */
473     if (PreviousMode != KernelMode)
474     {
475         /* This case requires a 512 byte stack buffer.
476          * We don't want to use that much stack in the kernel case, but we
477          * can't use _alloca due to PSEH. So the buffer exists in this
478          * helper function instead.
479          */
480         return KdpPrintFromUser(ComponentId,
481                                 Level,
482                                 String,
483                                 Length,
484                                 PreviousMode,
485                                 TrapFrame,
486                                 ExceptionFrame,
487                                 Handled);
488     }
489 
490     /* Setup the output string */
491     OutputString.Buffer = String;
492     OutputString.Length = OutputString.MaximumLength = Length;
493 
494     /* Log the print */
495     KdLogDbgPrint(&OutputString);
496 
497     /* Check for a debugger */
498     if (KdDebuggerNotPresent)
499     {
500         /* Fail */
501         *Handled = TRUE;
502         return STATUS_DEVICE_NOT_CONNECTED;
503     }
504 
505     /* Enter the debugger */
506     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
507 
508     /* Print the string */
509     if (KdpPrintString(&OutputString))
510     {
511         /* User pressed CTRL-C, breakpoint on return */
512         Status = STATUS_BREAKPOINT;
513     }
514     else
515     {
516         /* String was printed */
517         Status = STATUS_SUCCESS;
518     }
519 
520     /* Exit the debugger and return */
521     KdExitDebugger(Enable);
522     *Handled = TRUE;
523     return Status;
524 }
525 
526 VOID
527 __cdecl
528 KdpDprintf(
529     _In_ PCSTR Format,
530     ...)
531 {
532     STRING String;
533     USHORT Length;
534     va_list ap;
535     CHAR Buffer[512];
536 
537     /* Format the string */
538     va_start(ap, Format);
539     Length = (USHORT)_vsnprintf(Buffer,
540                                 sizeof(Buffer),
541                                 Format,
542                                 ap);
543     va_end(ap);
544 
545     /* Set it up */
546     String.Buffer = Buffer;
547     String.Length = String.MaximumLength = Length;
548 
549     /* Send it to the debugger directly */
550     KdpPrintString(&String);
551 }
552