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