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