769539b0bd836bf6b1bbd7f53d402db590754a73
[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 KdBreakAfterSymbolLoad = FALSE;
20 BOOLEAN KdpBreakPending = FALSE;
21 BOOLEAN KdPitchDebugger = TRUE;
22 BOOLEAN KdIgnoreUmExceptions = FALSE;
23 KD_CONTEXT KdpContext;
24 ULONG Kd_WIN2000_Mask;
25 VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads);
26
27 typedef struct
28 {
29 ULONG ComponentId;
30 ULONG Level;
31 } KD_COMPONENT_DATA;
32 #define MAX_KD_COMPONENT_TABLE_ENTRIES 128
33 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES];
34 ULONG KdComponentTableEntries = 0;
35
36 ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL;
37
38 /* PRIVATE FUNCTIONS *********************************************************/
39
40 ULONG
41 NTAPI
42 KdpServiceDispatcher(ULONG Service,
43 PVOID Buffer1,
44 ULONG Buffer1Length)
45 {
46 ULONG Result = 0;
47
48 switch (Service)
49 {
50 case BREAKPOINT_PRINT: /* DbgPrint */
51 Result = KdpPrintString(Buffer1, Buffer1Length);
52 break;
53
54 #if DBG
55 case ' soR': /* ROS-INTERNAL */
56 {
57 switch ((ULONG_PTR)Buffer1)
58 {
59 case DumpAllThreads:
60 PspDumpThreads(TRUE);
61 break;
62
63 case DumpUserThreads:
64 PspDumpThreads(FALSE);
65 break;
66
67 case KdSpare3:
68 MmDumpArmPfnDatabase(FALSE);
69 break;
70
71 default:
72 break;
73 }
74 break;
75 }
76
77 /* Register a debug callback */
78 case 'CsoR':
79 {
80 switch (Buffer1Length)
81 {
82 case ID_Win32PreServiceHook:
83 KeWin32PreServiceHook = Buffer1;
84 break;
85
86 case ID_Win32PostServiceHook:
87 KeWin32PostServiceHook = Buffer1;
88 break;
89
90 }
91 break;
92 }
93
94 /* Special case for stack frame dumps */
95 case 'DsoR':
96 {
97 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length);
98 break;
99 }
100
101 #if KDBG
102 /* Register KDBG CLI callback */
103 case 'RbdK':
104 {
105 Result = KdbRegisterCliCallback(Buffer1, Buffer1Length);
106 break;
107 }
108 #endif /* KDBG */
109 #endif /* DBG */
110 default:
111 DPRINT1("Invalid debug service call!\n");
112 HalDisplayString("Invalid debug service call!\r\n");
113 break;
114 }
115
116 return Result;
117 }
118
119 BOOLEAN
120 NTAPI
121 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
122 IN PKEXCEPTION_FRAME ExceptionFrame,
123 IN PEXCEPTION_RECORD ExceptionRecord,
124 IN PCONTEXT Context,
125 IN KPROCESSOR_MODE PreviousMode,
126 IN BOOLEAN SecondChance)
127 {
128 KD_CONTINUE_TYPE Return = kdHandleException;
129 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
130
131 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
132 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
133 (ExceptionRecord->NumberParameters > 0) &&
134 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
135 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
136 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
137 (ExceptionCommand == BREAKPOINT_PRINT) ||
138 (ExceptionCommand == BREAKPOINT_PROMPT)))
139 {
140 /* Check if this is a debug print */
141 if (ExceptionCommand == BREAKPOINT_PRINT)
142 {
143 /* Print the string */
144 KdpServiceDispatcher(BREAKPOINT_PRINT,
145 (PVOID)ExceptionRecord->ExceptionInformation[1],
146 ExceptionRecord->ExceptionInformation[2]);
147
148 /* Return success */
149 KeSetContextReturnRegister(Context, STATUS_SUCCESS);
150 }
151 #ifdef KDBG
152 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
153 {
154 PLDR_DATA_TABLE_ENTRY LdrEntry;
155
156 /* Load symbols. Currently implemented only for KDBG! */
157 if(KdbpSymFindModule(((PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2])->BaseOfDll, NULL, -1, &LdrEntry))
158 KdbSymProcessSymbols(LdrEntry);
159 }
160 else if (ExceptionCommand == BREAKPOINT_PROMPT)
161 {
162 ULONG ReturnValue;
163 LPSTR OutString;
164 USHORT OutStringLength;
165
166 /* Get the response string and length */
167 OutString = (LPSTR)Context->Ebx;
168 OutStringLength = (USHORT)Context->Edi;
169
170 /* Call KDBG */
171 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord->
172 ExceptionInformation[1],
173 (USHORT)ExceptionRecord->
174 ExceptionInformation[2],
175 OutString,
176 OutStringLength);
177
178 /* Return the number of characters that we received */
179 Context->Eax = ReturnValue;
180 }
181 #endif
182
183 /* This we can handle: simply bump the Program Counter */
184 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
185 return TRUE;
186 }
187
188 #ifdef KDBG
189 /* Check if this is an assertion failure */
190 if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE)
191 {
192 /* Warn about it */
193 DbgPrint("\n!!! Assertion Failure at Address 0x%p !!!\n\n",
194 (PVOID)Context->Eip);
195
196 /* Bump EIP to the instruction following the int 2C and return */
197 Context->Eip += 2;
198 return TRUE;
199 }
200 #endif
201
202 /* Get out of here if the Debugger isn't connected */
203 if (KdDebuggerNotPresent) return FALSE;
204
205 #ifdef KDBG
206 /* Call KDBG if available */
207 Return = KdbEnterDebuggerException(ExceptionRecord,
208 PreviousMode,
209 Context,
210 TrapFrame,
211 !SecondChance);
212 #else /* not KDBG */
213 if (WrapperInitRoutine)
214 {
215 /* Call GDB */
216 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
217 Context,
218 TrapFrame);
219 }
220 #endif /* not KDBG */
221
222 /* Debugger didn't handle it, please handle! */
223 if (Return == kdHandleException) return FALSE;
224
225 /* Debugger handled it */
226 return TRUE;
227 }
228
229 BOOLEAN
230 NTAPI
231 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
232 IN PEXCEPTION_RECORD ExceptionRecord,
233 IN PCONTEXT Context)
234 {
235 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
236
237 /* Get out of here if the Debugger isn't connected */
238 if (KdDebuggerNotPresent) return FALSE;
239
240 /* FIXME:
241 * Right now, the GDB wrapper seems to handle exceptions differntly
242 * from KDGB and both are called at different times, while the GDB
243 * one is only called once and that's it. I don't really have the knowledge
244 * to fix the GDB stub, so until then, we'll be using this hack
245 */
246 if (WrapperInitRoutine)
247 {
248 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
249 Context,
250 TrapFrame);
251 }
252
253 /* Debugger didn't handle it, please handle! */
254 if (Return == kdHandleException) return FALSE;
255
256 /* Debugger handled it */
257 return TRUE;
258 }
259
260 BOOLEAN
261 NTAPI
262 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
263 IN PCONTEXT Context,
264 IN KPROCESSOR_MODE PreviousMode)
265 {
266 /* KDBG has its own mechanism for ignoring user mode exceptions */
267 return FALSE;
268 }
269
270 /* PUBLIC FUNCTIONS *********************************************************/
271
272 /*
273 * @implemented
274 */
275 BOOLEAN
276 NTAPI
277 KdRefreshDebuggerNotPresent(VOID)
278 {
279 UNIMPLEMENTED;
280
281 /* Just return whatever was set previously -- FIXME! */
282 return KdDebuggerNotPresent;
283 }
284
285 /*
286 * @implemented
287 */
288 NTSTATUS
289 NTAPI
290 KdDisableDebugger(VOID)
291 {
292 KIRQL OldIrql;
293
294 /* Raise IRQL */
295 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
296
297 /* TODO: Disable any breakpoints */
298
299 /* Disable the Debugger */
300 KdDebuggerEnabled = FALSE;
301
302 /* Lower the IRQL */
303 KeLowerIrql(OldIrql);
304
305 /* Return success */
306 return STATUS_SUCCESS;
307 }
308
309 /*
310 * @implemented
311 */
312 NTSTATUS
313 NTAPI
314 KdEnableDebugger(VOID)
315 {
316 KIRQL OldIrql;
317
318 /* Raise IRQL */
319 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
320
321 /* TODO: Re-enable any breakpoints */
322
323 /* Enable the Debugger */
324 KdDebuggerEnabled = TRUE;
325
326 /* Lower the IRQL */
327 KeLowerIrql(OldIrql);
328
329 /* Return success */
330 return STATUS_SUCCESS;
331 }
332
333 /*
334 * @implemented
335 */
336 BOOLEAN
337 NTAPI
338 KdPollBreakIn(VOID)
339 {
340 return KdpBreakPending;
341 }
342
343 /*
344 * @unimplemented
345 */
346 NTSTATUS
347 NTAPI
348 KdPowerTransition(ULONG PowerState)
349 {
350 UNIMPLEMENTED;
351 return STATUS_NOT_IMPLEMENTED;
352 }
353
354 /*
355 * @unimplemented
356 */
357 NTSTATUS
358 NTAPI
359 KdChangeOption(IN KD_OPTION Option,
360 IN ULONG InBufferLength OPTIONAL,
361 IN PVOID InBuffer,
362 IN ULONG OutBufferLength OPTIONAL,
363 OUT PVOID OutBuffer,
364 OUT PULONG OutBufferRequiredLength OPTIONAL)
365 {
366 UNIMPLEMENTED;
367 return STATUS_NOT_IMPLEMENTED;
368 }
369
370
371 NTSTATUS
372 NTAPI
373 NtQueryDebugFilterState(IN ULONG ComponentId,
374 IN ULONG Level)
375 {
376 ULONG i;
377
378 /* Convert Level to mask if it isn't already one */
379 if (Level < 32)
380 Level = 1 << Level;
381
382 /* Check if it is not the default component */
383 if (ComponentId != DPFLTR_DEFAULT_ID)
384 {
385 /* No, search for an existing entry in the table */
386 for (i = 0; i < KdComponentTableEntries; i++)
387 {
388 /* Check if it is the right component */
389 if (ComponentId == KdComponentTable[i].ComponentId)
390 {
391 /* Check if mask are matching */
392 return (Level & KdComponentTable[i].Level) ? TRUE : FALSE;
393 }
394 }
395 }
396
397 /* Entry not found in the table, use default mask */
398 return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE;
399 }
400
401 NTSTATUS
402 NTAPI
403 NtSetDebugFilterState(IN ULONG ComponentId,
404 IN ULONG Level,
405 IN BOOLEAN State)
406 {
407 ULONG i;
408
409 /* Convert Level to mask if it isn't already one */
410 if (Level < 32)
411 Level = 1 << Level;
412 Level &= ~DPFLTR_MASK;
413
414 /* Check if it is the default component */
415 if (ComponentId == DPFLTR_DEFAULT_ID)
416 {
417 /* Yes, modify the default mask */
418 if (State)
419 Kd_DEFAULT_MASK |= Level;
420 else
421 Kd_DEFAULT_MASK &= ~Level;
422
423 return STATUS_SUCCESS;
424 }
425
426 /* Search for an existing entry */
427 for (i = 0; i < KdComponentTableEntries; i++ )
428 {
429 if (ComponentId == KdComponentTable[i].ComponentId)
430 break;
431 }
432
433 /* Check if we have found an existing entry */
434 if (i == KdComponentTableEntries)
435 {
436 /* Check if we have enough space in the table */
437 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
438 return STATUS_INVALID_PARAMETER_1;
439
440 /* Add a new entry */
441 ++KdComponentTableEntries;
442 KdComponentTable[i].ComponentId = ComponentId;
443 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
444 }
445
446 /* Update entry table */
447 if (State)
448 KdComponentTable[i].Level |= Level;
449 else
450 KdComponentTable[i].Level &= ~Level;
451
452 return STATUS_SUCCESS;
453 }
454
455 /*
456 * @unimplemented
457 */
458 NTSTATUS
459 NTAPI
460 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
461 IN PVOID InputBuffer,
462 IN ULONG InputBufferLength,
463 OUT PVOID OutputBuffer,
464 IN ULONG OutputBufferLength,
465 IN OUT PULONG ReturnLength,
466 IN KPROCESSOR_MODE PreviousMode)
467 {
468 /* HACK */
469 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
470 }
471
472 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
473
474 /* EOF */