- If KDBG is not compiled in, try to use GDB instead.
[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 Initialization
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <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 NTAPI 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 NTAPI
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 #if 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 break;
93 }
94
95 /* Special case for stack frame dumps */
96 case TAG('R', 'o', 's', 'D'):
97 {
98 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length);
99 break;
100 }
101 #endif
102 default:
103 HalDisplayString ("Invalid debug service call!\n");
104 break;
105 }
106
107 return Result;
108 }
109
110 BOOLEAN
111 NTAPI
112 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
113 IN PKEXCEPTION_FRAME ExceptionFrame,
114 IN PEXCEPTION_RECORD ExceptionRecord,
115 IN PCONTEXT Context,
116 IN KPROCESSOR_MODE PreviousMode,
117 IN BOOLEAN SecondChance)
118 {
119 KD_CONTINUE_TYPE Return;
120 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
121 #ifdef _M_IX86
122 ULONG EipOld;
123 #endif
124
125 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
126 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
127 (ExceptionRecord->NumberParameters > 0) &&
128 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
129 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
130 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
131 (ExceptionCommand == BREAKPOINT_PRINT)))
132 {
133 /* Check if this is a debug print */
134 if (ExceptionCommand == BREAKPOINT_PRINT)
135 {
136 /* Print the string */
137 KdpServiceDispatcher(BREAKPOINT_PRINT,
138 (PVOID)ExceptionRecord->ExceptionInformation[1],
139 ExceptionRecord->ExceptionInformation[2]);
140 Context->Eax = STATUS_SUCCESS;
141 }
142 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
143 {
144 /* Load symbols. Currently implemented only for KDBG! */
145 KDB_SYMBOLFILE_HOOK((PANSI_STRING)ExceptionRecord->ExceptionInformation[1],
146 (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2]);
147 }
148
149 /* This we can handle: simply bump EIP */
150 #ifdef _M_IX86
151 Context->Eip++;
152 #elif _M_ARM
153 Context->Pc += sizeof(ULONG);
154 #endif
155 return TRUE;
156 }
157
158 /* Get out of here if the Debugger isn't connected */
159 if (KdDebuggerNotPresent) return FALSE;
160
161 /* Save old EIP value */
162 #ifdef _M_IX86
163 EipOld = Context->Eip;
164 #endif
165
166 #ifdef KDBG
167 /* Call KDBG if available */
168 Return = KdbEnterDebuggerException(ExceptionRecord,
169 PreviousMode,
170 Context,
171 TrapFrame,
172 !SecondChance);
173 #else /* not KDBG */
174 if (WrapperInitRoutine)
175 {
176 /* Call GDB */
177 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
178 Context,
179 TrapFrame);
180 }
181 #endif /* not KDBG */
182
183 /* Bump EIP over int 3 if debugger did not already change it */
184 if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
185 {
186 //DPRINT1("Address: %p. Return: %d\n", EipOld, Return);
187 }
188
189 /* Debugger didn't handle it, please handle! */
190 if (Return == kdHandleException) return FALSE;
191
192 /* Debugger handled it */
193 return TRUE;
194 }
195
196 BOOLEAN
197 NTAPI
198 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
199 IN PEXCEPTION_RECORD ExceptionRecord,
200 IN PCONTEXT Context)
201 {
202 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
203
204 /* Get out of here if the Debugger isn't connected */
205 if (KdDebuggerNotPresent) return FALSE;
206
207 /* FIXME:
208 * Right now, the GDB wrapper seems to handle exceptions differntly
209 * from KDGB and both are called at different times, while the GDB
210 * one is only called once and that's it. I don't really have the knowledge
211 * to fix the GDB stub, so until then, we'll be using this hack
212 */
213 if (WrapperInitRoutine)
214 {
215 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
216 Context,
217 TrapFrame);
218 }
219
220 /* Debugger didn't handle it, please handle! */
221 if (Return == kdHandleException) return FALSE;
222
223 /* Debugger handled it */
224 return TRUE;
225 }
226
227 /* PUBLIC FUNCTIONS *********************************************************/
228
229 /*
230 * @implemented
231 */
232 BOOLEAN
233 NTAPI
234 KdRefreshDebuggerNotPresent(VOID)
235 {
236 UNIMPLEMENTED;
237
238 /* Just return whatever was set previously -- FIXME! */
239 return KdDebuggerNotPresent;
240 }
241
242 /*
243 * @implemented
244 */
245 NTSTATUS
246 NTAPI
247 KdDisableDebugger(VOID)
248 {
249 KIRQL OldIrql;
250
251 /* Raise IRQL */
252 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
253
254 /* TODO: Disable any breakpoints */
255
256 /* Disable the Debugger */
257 KdDebuggerEnabled = FALSE;
258
259 /* Lower the IRQL */
260 KeLowerIrql(OldIrql);
261
262 /* Return success */
263 return STATUS_SUCCESS;
264 }
265
266 /*
267 * @implemented
268 */
269 NTSTATUS
270 NTAPI
271 KdEnableDebugger(VOID)
272 {
273 KIRQL OldIrql;
274
275 /* Raise IRQL */
276 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
277
278 /* TODO: Re-enable any breakpoints */
279
280 /* Enable the Debugger */
281 KdDebuggerEnabled = TRUE;
282
283 /* Lower the IRQL */
284 KeLowerIrql(OldIrql);
285
286 /* Return success */
287 return STATUS_SUCCESS;
288 }
289
290 /*
291 * @implemented
292 */
293 BOOLEAN
294 NTAPI
295 KdPollBreakIn(VOID)
296 {
297 return KdpBreakPending;
298 }
299
300 /*
301 * @unimplemented
302 */
303 NTSTATUS
304 NTAPI
305 KdPowerTransition(ULONG PowerState)
306 {
307 UNIMPLEMENTED;
308 return STATUS_NOT_IMPLEMENTED;
309 }
310
311 /*
312 * @unimplemented
313 */
314 NTSTATUS
315 NTAPI
316 KdChangeOption(IN KD_OPTION Option,
317 IN ULONG InBufferLength OPTIONAL,
318 IN PVOID InBuffer,
319 IN ULONG OutBufferLength OPTIONAL,
320 OUT PVOID OutBuffer,
321 OUT PULONG OutBufferRequiredLength OPTIONAL)
322 {
323 UNIMPLEMENTED;
324 return STATUS_NOT_IMPLEMENTED;
325 }
326
327
328 NTSTATUS
329 NTAPI
330 NtQueryDebugFilterState(IN ULONG ComponentId,
331 IN ULONG Level)
332 {
333 ULONG i;
334
335 /* Convert Level to mask if it isn't already one */
336 if (Level < 32)
337 Level = 1 << Level;
338
339 /* Check if it is not the default component */
340 if (ComponentId != DPFLTR_DEFAULT_ID)
341 {
342 /* No, search for an existing entry in the table */
343 for (i = 0; i < KdComponentTableEntries; i++)
344 {
345 /* Check if it is the right component */
346 if (ComponentId == KdComponentTable[i].ComponentId)
347 {
348 /* Check if mask are matching */
349 return (Level & KdComponentTable[i].Level) != 0;
350 }
351 }
352 }
353
354 /* Entry not found in the table, use default mask */
355 return (Level & Kd_DEFAULT_MASK) != 0;
356 }
357
358 NTSTATUS
359 NTAPI
360 NtSetDebugFilterState(IN ULONG ComponentId,
361 IN ULONG Level,
362 IN BOOLEAN State)
363 {
364 ULONG i;
365
366 /* Convert Level to mask if it isn't already one */
367 if (Level < 32)
368 Level = 1 << Level;
369 Level &= ~DPFLTR_MASK;
370
371 /* Check if it is the default component */
372 if (ComponentId == DPFLTR_DEFAULT_ID)
373 {
374 /* Yes, modify the default mask */
375 if (State)
376 Kd_DEFAULT_MASK |= Level;
377 else
378 Kd_DEFAULT_MASK &= ~Level;
379
380 return STATUS_SUCCESS;
381 }
382
383 /* Search for an existing entry */
384 for (i = 0; i < KdComponentTableEntries; i++ )
385 {
386 if (ComponentId == KdComponentTable[i].ComponentId)
387 break;
388 }
389
390 /* Check if we have found an existing entry */
391 if (i == KdComponentTableEntries)
392 {
393 /* Check if we have enough space in the table */
394 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
395 return STATUS_INVALID_PARAMETER_1;
396
397 /* Add a new entry */
398 ++KdComponentTableEntries;
399 KdComponentTable[i].ComponentId = ComponentId;
400 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
401 }
402
403 /* Update entry table */
404 if (State)
405 KdComponentTable[i].Level |= Level;
406 else
407 KdComponentTable[i].Level &= ~Level;
408
409 return STATUS_SUCCESS;
410 }
411
412 /*
413 * @unimplemented
414 */
415 NTSTATUS
416 NTAPI
417 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
418 IN PVOID InputBuffer,
419 IN ULONG InputBufferLength,
420 OUT PVOID OutputBuffer,
421 IN ULONG OutputBufferLength,
422 IN OUT PULONG ReturnLength,
423 IN KPROCESSOR_MODE PreviousMode)
424 {
425 /* HACK */
426 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
427 }
428
429 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
430
431 /* EOF */