* Sync up to trunk head (r64377).
[reactos.git] / ntoskrnl / kd64 / kdtrap.c
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 = ExceptionRecord->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 if ((ExceptionCode == STATUS_BREAKPOINT) ||
69 (ExceptionCode == STATUS_SINGLE_STEP) ||
70 (ExceptionCode == STATUS_ASSERTION_FAILURE))
71 {
72 /* This is a debug exception; we always pass them to the debugger */
73 }
74 else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION)
75 {
76 /*
77 * Not a debug exception, but the stop-on-exception flag is set,
78 * meaning the debugger requests that we pass it first chance
79 * exceptions. However, some exceptions are always passed to the
80 * exception handler first, namely exceptions with a code that isn't
81 * an error or warning code, and also exceptions with the special
82 * STATUS_PORT_DISCONNECTED code (an error code).
83 */
84 if ((SecondChanceException == FALSE) &&
85 ((ExceptionCode == STATUS_PORT_DISCONNECTED) ||
86 (NT_SUCCESS(ExceptionCode))))
87 {
88 /* Let the exception handler, if any, try to handle it */
89 return FALSE;
90 }
91 }
92 else if (SecondChanceException == FALSE)
93 {
94 /*
95 * This isn't a debug exception and the stop-on-exception flag isn't
96 * set, so don't bother
97 */
98 return FALSE;
99 }
100
101 /* Enter the debugger */
102 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
103
104 /*
105 * Get the KPRCB and save the CPU Control State manually instead of
106 * using KiSaveProcessorState, since we already have a valid CONTEXT.
107 */
108 Prcb = KeGetCurrentPrcb();
109 KiSaveProcessorControlState(&Prcb->ProcessorState);
110 RtlCopyMemory(&Prcb->ProcessorState.ContextFrame,
111 ContextRecord,
112 sizeof(CONTEXT));
113
114 /* Report the new state */
115 Handled = KdpReportExceptionStateChange(ExceptionRecord,
116 &Prcb->ProcessorState.
117 ContextFrame,
118 SecondChanceException);
119
120 /* Now restore the processor state, manually again. */
121 RtlCopyMemory(ContextRecord,
122 &Prcb->ProcessorState.ContextFrame,
123 sizeof(CONTEXT));
124 KiRestoreProcessorControlState(&Prcb->ProcessorState);
125
126 /* Exit the debugger and clear the CTRL-C state */
127 KdExitDebugger(Enable);
128 KdpControlCPressed = FALSE;
129 return Handled;
130 }
131
132 BOOLEAN
133 NTAPI
134 KdpTrap(IN PKTRAP_FRAME TrapFrame,
135 IN PKEXCEPTION_FRAME ExceptionFrame,
136 IN PEXCEPTION_RECORD ExceptionRecord,
137 IN PCONTEXT ContextRecord,
138 IN KPROCESSOR_MODE PreviousMode,
139 IN BOOLEAN SecondChanceException)
140 {
141 BOOLEAN Unload = FALSE;
142 ULONG_PTR ProgramCounter;
143 BOOLEAN Handled;
144 NTSTATUS ReturnStatus;
145 USHORT ReturnLength;
146
147 /*
148 * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or
149 * Load/Unload symbols. Make sure it isn't a software breakpoints as those
150 * are handled by KdpReport.
151 */
152 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
153 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
154 {
155 /* Save Program Counter */
156 ProgramCounter = KeGetContextPc(ContextRecord);
157
158 /* Check what kind of operation was requested from us */
159 switch (ExceptionRecord->ExceptionInformation[0])
160 {
161 /* DbgPrint */
162 case BREAKPOINT_PRINT:
163
164 /* Call the worker routine */
165 ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord),
166 (ULONG)KdpGetParameterFour(ContextRecord),
167 (LPSTR)ExceptionRecord->
168 ExceptionInformation[1],
169 (USHORT)ExceptionRecord->
170 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((LPSTR)ExceptionRecord->
185 ExceptionInformation[1],
186 (USHORT)ExceptionRecord->
187 ExceptionInformation[2],
188 (LPSTR)KdpGetParameterThree(ContextRecord),
189 (USHORT)KdpGetParameterFour(ContextRecord),
190 PreviousMode,
191 TrapFrame,
192 ExceptionFrame);
193 Handled = TRUE;
194
195 /* Update the return value for the caller */
196 KeSetContextReturnRegister(ContextRecord, ReturnLength);
197 break;
198
199 /* DbgUnLoadImageSymbols */
200 case BREAKPOINT_UNLOAD_SYMBOLS:
201
202 /* Drop into the load case below, with the unload parameter */
203 Unload = TRUE;
204
205 /* DbgLoadImageSymbols */
206 case BREAKPOINT_LOAD_SYMBOLS:
207
208 /* Call the worker routine */
209 KdpSymbol((PSTRING)ExceptionRecord->
210 ExceptionInformation[1],
211 (PKD_SYMBOLS_INFO)ExceptionRecord->
212 ExceptionInformation[2],
213 Unload,
214 PreviousMode,
215 ContextRecord,
216 TrapFrame,
217 ExceptionFrame);
218 Handled = TRUE;
219 break;
220
221 /* DbgCommandString */
222 case BREAKPOINT_COMMAND_STRING:
223
224 /* Call the worker routine */
225 KdpCommandString((PSTRING)ExceptionRecord->
226 ExceptionInformation[1],
227 (PSTRING)ExceptionRecord->
228 ExceptionInformation[2],
229 PreviousMode,
230 ContextRecord,
231 TrapFrame,
232 ExceptionFrame);
233 Handled = TRUE;
234 break;
235
236 /* Anything else, do nothing */
237 default:
238
239 /* Invalid debug service! Don't handle this! */
240 Handled = FALSE;
241 break;
242 }
243
244 /*
245 * If the PC was not updated, we'll increment it ourselves so execution
246 * continues past the breakpoint.
247 */
248 if (ProgramCounter == KeGetContextPc(ContextRecord))
249 {
250 /* Update it */
251 KeSetContextPc(ContextRecord,
252 ProgramCounter + KD_BREAKPOINT_SIZE);
253 }
254 }
255 else
256 {
257 /* Call the worker routine */
258 Handled = KdpReport(TrapFrame,
259 ExceptionFrame,
260 ExceptionRecord,
261 ContextRecord,
262 PreviousMode,
263 SecondChanceException);
264 }
265
266 /* Return TRUE or FALSE to caller */
267 return Handled;
268 }
269
270 BOOLEAN
271 NTAPI
272 KdpStub(IN PKTRAP_FRAME TrapFrame,
273 IN PKEXCEPTION_FRAME ExceptionFrame,
274 IN PEXCEPTION_RECORD ExceptionRecord,
275 IN PCONTEXT ContextRecord,
276 IN KPROCESSOR_MODE PreviousMode,
277 IN BOOLEAN SecondChanceException)
278 {
279 ULONG_PTR ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
280
281 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
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 }