- Fix KDBG's EIP munging.
[reactos.git] / reactos / ntoskrnl / kd / kdmain.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/kd/kdinit.c
5 * PURPOSE: Kernel Debugger Initializtion
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <internal/debug.h>
13
14 /* VARIABLES ***************************************************************/
15
16 BOOLEAN KdDebuggerEnabled = FALSE;
17 BOOLEAN KdEnteredDebugger = FALSE;
18 BOOLEAN KdDebuggerNotPresent = TRUE;
19 BOOLEAN KiEnableTimerWatchdog = FALSE;
20 BOOLEAN KdBreakAfterSymbolLoad = FALSE;
21 BOOLEAN KdpBreakPending;
22 BOOLEAN KdPitchDebugger = TRUE;
23 VOID STDCALL PspDumpThreads(BOOLEAN SystemThreads);
24
25 typedef struct
26 {
27 ULONG ComponentId;
28 ULONG Level;
29 } KD_COMPONENT_DATA;
30 #define MAX_KD_COMPONENT_TABLE_ENTRIES 128
31 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES];
32 ULONG KdComponentTableEntries = 0;
33
34 ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL;
35
36 /* PRIVATE FUNCTIONS *********************************************************/
37
38 ULONG
39 STDCALL
40 KdpServiceDispatcher(ULONG Service,
41 PVOID Buffer1,
42 ULONG Buffer1Length)
43 {
44 ULONG Result = 0;
45
46 switch (Service)
47 {
48 case BREAKPOINT_PRINT: /* DbgPrint */
49 Result = KdpPrintString(Buffer1, Buffer1Length);
50 break;
51
52 #ifdef DBG
53 case TAG('R', 'o', 's', ' '): /* ROS-INTERNAL */
54 {
55 switch ((ULONG)Buffer1)
56 {
57 case DumpNonPagedPool:
58 MiDebugDumpNonPagedPool(FALSE);
59 break;
60
61 case ManualBugCheck:
62 KEBUGCHECK(MANUALLY_INITIATED_CRASH);
63 break;
64
65 case DumpNonPagedPoolStats:
66 MiDebugDumpNonPagedPoolStats(FALSE);
67 break;
68
69 case DumpNewNonPagedPool:
70 MiDebugDumpNonPagedPool(TRUE);
71 break;
72
73 case DumpNewNonPagedPoolStats:
74 MiDebugDumpNonPagedPoolStats(TRUE);
75 break;
76
77 case DumpAllThreads:
78 PspDumpThreads(TRUE);
79 break;
80
81 case DumpUserThreads:
82 PspDumpThreads(FALSE);
83 break;
84
85 case EnterDebugger:
86 DbgBreakPoint();
87 break;
88
89 default:
90 break;
91 }
92 }
93 #endif
94 default:
95 HalDisplayString ("Invalid debug service call!\n");
96 break;
97 }
98
99 return Result;
100 }
101
102 BOOLEAN
103 NTAPI
104 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
105 IN PKEXCEPTION_FRAME ExceptionFrame,
106 IN PEXCEPTION_RECORD ExceptionRecord,
107 IN PCONTEXT Context,
108 IN KPROCESSOR_MODE PreviousMode,
109 IN BOOLEAN SecondChance)
110 {
111 KD_CONTINUE_TYPE Return;
112 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
113 #ifdef _M_IX86
114 ULONG EipOld;
115 #endif
116
117 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
118 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
119 (ExceptionRecord->NumberParameters > 0) &&
120 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
121 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
122 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
123 (ExceptionCommand == BREAKPOINT_PRINT)))
124 {
125 /* Check if this is a debug print */
126 if (ExceptionCommand == BREAKPOINT_PRINT)
127 {
128 /* Print the string */
129 KdpServiceDispatcher(BREAKPOINT_PRINT,
130 (PVOID)ExceptionRecord->ExceptionInformation[1],
131 ExceptionRecord->ExceptionInformation[2]);
132 }
133 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
134 {
135 /* Load symbols. Currently implemented only for KDBG! */
136 KDB_SYMBOLFILE_HOOK((PANSI_STRING)ExceptionRecord->ExceptionInformation[1],
137 (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2]);
138 }
139
140 /* This we can handle: simply bump EIP */
141 #ifdef _M_IX86
142 Context->Eip++;
143 #endif
144 return TRUE;
145 }
146
147 /* Get out of here if the Debugger isn't connected */
148 if (KdDebuggerNotPresent) return FALSE;
149
150 /* Save old EIP value */
151 #ifdef _M_IX86
152 EipOld = Context->Eip;
153 #endif
154
155 /* Call KDBG if available */
156 Return = KdbEnterDebuggerException(ExceptionRecord,
157 PreviousMode,
158 Context,
159 TrapFrame,
160 !SecondChance);
161
162 /* Bump EIP over int 3 if debugger did not already change it */
163 if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
164 {
165 //DPRINT1("Address: %p. Return: %d\n", EipOld, Return);
166 }
167
168 /* Convert return to BOOLEAN */
169 if (Return == kdDoNotHandleException) return FALSE;
170 return FALSE;
171 }
172
173 BOOLEAN
174 NTAPI
175 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
176 IN PEXCEPTION_RECORD ExceptionRecord,
177 IN PCONTEXT Context)
178 {
179 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
180
181 /* Get out of here if the Debugger isn't connected */
182 if (KdDebuggerNotPresent) return FALSE;
183
184 /* FIXME:
185 * Right now, the GDB wrapper seems to handle exceptions differntly
186 * from KDGB and both are called at different times, while the GDB
187 * one is only called once and that's it. I don't really have the knowledge
188 * to fix the GDB stub, so until then, we'll be using this hack
189 */
190 if (WrapperInitRoutine)
191 {
192 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
193 Context,
194 TrapFrame);
195 }
196
197 /* Convert return to BOOLEAN */
198 if (Return == kdDoNotHandleException) return FALSE;
199 return FALSE;
200 }
201
202 /* PUBLIC FUNCTIONS *********************************************************/
203
204 /*
205 * @implemented
206 */
207 NTSTATUS
208 STDCALL
209 KdDisableDebugger(VOID)
210 {
211 KIRQL OldIrql;
212
213 /* Raise IRQL */
214 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
215
216 /* TODO: Disable any breakpoints */
217
218 /* Disable the Debugger */
219 KdDebuggerEnabled = FALSE;
220
221 /* Lower the IRQL */
222 KeLowerIrql(OldIrql);
223
224 /* Return success */
225 return STATUS_SUCCESS;
226 }
227
228 /*
229 * @implemented
230 */
231 NTSTATUS
232 STDCALL
233 KdEnableDebugger(VOID)
234 {
235 KIRQL OldIrql;
236
237 /* Raise IRQL */
238 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
239
240 /* TODO: Re-enable any breakpoints */
241
242 /* Enable the Debugger */
243 KdDebuggerEnabled = TRUE;
244
245 /* Lower the IRQL */
246 KeLowerIrql(OldIrql);
247
248 /* Return success */
249 return STATUS_SUCCESS;
250 }
251
252 /*
253 * @implemented
254 */
255 BOOLEAN
256 STDCALL
257 KdPollBreakIn(VOID)
258 {
259 return KdpBreakPending;
260 }
261
262 /*
263 * @unimplemented
264 */
265 NTSTATUS
266 STDCALL
267 KdPowerTransition(ULONG PowerState)
268 {
269 UNIMPLEMENTED;
270 return STATUS_NOT_IMPLEMENTED;
271 }
272
273 /*
274 * @unimplemented
275 */
276 NTSTATUS
277 NTAPI
278 KdChangeOption(IN KD_OPTION Option,
279 IN ULONG InBufferLength OPTIONAL,
280 IN PVOID InBuffer,
281 IN ULONG OutBufferLength OPTIONAL,
282 OUT PVOID OutBuffer,
283 OUT PULONG OutBufferRequiredLength OPTIONAL)
284 {
285 UNIMPLEMENTED;
286 return STATUS_NOT_IMPLEMENTED;
287 }
288
289
290 NTSTATUS
291 NTAPI
292 NtQueryDebugFilterState(IN ULONG ComponentId,
293 IN ULONG Level)
294 {
295 ULONG i;
296
297 /* Convert Level to mask if it isn't already one */
298 if (Level < 32)
299 Level = 1 << Level;
300
301 /* Check if it is not the default component */
302 if (ComponentId != DPFLTR_DEFAULT_ID)
303 {
304 /* No, search for an existing entry in the table */
305 for (i = 0; i < KdComponentTableEntries; i++)
306 {
307 /* Check if it is the right component */
308 if (ComponentId == KdComponentTable[i].ComponentId)
309 {
310 /* Check if mask are matching */
311 return (Level & KdComponentTable[i].Level) != 0;
312 }
313 }
314 }
315
316 /* Entry not found in the table, use default mask */
317 return (Level & Kd_DEFAULT_MASK) != 0;
318 }
319
320 NTSTATUS
321 NTAPI
322 NtSetDebugFilterState(IN ULONG ComponentId,
323 IN ULONG Level,
324 IN BOOLEAN State)
325 {
326 ULONG i;
327
328 /* Convert Level to mask if it isn't already one */
329 if (Level < 32)
330 Level = 1 << Level;
331 Level &= ~DPFLTR_MASK;
332
333 /* Check if it is the default component */
334 if (ComponentId == DPFLTR_DEFAULT_ID)
335 {
336 /* Yes, modify the default mask */
337 if (State)
338 Kd_DEFAULT_MASK |= Level;
339 else
340 Kd_DEFAULT_MASK &= ~Level;
341
342 return STATUS_SUCCESS;
343 }
344
345 /* Search for an existing entry */
346 for (i = 0; i < KdComponentTableEntries; i++ )
347 {
348 if (ComponentId == KdComponentTable[i].ComponentId)
349 break;
350 }
351
352 /* Check if we have found an existing entry */
353 if (i == KdComponentTableEntries)
354 {
355 /* Check if we have enough space in the table */
356 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
357 return STATUS_INVALID_PARAMETER_1;
358
359 /* Add a new entry */
360 ++KdComponentTableEntries;
361 KdComponentTable[i].ComponentId = ComponentId;
362 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
363 }
364
365 /* Update entry table */
366 if (State)
367 KdComponentTable[i].Level |= Level;
368 else
369 KdComponentTable[i].Level &= ~Level;
370
371 return STATUS_SUCCESS;
372 }
373
374 /*
375 * @unimplemented
376 */
377 NTSTATUS
378 NTAPI
379 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
380 IN PVOID InputBuffer,
381 IN ULONG InputBufferLength,
382 OUT PVOID OutputBuffer,
383 IN ULONG OutputBufferLength,
384 IN OUT PULONG ReturnLength,
385 IN KPROCESSOR_MODE PreviousMode)
386 {
387 /* HACK */
388 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
389 }
390
391 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
392
393 /* EOF */