Merge trunk (r43561)
[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 = FALSE;
22 BOOLEAN KdPitchDebugger = TRUE;
23 BOOLEAN KdIgnoreUmExceptions = FALSE;
24 VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads);
25
26 typedef struct
27 {
28 ULONG ComponentId;
29 ULONG Level;
30 } KD_COMPONENT_DATA;
31 #define MAX_KD_COMPONENT_TABLE_ENTRIES 128
32 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES];
33 ULONG KdComponentTableEntries = 0;
34
35 ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL;
36
37 /* PRIVATE FUNCTIONS *********************************************************/
38
39 ULONG
40 NTAPI
41 KdpServiceDispatcher(ULONG Service,
42 PVOID Buffer1,
43 ULONG Buffer1Length)
44 {
45 ULONG Result = 0;
46
47 switch (Service)
48 {
49 case BREAKPOINT_PRINT: /* DbgPrint */
50 Result = KdpPrintString(Buffer1, Buffer1Length);
51 break;
52
53 #if DBG
54 case ' soR': /* ROS-INTERNAL */
55 {
56 switch ((ULONG_PTR)Buffer1)
57 {
58 case ManualBugCheck:
59 KeBugCheck(MANUALLY_INITIATED_CRASH);
60 break;
61
62 case DumpAllThreads:
63 PspDumpThreads(TRUE);
64 break;
65
66 case DumpUserThreads:
67 PspDumpThreads(FALSE);
68 break;
69
70 case EnterDebugger:
71 DbgBreakPoint();
72 break;
73
74 case ThatsWhatSheSaid:
75 MmDumpPfnDatabase();
76 break;
77
78 default:
79 break;
80 }
81 break;
82 }
83
84 /* Special case for stack frame dumps */
85 case 'DsoR':
86 {
87 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length);
88 break;
89 }
90 #endif
91 default:
92 HalDisplayString ("Invalid debug service call!\n");
93 break;
94 }
95
96 return Result;
97 }
98
99 BOOLEAN
100 NTAPI
101 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
102 IN PKEXCEPTION_FRAME ExceptionFrame,
103 IN PEXCEPTION_RECORD ExceptionRecord,
104 IN PCONTEXT Context,
105 IN KPROCESSOR_MODE PreviousMode,
106 IN BOOLEAN SecondChance)
107 {
108 KD_CONTINUE_TYPE Return = kdHandleException;
109 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
110
111 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
112 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
113 (ExceptionRecord->NumberParameters > 0) &&
114 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
115 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
116 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
117 (ExceptionCommand == BREAKPOINT_PRINT)))
118 {
119 /* Check if this is a debug print */
120 if (ExceptionCommand == BREAKPOINT_PRINT)
121 {
122 /* Print the string */
123 KdpServiceDispatcher(BREAKPOINT_PRINT,
124 (PVOID)ExceptionRecord->ExceptionInformation[1],
125 ExceptionRecord->ExceptionInformation[2]);
126
127 /* Return success */
128 KeSetContextReturnRegister(Context, STATUS_SUCCESS);
129 }
130 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
131 {
132 #ifdef KDBG
133 PLDR_DATA_TABLE_ENTRY LdrEntry;
134
135 /* Load symbols. Currently implemented only for KDBG! */
136 if(KdbpSymFindModule(((PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2])->BaseOfDll, NULL, -1, &LdrEntry))
137 KdbSymProcessSymbols(LdrEntry);
138 #endif
139 }
140
141 /* This we can handle: simply bump the Program Counter */
142 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
143 return TRUE;
144 }
145
146 /* Get out of here if the Debugger isn't connected */
147 if (KdDebuggerNotPresent) return FALSE;
148
149 #ifdef KDBG
150 /* Call KDBG if available */
151 Return = KdbEnterDebuggerException(ExceptionRecord,
152 PreviousMode,
153 Context,
154 TrapFrame,
155 !SecondChance);
156 #else /* not KDBG */
157 if (WrapperInitRoutine)
158 {
159 /* Call GDB */
160 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
161 Context,
162 TrapFrame);
163 }
164 #endif /* not KDBG */
165
166 /* Debugger didn't handle it, please handle! */
167 if (Return == kdHandleException) return FALSE;
168
169 /* Debugger handled it */
170 return TRUE;
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 /* Debugger didn't handle it, please handle! */
198 if (Return == kdHandleException) return FALSE;
199
200 /* Debugger handled it */
201 return TRUE;
202 }
203
204 BOOLEAN
205 NTAPI
206 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
207 IN PCONTEXT Context,
208 IN KPROCESSOR_MODE PreviousMode)
209 {
210 /* KDBG has its own mechanism for ignoring user mode exceptions */
211 return FALSE;
212 }
213
214 /* PUBLIC FUNCTIONS *********************************************************/
215
216 /*
217 * @implemented
218 */
219 BOOLEAN
220 NTAPI
221 KdRefreshDebuggerNotPresent(VOID)
222 {
223 UNIMPLEMENTED;
224
225 /* Just return whatever was set previously -- FIXME! */
226 return KdDebuggerNotPresent;
227 }
228
229 /*
230 * @implemented
231 */
232 NTSTATUS
233 NTAPI
234 KdDisableDebugger(VOID)
235 {
236 KIRQL OldIrql;
237
238 /* Raise IRQL */
239 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
240
241 /* TODO: Disable any breakpoints */
242
243 /* Disable the Debugger */
244 KdDebuggerEnabled = FALSE;
245
246 /* Lower the IRQL */
247 KeLowerIrql(OldIrql);
248
249 /* Return success */
250 return STATUS_SUCCESS;
251 }
252
253 /*
254 * @implemented
255 */
256 NTSTATUS
257 NTAPI
258 KdEnableDebugger(VOID)
259 {
260 KIRQL OldIrql;
261
262 /* Raise IRQL */
263 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
264
265 /* TODO: Re-enable any breakpoints */
266
267 /* Enable the Debugger */
268 KdDebuggerEnabled = TRUE;
269
270 /* Lower the IRQL */
271 KeLowerIrql(OldIrql);
272
273 /* Return success */
274 return STATUS_SUCCESS;
275 }
276
277 /*
278 * @implemented
279 */
280 BOOLEAN
281 NTAPI
282 KdPollBreakIn(VOID)
283 {
284 return KdpBreakPending;
285 }
286
287 /*
288 * @unimplemented
289 */
290 NTSTATUS
291 NTAPI
292 KdPowerTransition(ULONG PowerState)
293 {
294 UNIMPLEMENTED;
295 return STATUS_NOT_IMPLEMENTED;
296 }
297
298 /*
299 * @unimplemented
300 */
301 NTSTATUS
302 NTAPI
303 KdChangeOption(IN KD_OPTION Option,
304 IN ULONG InBufferLength OPTIONAL,
305 IN PVOID InBuffer,
306 IN ULONG OutBufferLength OPTIONAL,
307 OUT PVOID OutBuffer,
308 OUT PULONG OutBufferRequiredLength OPTIONAL)
309 {
310 UNIMPLEMENTED;
311 return STATUS_NOT_IMPLEMENTED;
312 }
313
314
315 NTSTATUS
316 NTAPI
317 NtQueryDebugFilterState(IN ULONG ComponentId,
318 IN ULONG Level)
319 {
320 ULONG i;
321
322 /* Convert Level to mask if it isn't already one */
323 if (Level < 32)
324 Level = 1 << Level;
325
326 /* Check if it is not the default component */
327 if (ComponentId != DPFLTR_DEFAULT_ID)
328 {
329 /* No, search for an existing entry in the table */
330 for (i = 0; i < KdComponentTableEntries; i++)
331 {
332 /* Check if it is the right component */
333 if (ComponentId == KdComponentTable[i].ComponentId)
334 {
335 /* Check if mask are matching */
336 return (Level & KdComponentTable[i].Level) != 0;
337 }
338 }
339 }
340
341 /* Entry not found in the table, use default mask */
342 return (Level & Kd_DEFAULT_MASK) != 0;
343 }
344
345 NTSTATUS
346 NTAPI
347 NtSetDebugFilterState(IN ULONG ComponentId,
348 IN ULONG Level,
349 IN BOOLEAN State)
350 {
351 ULONG i;
352
353 /* Convert Level to mask if it isn't already one */
354 if (Level < 32)
355 Level = 1 << Level;
356 Level &= ~DPFLTR_MASK;
357
358 /* Check if it is the default component */
359 if (ComponentId == DPFLTR_DEFAULT_ID)
360 {
361 /* Yes, modify the default mask */
362 if (State)
363 Kd_DEFAULT_MASK |= Level;
364 else
365 Kd_DEFAULT_MASK &= ~Level;
366
367 return STATUS_SUCCESS;
368 }
369
370 /* Search for an existing entry */
371 for (i = 0; i < KdComponentTableEntries; i++ )
372 {
373 if (ComponentId == KdComponentTable[i].ComponentId)
374 break;
375 }
376
377 /* Check if we have found an existing entry */
378 if (i == KdComponentTableEntries)
379 {
380 /* Check if we have enough space in the table */
381 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
382 return STATUS_INVALID_PARAMETER_1;
383
384 /* Add a new entry */
385 ++KdComponentTableEntries;
386 KdComponentTable[i].ComponentId = ComponentId;
387 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
388 }
389
390 /* Update entry table */
391 if (State)
392 KdComponentTable[i].Level |= Level;
393 else
394 KdComponentTable[i].Level &= ~Level;
395
396 return STATUS_SUCCESS;
397 }
398
399 /*
400 * @unimplemented
401 */
402 NTSTATUS
403 NTAPI
404 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
405 IN PVOID InputBuffer,
406 IN ULONG InputBufferLength,
407 OUT PVOID OutputBuffer,
408 IN ULONG OutputBufferLength,
409 IN OUT PULONG ReturnLength,
410 IN KPROCESSOR_MODE PreviousMode)
411 {
412 /* HACK */
413 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
414 }
415
416 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
417
418 /* EOF */