- DBGKD_WAIT_STATE_CHANGE64 is used in KD protocol 5, not number 6 that we use. Proto...
[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;
139 BOOLEAN Status = FALSE;
140 NTSTATUS ReturnStatus;
141 USHORT ReturnLength;
142
143 /*
144 * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or
145 * Load/Unload symbols. Make sure it isn't a software breakpoints as those
146 * are handled by KdpReport.
147 */
148 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
149 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
150 {
151 /* Save Program Counter */
152 ProgramCounter = KeGetContextPc(ContextRecord);
153
154 /* Check what kind of operation was requested from us */
155 switch (ExceptionRecord->ExceptionInformation[0])
156 {
157 /* DbgPrint */
158 case BREAKPOINT_PRINT:
159
160 /* Call the worker routine */
161 ReturnStatus = KdpPrint((ULONG)KdpGetFirstParameter(ContextRecord),
162 (ULONG)KdpGetSecondParameter(ContextRecord),
163 (LPSTR)ExceptionRecord->
164 ExceptionInformation[1],
165 (USHORT)ExceptionRecord->
166 ExceptionInformation[2],
167 PreviousMode,
168 TrapFrame,
169 ExceptionFrame,
170 &Status);
171
172 /* Update the return value for the caller */
173 KeSetContextReturnRegister(ContextRecord, ReturnStatus);
174 break;
175
176 /* DbgPrompt */
177 case BREAKPOINT_PROMPT:
178
179 /* Call the worker routine */
180 ReturnLength = KdpPrompt((LPSTR)ExceptionRecord->
181 ExceptionInformation[1],
182 (USHORT)ExceptionRecord->
183 ExceptionInformation[2],
184 (LPSTR)KdpGetFirstParameter(ContextRecord),
185 (USHORT)KdpGetSecondParameter(ContextRecord),
186 PreviousMode,
187 TrapFrame,
188 ExceptionFrame);
189 Status = TRUE;
190
191 /* Update the return value for the caller */
192 KeSetContextReturnRegister(ContextRecord, ReturnLength);
193 break;
194
195 /* DbgUnLoadImageSymbols */
196 case BREAKPOINT_UNLOAD_SYMBOLS:
197
198 /* Drop into the load case below, with the unload parameter */
199 Unload = TRUE;
200
201 /* DbgLoadImageSymbols */
202 case BREAKPOINT_LOAD_SYMBOLS:
203
204 /* Call the worker routine */
205 KdpSymbol((PSTRING)ExceptionRecord->
206 ExceptionInformation[1],
207 (PKD_SYMBOLS_INFO)ExceptionRecord->
208 ExceptionInformation[2],
209 Unload,
210 PreviousMode,
211 ContextRecord,
212 TrapFrame,
213 ExceptionFrame);
214 Status = TRUE;
215 break;
216
217 /* DbgCommandString */
218 case BREAKPOINT_COMMAND_STRING:
219
220 /* Call the worker routine */
221 KdpCommandString((ULONG)ExceptionRecord->
222 ExceptionInformation[1],
223 (LPSTR)ExceptionRecord->
224 ExceptionInformation[2],
225 PreviousMode,
226 ContextRecord,
227 TrapFrame,
228 ExceptionFrame);
229 Status = TRUE;
230
231 /* Anything else, do nothing */
232 default:
233
234 /* Get out */
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 Status = KdpReport(TrapFrame,
253 ExceptionFrame,
254 ExceptionRecord,
255 ContextRecord,
256 PreviousMode,
257 SecondChanceException);
258 }
259
260 /* Return TRUE or FALSE to caller */
261 return Status;
262 }
263
264 BOOLEAN
265 NTAPI
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 ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
274
275 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
276 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
277 (ExceptionRecord->NumberParameters > 0) &&
278 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
279 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
280 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
281 (ExceptionCommand == BREAKPOINT_PRINT)))
282 {
283 /* This we can handle: simply bump the Program Counter */
284 KeSetContextPc(ContextRecord,
285 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE);
286 return TRUE;
287 }
288 else if (KdPitchDebugger)
289 {
290 /* There's no debugger, fail. */
291 return FALSE;
292 }
293 else if ((KdAutoEnableOnEvent) &&
294 (KdPreviouslyEnabled) &&
295 !(KdDebuggerEnabled) &&
296 (NT_SUCCESS(KdEnableDebugger())) &&
297 (KdDebuggerEnabled))
298 {
299 /* Debugging was Auto-Enabled. We can now send this to KD. */
300 return KdpTrap(TrapFrame,
301 ExceptionFrame,
302 ExceptionRecord,
303 ContextRecord,
304 PreviousMode,
305 SecondChanceException);
306 }
307 else
308 {
309 /* FIXME: All we can do in this case is trace this exception */
310 return FALSE;
311 }
312 }
313
314 BOOLEAN
315 NTAPI
316 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
317 IN PCONTEXT Context,
318 IN KPROCESSOR_MODE PreviousMode)
319 {
320 /*
321 * Determine if this is a valid debug service call and make sure that
322 * it isn't a software breakpoint
323 */
324 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
325 (ExceptionRecord->NumberParameters > 0) &&
326 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK))
327 {
328 /* Then we have to handle it */
329 return TRUE;
330 }
331 else
332 {
333 /* We don't have to handle it */
334 return FALSE;
335 }
336 }