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
KdpReport(IN PKTRAP_FRAME TrapFrame,IN PKEXCEPTION_FRAME ExceptionFrame,IN PEXCEPTION_RECORD ExceptionRecord,IN PCONTEXT ContextRecord,IN KPROCESSOR_MODE PreviousMode,IN BOOLEAN SecondChanceException)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
KdpTrap(IN PKTRAP_FRAME TrapFrame,IN PKEXCEPTION_FRAME ExceptionFrame,IN PEXCEPTION_RECORD ExceptionRecord,IN PCONTEXT ContextRecord,IN KPROCESSOR_MODE PreviousMode,IN BOOLEAN SecondChanceException)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
KdpStub(IN PKTRAP_FRAME TrapFrame,IN PKEXCEPTION_FRAME ExceptionFrame,IN PEXCEPTION_RECORD ExceptionRecord,IN PCONTEXT ContextRecord,IN KPROCESSOR_MODE PreviousMode,IN BOOLEAN SecondChanceException)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
KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,IN PCONTEXT Context,IN KPROCESSOR_MODE PreviousMode)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