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