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