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
KdpAcquireLock(_In_ PKSPIN_LOCK SpinLock)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
KdpReleaseLock(_In_ PKSPIN_LOCK SpinLock,_In_ KIRQL OldIrql)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
KdLogDbgPrint(_In_ PSTRING String)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
KdpPrintString(_In_ PSTRING Output)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
KdpPromptString(_In_ PSTRING PromptString,_In_ PSTRING ResponseString)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
KdpCommandString(IN PSTRING NameString,IN PSTRING CommandString,IN KPROCESSOR_MODE PreviousMode,IN PCONTEXT ContextRecord,IN PKTRAP_FRAME TrapFrame,IN PKEXCEPTION_FRAME ExceptionFrame)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
KdpSymbol(IN PSTRING DllPath,IN PKD_SYMBOLS_INFO SymbolInfo,IN BOOLEAN Unload,IN KPROCESSOR_MODE PreviousMode,IN PCONTEXT ContextRecord,IN PKTRAP_FRAME TrapFrame,IN PKEXCEPTION_FRAME ExceptionFrame)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
KdpPrompt(_In_reads_bytes_ (PromptLength)PCHAR PromptString,_In_ USHORT PromptLength,_Out_writes_bytes_ (MaximumResponseLength)PCHAR ResponseString,_In_ USHORT MaximumResponseLength,_In_ KPROCESSOR_MODE PreviousMode,_In_ PKTRAP_FRAME TrapFrame,_In_ PKEXCEPTION_FRAME ExceptionFrame)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
KdpPrintFromUser(_In_ ULONG ComponentId,_In_ ULONG Level,_In_reads_bytes_ (Length)PCHAR String,_In_ USHORT Length,_In_ KPROCESSOR_MODE PreviousMode,_In_ PKTRAP_FRAME TrapFrame,_In_ PKEXCEPTION_FRAME ExceptionFrame,_Out_ PBOOLEAN Handled)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
KdpPrint(_In_ ULONG ComponentId,_In_ ULONG Level,_In_reads_bytes_ (Length)PCHAR String,_In_ USHORT Length,_In_ KPROCESSOR_MODE PreviousMode,_In_ PKTRAP_FRAME TrapFrame,_In_ PKEXCEPTION_FRAME ExceptionFrame,_Out_ PBOOLEAN Handled)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
KdpDprintf(_In_ PCSTR Format,...)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