xref: /reactos/ntoskrnl/kd64/kdtrap.c (revision b3a5eeb1)
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     KIRQL OldIrql = DISPATCH_LEVEL;
148 
149     /* Raise if we have to. */
150     if (KeGetCurrentIrql() < DISPATCH_LEVEL)
151         OldIrql = KeRaiseIrqlToDpcLevel();
152 
153     /*
154      * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or
155      * Load/Unload symbols. Make sure it isn't a software breakpoints as those
156      * are handled by KdpReport.
157      */
158     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
159         (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
160     {
161         /* Save Program Counter */
162         ProgramCounter = KeGetContextPc(ContextRecord);
163 
164         /* Check what kind of operation was requested from us */
165         Unload = FALSE;
166         switch (ExceptionRecord->ExceptionInformation[0])
167         {
168             /* DbgPrint */
169             case BREAKPOINT_PRINT:
170 
171                 /* Call the worker routine */
172                 ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord),
173                                         (ULONG)KdpGetParameterFour(ContextRecord),
174                                         (PCHAR)ExceptionRecord->ExceptionInformation[1],
175                                         (USHORT)ExceptionRecord->ExceptionInformation[2],
176                                         PreviousMode,
177                                         TrapFrame,
178                                         ExceptionFrame,
179                                         &Handled);
180 
181                 /* Update the return value for the caller */
182                 KeSetContextReturnRegister(ContextRecord, ReturnStatus);
183                 break;
184 
185             /* DbgPrompt */
186             case BREAKPOINT_PROMPT:
187 
188                 /* Call the worker routine */
189                 ReturnLength = KdpPrompt((PCHAR)ExceptionRecord->ExceptionInformation[1],
190                                          (USHORT)ExceptionRecord->ExceptionInformation[2],
191                                          (PCHAR)KdpGetParameterThree(ContextRecord),
192                                          (USHORT)KdpGetParameterFour(ContextRecord),
193                                          PreviousMode,
194                                          TrapFrame,
195                                          ExceptionFrame);
196                 Handled = TRUE;
197 
198                 /* Update the return value for the caller */
199                 KeSetContextReturnRegister(ContextRecord, ReturnLength);
200                 break;
201 
202             /* DbgUnLoadImageSymbols */
203             case BREAKPOINT_UNLOAD_SYMBOLS:
204 
205                 /* Drop into the load case below, with the unload parameter */
206                 Unload = TRUE;
207 
208             /* DbgLoadImageSymbols */
209             case BREAKPOINT_LOAD_SYMBOLS:
210 
211                 /* Call the worker routine */
212                 KdpSymbol((PSTRING)ExceptionRecord->ExceptionInformation[1],
213                           (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2],
214                           Unload,
215                           PreviousMode,
216                           ContextRecord,
217                           TrapFrame,
218                           ExceptionFrame);
219                 Handled = TRUE;
220                 break;
221 
222             /* DbgCommandString */
223             case BREAKPOINT_COMMAND_STRING:
224 
225                 /* Call the worker routine */
226                 KdpCommandString((PSTRING)ExceptionRecord->ExceptionInformation[1],
227                                  (PSTRING)ExceptionRecord->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     if (OldIrql < DISPATCH_LEVEL)
266         KeLowerIrql(OldIrql);
267 
268     /* Return TRUE or FALSE to caller */
269     return Handled;
270 }
271 
272 BOOLEAN
273 NTAPI
274 KdpStub(IN PKTRAP_FRAME TrapFrame,
275         IN PKEXCEPTION_FRAME ExceptionFrame,
276         IN PEXCEPTION_RECORD ExceptionRecord,
277         IN PCONTEXT ContextRecord,
278         IN KPROCESSOR_MODE PreviousMode,
279         IN BOOLEAN SecondChanceException)
280 {
281     ULONG_PTR ExceptionCommand;
282 
283     /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
284     ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
285     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
286         (ExceptionRecord->NumberParameters > 0) &&
287         ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
288          (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
289          (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
290          (ExceptionCommand == BREAKPOINT_PRINT)))
291     {
292         /* This we can handle: simply bump the Program Counter */
293         KeSetContextPc(ContextRecord,
294                        KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
295         return TRUE;
296     }
297     else if (KdPitchDebugger)
298     {
299         /* There's no debugger, fail. */
300         return FALSE;
301     }
302     else if ((KdAutoEnableOnEvent) &&
303              (KdPreviouslyEnabled) &&
304              !(KdDebuggerEnabled) &&
305              (NT_SUCCESS(KdEnableDebugger())) &&
306              (KdDebuggerEnabled))
307     {
308         /* Debugging was Auto-Enabled. We can now send this to KD. */
309         return KdpTrap(TrapFrame,
310                        ExceptionFrame,
311                        ExceptionRecord,
312                        ContextRecord,
313                        PreviousMode,
314                        SecondChanceException);
315     }
316     else
317     {
318         /* FIXME: All we can do in this case is trace this exception */
319         return FALSE;
320     }
321 }
322 
323 BOOLEAN
324 NTAPI
325 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
326                 IN PCONTEXT Context,
327                 IN KPROCESSOR_MODE PreviousMode)
328 {
329 #ifdef _WINKD_
330     /*
331      * Determine if this is a valid debug service call and make sure that
332      * it isn't a software breakpoint
333      */
334     if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
335         (ExceptionRecord->NumberParameters > 0) &&
336         (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
337     {
338         /* Then we have to handle it */
339         return TRUE;
340     }
341     else
342     {
343         /* We don't have to handle it */
344         return FALSE;
345     }
346 #else
347     /* KDBG has its own mechanism for ignoring user mode exceptions */
348     return FALSE;
349 #endif
350 }
351