xref: /reactos/ntoskrnl/kd64/kdtrap.c (revision b707be90)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/kd64/kdtrap.c
5  * PURPOSE:         KD64 Trap Handlers
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 //
17 // Retrieves the ComponentId and Level for BREAKPOINT_PRINT
18 // and OutputString and OutputStringLength for BREAKPOINT_PROMPT.
19 //
20 #if defined(_X86_)
21 
22 //
23 // EBX/EDI on x86
24 //
25 #define KdpGetParameterThree(Context)  ((Context)->Ebx)
26 #define KdpGetParameterFour(Context)   ((Context)->Edi)
27 
28 #elif defined(_AMD64_)
29 
30 //
31 // R8/R9 on AMD64
32 //
33 #define KdpGetParameterThree(Context)  ((Context)->R8)
34 #define KdpGetParameterFour(Context)   ((Context)->R9)
35 
36 #elif defined(_ARM_)
37 
38 //
39 // R3/R4 on ARM
40 //
41 #define KdpGetParameterThree(Context)  ((Context)->R3)
42 #define KdpGetParameterFour(Context)   ((Context)->R4)
43 
44 #else
45 #error Unsupported Architecture
46 #endif
47 
48 /* FUNCTIONS *****************************************************************/
49 
50 BOOLEAN
51 NTAPI
52 KdpReport(IN PKTRAP_FRAME TrapFrame,
53           IN PKEXCEPTION_FRAME ExceptionFrame,
54           IN PEXCEPTION_RECORD ExceptionRecord,
55           IN PCONTEXT ContextRecord,
56           IN KPROCESSOR_MODE PreviousMode,
57           IN BOOLEAN SecondChanceException)
58 {
59     BOOLEAN Enable, Handled;
60     PKPRCB Prcb;
61     NTSTATUS ExceptionCode;
62 
63     /*
64      * Determine whether to pass the exception to the debugger.
65      * First, check if this is a "debug exception", meaning breakpoint
66      * (including debug service), single step and assertion failure exceptions.
67      */
68     ExceptionCode = ExceptionRecord->ExceptionCode;
69     if ((ExceptionCode == STATUS_BREAKPOINT) ||
70         (ExceptionCode == STATUS_SINGLE_STEP) ||
71         (ExceptionCode == STATUS_ASSERTION_FAILURE))
72     {
73         /* This is a debug exception; we always pass them to the debugger */
74     }
75     else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION)
76     {
77         /*
78          * Not a debug exception, but the stop-on-exception flag is set,
79          * meaning the debugger requests that we pass it first chance
80          * exceptions. However, some exceptions are always passed to the
81          * exception handler first, namely exceptions with a code that isn't
82          * an error or warning code, and also exceptions with the special
83          * STATUS_PORT_DISCONNECTED code (an error code).
84          */
85         if ((SecondChanceException == FALSE) &&
86             ((ExceptionCode == STATUS_PORT_DISCONNECTED) ||
87              (NT_SUCCESS(ExceptionCode))))
88         {
89             /* Let the exception handler, if any, try to handle it */
90             return FALSE;
91         }
92     }
93     else if (SecondChanceException == FALSE)
94     {
95         /*
96          * This isn't a debug exception and the stop-on-exception flag isn't set,
97          * so don't bother handling it
98          */
99         return FALSE;
100     }
101 
102     /* Enter the debugger */
103     Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
104 
105     /*
106      * Get the KPRCB and save the CPU Control State manually instead of
107      * using KiSaveProcessorState, since we already have a valid CONTEXT.
108      */
109     Prcb = KeGetCurrentPrcb();
110     KiSaveProcessorControlState(&Prcb->ProcessorState);
111     KdpMoveMemory(&Prcb->ProcessorState.ContextFrame,
112                   ContextRecord,
113                   sizeof(CONTEXT));
114 
115     /* Report the new state */
116     Handled = KdpReportExceptionStateChange(ExceptionRecord,
117                                             &Prcb->ProcessorState.
118                                             ContextFrame,
119                                             SecondChanceException);
120 
121     /* Now restore the processor state, manually again. */
122     KdpMoveMemory(ContextRecord,
123                   &Prcb->ProcessorState.ContextFrame,
124                   sizeof(CONTEXT));
125     KiRestoreProcessorControlState(&Prcb->ProcessorState);
126 
127     /* Exit the debugger and clear the CTRL-C state */
128     KdExitDebugger(Enable);
129     KdpControlCPressed = FALSE;
130     return Handled;
131 }
132 
133 BOOLEAN
134 NTAPI
135 KdpTrap(IN PKTRAP_FRAME TrapFrame,
136         IN PKEXCEPTION_FRAME ExceptionFrame,
137         IN PEXCEPTION_RECORD ExceptionRecord,
138         IN PCONTEXT ContextRecord,
139         IN KPROCESSOR_MODE PreviousMode,
140         IN BOOLEAN SecondChanceException)
141 {
142     BOOLEAN Unload;
143     ULONG_PTR ProgramCounter;
144     BOOLEAN Handled;
145     NTSTATUS ReturnStatus;
146     USHORT ReturnLength;
147 
148     /*
149      * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or
150      * Load/Unload symbols. Make sure it isn't a software breakpoint as those
151      * are handled by KdpReport.
152      */
153     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
154         (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
155     {
156         /* Save Program Counter */
157         ProgramCounter = KeGetContextPc(ContextRecord);
158 
159         /* Check what kind of operation was requested from us */
160         Unload = FALSE;
161         switch (ExceptionRecord->ExceptionInformation[0])
162         {
163             /* DbgPrint */
164             case BREAKPOINT_PRINT:
165 
166                 /* Call the worker routine */
167                 ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord),
168                                         (ULONG)KdpGetParameterFour(ContextRecord),
169                                         (PCHAR)ExceptionRecord->ExceptionInformation[1],
170                                         (USHORT)ExceptionRecord->ExceptionInformation[2],
171                                         PreviousMode,
172                                         TrapFrame,
173                                         ExceptionFrame,
174                                         &Handled);
175 
176                 /* Update the return value for the caller */
177                 KeSetContextReturnRegister(ContextRecord, ReturnStatus);
178                 break;
179 
180             /* DbgPrompt */
181             case BREAKPOINT_PROMPT:
182 
183                 /* Call the worker routine */
184                 ReturnLength = KdpPrompt((PCHAR)ExceptionRecord->ExceptionInformation[1],
185                                          (USHORT)ExceptionRecord->ExceptionInformation[2],
186                                          (PCHAR)KdpGetParameterThree(ContextRecord),
187                                          (USHORT)KdpGetParameterFour(ContextRecord),
188                                          PreviousMode,
189                                          TrapFrame,
190                                          ExceptionFrame);
191                 Handled = TRUE;
192 
193                 /* Update the return value for the caller */
194                 KeSetContextReturnRegister(ContextRecord, ReturnLength);
195                 break;
196 
197             /* DbgUnLoadImageSymbols */
198             case BREAKPOINT_UNLOAD_SYMBOLS:
199 
200                 /* Drop into the load case below, with the unload parameter */
201                 Unload = TRUE;
202 
203             /* DbgLoadImageSymbols */
204             case BREAKPOINT_LOAD_SYMBOLS:
205 
206                 /* Call the worker routine */
207                 KdpSymbol((PSTRING)ExceptionRecord->ExceptionInformation[1],
208                           (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2],
209                           Unload,
210                           PreviousMode,
211                           ContextRecord,
212                           TrapFrame,
213                           ExceptionFrame);
214                 Handled = TRUE;
215                 break;
216 
217             /* DbgCommandString */
218             case BREAKPOINT_COMMAND_STRING:
219 
220                 /* Call the worker routine */
221                 KdpCommandString((PSTRING)ExceptionRecord->ExceptionInformation[1],
222                                  (PSTRING)ExceptionRecord->ExceptionInformation[2],
223                                  PreviousMode,
224                                  ContextRecord,
225                                  TrapFrame,
226                                  ExceptionFrame);
227                 Handled = TRUE;
228                 break;
229 
230             /* Anything else, do nothing */
231             default:
232 
233                 /* Invalid debug service! Don't handle this! */
234                 Handled = FALSE;
235                 break;
236         }
237 
238         /*
239          * If the PC was not updated, we'll increment it ourselves so execution
240          * continues past the breakpoint.
241          */
242         if (ProgramCounter == KeGetContextPc(ContextRecord))
243         {
244             /* Update it */
245             KeSetContextPc(ContextRecord,
246                            ProgramCounter + KD_BREAKPOINT_SIZE);
247         }
248     }
249     else
250     {
251         /* Call the worker routine */
252         Handled = KdpReport(TrapFrame,
253                             ExceptionFrame,
254                             ExceptionRecord,
255                             ContextRecord,
256                             PreviousMode,
257                             SecondChanceException);
258     }
259 
260     /* Return TRUE or FALSE to caller */
261     return Handled;
262 }
263 
264 BOOLEAN
265 NTAPI
266 KdpStub(IN PKTRAP_FRAME TrapFrame,
267         IN PKEXCEPTION_FRAME ExceptionFrame,
268         IN PEXCEPTION_RECORD ExceptionRecord,
269         IN PCONTEXT ContextRecord,
270         IN KPROCESSOR_MODE PreviousMode,
271         IN BOOLEAN SecondChanceException)
272 {
273     ULONG_PTR ExceptionCommand;
274 
275     /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
276     ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
277     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
278         (ExceptionRecord->NumberParameters > 0) &&
279         ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
280          (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
281          (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
282          (ExceptionCommand == BREAKPOINT_PRINT)))
283     {
284         /* This we can handle: simply bump the Program Counter */
285         KeSetContextPc(ContextRecord,
286                        KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
287         return TRUE;
288     }
289     else if (KdPitchDebugger)
290     {
291         /* There's no debugger, fail. */
292         return FALSE;
293     }
294     else if ((KdAutoEnableOnEvent) &&
295              (KdPreviouslyEnabled) &&
296              !(KdDebuggerEnabled) &&
297              (NT_SUCCESS(KdEnableDebugger())) &&
298              (KdDebuggerEnabled))
299     {
300         /* Debugging was Auto-Enabled. We can now send this to KD. */
301         return KdpTrap(TrapFrame,
302                        ExceptionFrame,
303                        ExceptionRecord,
304                        ContextRecord,
305                        PreviousMode,
306                        SecondChanceException);
307     }
308     else
309     {
310         /* FIXME: All we can do in this case is trace this exception */
311         return FALSE;
312     }
313 }
314 
315 BOOLEAN
316 NTAPI
317 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
318                 IN PCONTEXT Context,
319                 IN KPROCESSOR_MODE PreviousMode)
320 {
321 #ifdef _WINKD_
322     /*
323      * Determine if this is a valid debug service call and make sure that
324      * it isn't a software breakpoint
325      */
326     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
327         (ExceptionRecord->NumberParameters > 0) &&
328         (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
329     {
330         /* Then we have to handle it */
331         return TRUE;
332     }
333     else
334     {
335         /* We don't have to handle it */
336         return FALSE;
337     }
338 #else
339     /* KDBG has its own mechanism for ignoring user mode exceptions */
340     return FALSE;
341 #endif
342 }
343