Sync with trunk (r48545)
[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
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 ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
279
280 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
281 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
282 (ExceptionRecord->NumberParameters > 0) &&
283 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
284 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
285 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
286 (ExceptionCommand == BREAKPOINT_PRINT)))
287 {
288 /* This we can handle: simply bump the Program Counter */
289 KeSetContextPc(ContextRecord,
290 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
291 return TRUE;
292 }
293 else if (KdPitchDebugger)
294 {
295 /* There's no debugger, fail. */
296 return FALSE;
297 }
298 else if ((KdAutoEnableOnEvent) &&
299 (KdPreviouslyEnabled) &&
300 !(KdDebuggerEnabled) &&
301 (NT_SUCCESS(KdEnableDebugger())) &&
302 (KdDebuggerEnabled))
303 {
304 /* Debugging was Auto-Enabled. We can now send this to KD. */
305 return KdpTrap(TrapFrame,
306 ExceptionFrame,
307 ExceptionRecord,
308 ContextRecord,
309 PreviousMode,
310 SecondChanceException);
311 }
312 else
313 {
314 /* FIXME: All we can do in this case is trace this exception */
315 return FALSE;
316 }
317 }
318
319 BOOLEAN
320 NTAPI
321 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
322 IN PCONTEXT Context,
323 IN KPROCESSOR_MODE PreviousMode)
324 {
325 /*
326 * Determine if this is a valid debug service call and make sure that
327 * it isn't a software breakpoint
328 */
329 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
330 (ExceptionRecord->NumberParameters > 0) &&
331 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
332 {
333 /* Then we have to handle it */
334 return TRUE;
335 }
336 else
337 {
338 /* We don't have to handle it */
339 return FALSE;
340 }
341 }