xref: /reactos/ntoskrnl/kd64/kdtrap.c (revision 845faec4)
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 breakpoints 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                                         (LPSTR)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,
178                                            ReturnStatus);
179                 break;
180 
181             /* DbgPrompt */
182             case BREAKPOINT_PROMPT:
183 
184                 /* Call the worker routine */
185                 ReturnLength = KdpPrompt((LPSTR)ExceptionRecord->ExceptionInformation[1],
186                                          (USHORT)ExceptionRecord->ExceptionInformation[2],
187                                          (LPSTR)KdpGetParameterThree(ContextRecord),
188                                          (USHORT)KdpGetParameterFour(ContextRecord),
189                                          PreviousMode,
190                                          TrapFrame,
191                                          ExceptionFrame);
192                 Handled = TRUE;
193 
194                 /* Update the return value for the caller */
195                 KeSetContextReturnRegister(ContextRecord, ReturnLength);
196                 break;
197 
198             /* DbgUnLoadImageSymbols */
199             case BREAKPOINT_UNLOAD_SYMBOLS:
200 
201                 /* Drop into the load case below, with the unload parameter */
202                 Unload = TRUE;
203 
204             /* DbgLoadImageSymbols */
205             case BREAKPOINT_LOAD_SYMBOLS:
206 
207                 /* Call the worker routine */
208                 KdpSymbol((PSTRING)ExceptionRecord->
209                           ExceptionInformation[1],
210                           (PKD_SYMBOLS_INFO)ExceptionRecord->
211                           ExceptionInformation[2],
212                           Unload,
213                           PreviousMode,
214                           ContextRecord,
215                           TrapFrame,
216                           ExceptionFrame);
217                 Handled = TRUE;
218                 break;
219 
220             /* DbgCommandString */
221             case BREAKPOINT_COMMAND_STRING:
222 
223                 /* Call the worker routine */
224                 KdpCommandString((PSTRING)ExceptionRecord->
225                                  ExceptionInformation[1],
226                                  (PSTRING)ExceptionRecord->
227                                  ExceptionInformation[2],
228                                  PreviousMode,
229                                  ContextRecord,
230                                  TrapFrame,
231                                  ExceptionFrame);
232                 Handled = TRUE;
233                 break;
234 
235             /* Anything else, do nothing */
236             default:
237 
238                 /* Invalid debug service! Don't handle this! */
239                 Handled = FALSE;
240                 break;
241         }
242 
243         /*
244          * If the PC was not updated, we'll increment it ourselves so execution
245          * continues past the breakpoint.
246          */
247         if (ProgramCounter == KeGetContextPc(ContextRecord))
248         {
249             /* Update it */
250             KeSetContextPc(ContextRecord,
251                            ProgramCounter + KD_BREAKPOINT_SIZE);
252         }
253     }
254     else
255     {
256         /* Call the worker routine */
257         Handled = KdpReport(TrapFrame,
258                             ExceptionFrame,
259                             ExceptionRecord,
260                             ContextRecord,
261                             PreviousMode,
262                             SecondChanceException);
263     }
264 
265     /* Return TRUE or FALSE to caller */
266     return Handled;
267 }
268 
269 BOOLEAN
270 NTAPI
271 KdpStub(IN PKTRAP_FRAME TrapFrame,
272         IN PKEXCEPTION_FRAME ExceptionFrame,
273         IN PEXCEPTION_RECORD ExceptionRecord,
274         IN PCONTEXT ContextRecord,
275         IN KPROCESSOR_MODE PreviousMode,
276         IN BOOLEAN SecondChanceException)
277 {
278     ULONG_PTR ExceptionCommand;
279 
280     /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
281     ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
282     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
283         (ExceptionRecord->NumberParameters > 0) &&
284         ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
285          (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
286          (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
287          (ExceptionCommand == BREAKPOINT_PRINT)))
288     {
289         /* This we can handle: simply bump the Program Counter */
290         KeSetContextPc(ContextRecord,
291                        KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
292         return TRUE;
293     }
294     else if (KdPitchDebugger)
295     {
296         /* There's no debugger, fail. */
297         return FALSE;
298     }
299     else if ((KdAutoEnableOnEvent) &&
300              (KdPreviouslyEnabled) &&
301              !(KdDebuggerEnabled) &&
302              (NT_SUCCESS(KdEnableDebugger())) &&
303              (KdDebuggerEnabled))
304     {
305         /* Debugging was Auto-Enabled. We can now send this to KD. */
306         return KdpTrap(TrapFrame,
307                        ExceptionFrame,
308                        ExceptionRecord,
309                        ContextRecord,
310                        PreviousMode,
311                        SecondChanceException);
312     }
313     else
314     {
315         /* FIXME: All we can do in this case is trace this exception */
316         return FALSE;
317     }
318 }
319 
320 BOOLEAN
321 NTAPI
322 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
323                 IN PCONTEXT Context,
324                 IN KPROCESSOR_MODE PreviousMode)
325 {
326     /*
327      * Determine if this is a valid debug service call and make sure that
328      * it isn't a software breakpoint
329      */
330     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
331         (ExceptionRecord->NumberParameters > 0) &&
332         (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
333     {
334         /* Then we have to handle it */
335         return TRUE;
336     }
337     else
338     {
339         /* We don't have to handle it */
340         return FALSE;
341     }
342 }
343