aed14beba55a0ab37610a65c644901149c5706ae
[reactos.git] / reactos / 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(_M_IX86)
21
22 //
23 // EBX/EDI on x86
24 //
25 #define KdpGetFirstParameter(Context) ((Context)->Ebx)
26 #define KdpGetSecondParameter(Context) ((Context)->Edi)
27
28 #elif defined(_M_AMD64)
29
30 //
31 // R8/R9 on AMD64
32 //
33 #define KdpGetFirstParameter(Context) ((Context)->R8)
34 #define KdpGetSecondParameter(Context) ((Context)->R9)
35
36 #elif defined(_M_ARM)
37
38 #error Yo Ninjas!
39
40 #else
41 #error Unsupported Architecture
42 #endif
43
44 /* FUNCTIONS *****************************************************************/
45
46 BOOLEAN
47 NTAPI
48 KdpReport(IN PKTRAP_FRAME TrapFrame,
49 IN PKEXCEPTION_FRAME ExceptionFrame,
50 IN PEXCEPTION_RECORD ExceptionRecord,
51 IN PCONTEXT ContextRecord,
52 IN KPROCESSOR_MODE PreviousMode,
53 IN BOOLEAN SecondChanceException)
54 {
55 BOOLEAN Entered, Status;
56 PKPRCB Prcb;
57 NTSTATUS ExceptionCode = ExceptionRecord->ExceptionCode;
58
59 /*
60 * Determine whether to pass the exception to the debugger.
61 * First, check if this is a "debug exception", meaning breakpoint
62 * (including debug service), single step and assertion failure exceptions.
63 */
64 if ((ExceptionCode == STATUS_BREAKPOINT) ||
65 (ExceptionCode == STATUS_SINGLE_STEP) ||
66 (ExceptionCode == STATUS_ASSERTION_FAILURE))
67 {
68 /* This is a debug exception; we always pass them to the debugger */
69 }
70 else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION)
71 {
72 /*
73 * Not a debug exception, but the stop-on-exception flag is set,
74 * meaning the debugger requests that we pass it first chance
75 * exceptions. However, some exceptions are always passed to the
76 * exception handler first, namely exceptions with a code that isn't
77 * an error or warning code, and also exceptions with the special
78 * STATUS_PORT_DISCONNECTED code (an error code).
79 */
80 if ((SecondChanceException == FALSE) &&
81 ((ExceptionCode == STATUS_PORT_DISCONNECTED) ||
82 (NT_SUCCESS(ExceptionCode))))
83 {
84 /* Let the exception handler, if any, try to handle it */
85 return FALSE;
86 }
87 }
88 else if (SecondChanceException == FALSE)
89 {
90 /*
91 * This isn't a debug exception and the stop-on-exception flag isn't
92 * set, so don't bother
93 */
94 return FALSE;
95 }
96
97 /* Enter the debugger */
98 Entered = KdEnterDebugger(TrapFrame, ExceptionFrame);
99
100 /*
101 * Get the KPRCB and save the CPU Control State manually instead of
102 * using KiSaveProcessorState, since we already have a valid CONTEXT.
103 */
104 Prcb = KeGetCurrentPrcb();
105 KiSaveProcessorControlState(&Prcb->ProcessorState);
106 RtlCopyMemory(&Prcb->ProcessorState.ContextFrame,
107 ContextRecord,
108 sizeof(CONTEXT));
109
110 /* Report the new state */
111 Status = KdpReportExceptionStateChange(ExceptionRecord,
112 &Prcb->ProcessorState.
113 ContextFrame,
114 SecondChanceException);
115
116 /* Now restore the processor state, manually again. */
117 RtlCopyMemory(ContextRecord,
118 &Prcb->ProcessorState.ContextFrame,
119 sizeof(CONTEXT));
120 KiRestoreProcessorControlState(&Prcb->ProcessorState);
121
122 /* Exit the debugger and clear the CTRL-C state */
123 KdExitDebugger(Entered);
124 KdpControlCPressed = FALSE;
125 return Status;
126 }
127
128 BOOLEAN
129 NTAPI
130 KdpTrap(IN PKTRAP_FRAME TrapFrame,
131 IN PKEXCEPTION_FRAME ExceptionFrame,
132 IN PEXCEPTION_RECORD ExceptionRecord,
133 IN PCONTEXT ContextRecord,
134 IN KPROCESSOR_MODE PreviousMode,
135 IN BOOLEAN SecondChanceException)
136 {
137 BOOLEAN Unload = FALSE;
138 ULONG_PTR ProgramCounter, ReturnValue;
139 BOOLEAN Status = FALSE;
140
141 /*
142 * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or
143 * Load/Unload symbols. Make sure it isn't a software breakpoints as those
144 * are handled by KdpReport.
145 */
146 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
147 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
148 {
149 /* Save Program Counter */
150 ProgramCounter = KeGetContextPc(ContextRecord);
151
152 /* Check what kind of operation was requested from us */
153 switch (ExceptionRecord->ExceptionInformation[0])
154 {
155 /* DbgPrint */
156 case BREAKPOINT_PRINT:
157
158 /* Call the worker routine */
159 ReturnValue = KdpPrint((ULONG)KdpGetFirstParameter(ContextRecord),
160 (ULONG)KdpGetSecondParameter(ContextRecord),
161 (LPSTR)ExceptionRecord->
162 ExceptionInformation[1],
163 (USHORT)ExceptionRecord->
164 ExceptionInformation[2],
165 PreviousMode,
166 TrapFrame,
167 ExceptionFrame,
168 &Status);
169
170 /* Update the return value for the caller */
171 KeSetContextReturnRegister(ContextRecord, ReturnValue);
172 break;
173
174 /* DbgPrompt */
175 case BREAKPOINT_PROMPT:
176
177 /* Call the worker routine */
178 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord->
179 ExceptionInformation[1],
180 (USHORT)ExceptionRecord->
181 ExceptionInformation[2],
182 (LPSTR)KdpGetFirstParameter(ContextRecord),
183 (USHORT)KdpGetSecondParameter(ContextRecord),
184 PreviousMode,
185 TrapFrame,
186 ExceptionFrame);
187 Status = TRUE;
188
189 /* Update the return value for the caller */
190 KeSetContextReturnRegister(ContextRecord, ReturnValue);
191 break;
192
193 /* DbgUnLoadImageSymbols */
194 case BREAKPOINT_UNLOAD_SYMBOLS:
195
196 /* Drop into the load case below, with the unload parameter */
197 Unload = TRUE;
198
199 /* DbgLoadImageSymbols */
200 case BREAKPOINT_LOAD_SYMBOLS:
201
202 /* Call the worker routine */
203 KdpSymbol((PSTRING)ExceptionRecord->
204 ExceptionInformation[1],
205 (PKD_SYMBOLS_INFO)ExceptionRecord->
206 ExceptionInformation[2],
207 Unload,
208 PreviousMode,
209 ContextRecord,
210 TrapFrame,
211 ExceptionFrame);
212 Status = TRUE;
213 break;
214
215 /* DbgCommandString */
216 case BREAKPOINT_COMMAND_STRING:
217
218 /* Call the worker routine */
219 KdpCommandString((ULONG)ExceptionRecord->
220 ExceptionInformation[1],
221 (LPSTR)ExceptionRecord->
222 ExceptionInformation[2],
223 PreviousMode,
224 ContextRecord,
225 TrapFrame,
226 ExceptionFrame);
227 Status = TRUE;
228
229 /* Anything else, do nothing */
230 default:
231
232 /* Get out */
233 break;
234 }
235
236 /*
237 * If the PC was not updated, we'll increment it ourselves so execution
238 * continues past the breakpoint.
239 */
240 if (ProgramCounter == KeGetContextPc(ContextRecord))
241 {
242 /* Update it */
243 KeSetContextPc(ContextRecord,
244 ProgramCounter + KD_BREAKPOINT_SIZE);
245 }
246 }
247 else
248 {
249 /* Call the worker routine */
250 Status = KdpReport(TrapFrame,
251 ExceptionFrame,
252 ExceptionRecord,
253 ContextRecord,
254 PreviousMode,
255 SecondChanceException);
256 }
257
258 /* Return TRUE or FALSE to caller */
259 return Status;
260 }
261
262 BOOLEAN
263 NTAPI
264 KdpStub(IN PKTRAP_FRAME TrapFrame,
265 IN PKEXCEPTION_FRAME ExceptionFrame,
266 IN PEXCEPTION_RECORD ExceptionRecord,
267 IN PCONTEXT ContextRecord,
268 IN KPROCESSOR_MODE PreviousMode,
269 IN BOOLEAN SecondChanceException)
270 {
271 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
272
273 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
274 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
275 (ExceptionRecord->NumberParameters > 0) &&
276 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
277 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
278 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
279 (ExceptionCommand == BREAKPOINT_PRINT)))
280 {
281 /* This we can handle: simply bump the Program Counter */
282 KeSetContextPc(ContextRecord,
283 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
284 return TRUE;
285 }
286 else if (KdPitchDebugger)
287 {
288 /* There's no debugger, fail. */
289 return FALSE;
290 }
291 else if ((KdAutoEnableOnEvent) &&
292 (KdPreviouslyEnabled) &&
293 !(KdDebuggerEnabled) &&
294 (NT_SUCCESS(KdEnableDebugger())) &&
295 (KdDebuggerEnabled))
296 {
297 /* Debugging was Auto-Enabled. We can now send this to KD. */
298 return KdpTrap(TrapFrame,
299 ExceptionFrame,
300 ExceptionRecord,
301 ContextRecord,
302 PreviousMode,
303 SecondChanceException);
304 }
305 else
306 {
307 /* FIXME: All we can do in this case is trace this exception */
308 return FALSE;
309 }
310 }
311
312 BOOLEAN
313 NTAPI
314 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
315 IN PCONTEXT Context,
316 IN KPROCESSOR_MODE PreviousMode)
317 {
318 /*
319 * Determine if this is a valid debug service call and make sure that
320 * it isn't a software breakpoint
321 */
322 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
323 (ExceptionRecord->NumberParameters > 0) &&
324 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
325 {
326 /* Then we have to handle it */
327 return TRUE;
328 }
329 else
330 {
331 /* We don't have to handle it */
332 return FALSE;
333 }
334 }