[NTOSKRNL] Add a raw implementation of !irpfind in kdbg
[reactos.git] / ntoskrnl / kd / kdmain.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/kd/kdmain.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 KPROCESSOR_MODE PreviousMode)
46 {
47 ULONG Result = 0;
48
49 switch (Service)
50 {
51 case BREAKPOINT_PRINT: /* DbgPrint */
52 Result = KdpPrintString(Buffer1, Buffer1Length, PreviousMode);
53 break;
54
55 #if DBG
56 case ' soR': /* ROS-INTERNAL */
57 {
58 switch ((ULONG_PTR)Buffer1)
59 {
60 case DumpAllThreads:
61 PspDumpThreads(TRUE);
62 break;
63
64 case DumpUserThreads:
65 PspDumpThreads(FALSE);
66 break;
67
68 case KdSpare3:
69 MmDumpArmPfnDatabase(FALSE);
70 break;
71
72 default:
73 break;
74 }
75 break;
76 }
77
78 #if defined(_M_IX86) && !defined(_WINKD_) // See ke/i386/traphdlr.c
79 /* Register a debug callback */
80 case 'CsoR':
81 {
82 switch (Buffer1Length)
83 {
84 case ID_Win32PreServiceHook:
85 KeWin32PreServiceHook = Buffer1;
86 break;
87
88 case ID_Win32PostServiceHook:
89 KeWin32PostServiceHook = Buffer1;
90 break;
91
92 }
93 break;
94 }
95 #endif
96
97 /* Special case for stack frame dumps */
98 case 'DsoR':
99 {
100 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length);
101 break;
102 }
103
104 #if defined(KDBG)
105 /* Register KDBG CLI callback */
106 case 'RbdK':
107 {
108 Result = KdbRegisterCliCallback(Buffer1, Buffer1Length);
109 break;
110 }
111 #endif /* KDBG */
112 #endif /* DBG */
113 default:
114 DPRINT1("Invalid debug service call!\n");
115 HalDisplayString("Invalid debug service call!\r\n");
116 break;
117 }
118
119 return Result;
120 }
121
122 BOOLEAN
123 NTAPI
124 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
125 IN PKEXCEPTION_FRAME ExceptionFrame,
126 IN PEXCEPTION_RECORD ExceptionRecord,
127 IN PCONTEXT Context,
128 IN KPROCESSOR_MODE PreviousMode,
129 IN BOOLEAN SecondChance)
130 {
131 KD_CONTINUE_TYPE Return = kdHandleException;
132 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
133
134 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
135 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
136 (ExceptionRecord->NumberParameters > 0) &&
137 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
138 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
139 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
140 (ExceptionCommand == BREAKPOINT_PRINT) ||
141 (ExceptionCommand == BREAKPOINT_PROMPT)))
142 {
143 /* Check if this is a debug print */
144 if (ExceptionCommand == BREAKPOINT_PRINT)
145 {
146 /* Print the string */
147 KdpServiceDispatcher(BREAKPOINT_PRINT,
148 (PVOID)ExceptionRecord->ExceptionInformation[1],
149 ExceptionRecord->ExceptionInformation[2],
150 PreviousMode);
151
152 /* Return success */
153 KeSetContextReturnRegister(Context, STATUS_SUCCESS);
154 }
155 #ifdef KDBG
156 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
157 {
158 PKD_SYMBOLS_INFO SymbolsInfo;
159 KD_SYMBOLS_INFO CapturedSymbolsInfo;
160 PLDR_DATA_TABLE_ENTRY LdrEntry;
161
162 SymbolsInfo = (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2];
163 if (PreviousMode != KernelMode)
164 {
165 _SEH2_TRY
166 {
167 ProbeForRead(SymbolsInfo,
168 sizeof(*SymbolsInfo),
169 1);
170 RtlCopyMemory(&CapturedSymbolsInfo,
171 SymbolsInfo,
172 sizeof(*SymbolsInfo));
173 SymbolsInfo = &CapturedSymbolsInfo;
174 }
175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
176 {
177 SymbolsInfo = NULL;
178 }
179 _SEH2_END;
180 }
181
182 if (SymbolsInfo != NULL)
183 {
184 /* Load symbols. Currently implemented only for KDBG! */
185 if (KdbpSymFindModule(SymbolsInfo->BaseOfDll, NULL, -1, &LdrEntry))
186 {
187 KdbSymProcessSymbols(LdrEntry);
188 }
189 }
190 }
191 else if (ExceptionCommand == BREAKPOINT_PROMPT)
192 {
193 ULONG ReturnValue;
194 LPSTR OutString;
195 USHORT OutStringLength;
196
197 /* Get the response string and length */
198 OutString = (LPSTR)Context->Ebx;
199 OutStringLength = (USHORT)Context->Edi;
200
201 /* Call KDBG */
202 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord->
203 ExceptionInformation[1],
204 (USHORT)ExceptionRecord->
205 ExceptionInformation[2],
206 OutString,
207 OutStringLength,
208 PreviousMode);
209
210 /* Return the number of characters that we received */
211 Context->Eax = ReturnValue;
212 }
213 #endif
214
215 /* This we can handle: simply bump the Program Counter */
216 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
217 return TRUE;
218 }
219
220 #ifdef KDBG
221 /* Check if this is an assertion failure */
222 if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE)
223 {
224 /* Bump EIP to the instruction following the int 2C */
225 Context->Eip += 2;
226 }
227 #endif
228
229 /* Get out of here if the Debugger isn't connected */
230 if (KdDebuggerNotPresent) return FALSE;
231
232 #ifdef KDBG
233 /* Call KDBG if available */
234 Return = KdbEnterDebuggerException(ExceptionRecord,
235 PreviousMode,
236 Context,
237 TrapFrame,
238 !SecondChance);
239 #else /* not KDBG */
240 if (WrapperInitRoutine)
241 {
242 /* Call GDB */
243 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
244 Context,
245 TrapFrame);
246 }
247 #endif /* not KDBG */
248
249 /* Debugger didn't handle it, please handle! */
250 if (Return == kdHandleException) return FALSE;
251
252 /* Debugger handled it */
253 return TRUE;
254 }
255
256 BOOLEAN
257 NTAPI
258 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
259 IN PEXCEPTION_RECORD ExceptionRecord,
260 IN PCONTEXT Context)
261 {
262 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
263
264 /* Get out of here if the Debugger isn't connected */
265 if (KdDebuggerNotPresent) return FALSE;
266
267 /* FIXME:
268 * Right now, the GDB wrapper seems to handle exceptions differntly
269 * from KDGB and both are called at different times, while the GDB
270 * one is only called once and that's it. I don't really have the knowledge
271 * to fix the GDB stub, so until then, we'll be using this hack
272 */
273 if (WrapperInitRoutine)
274 {
275 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
276 Context,
277 TrapFrame);
278 }
279
280 /* Debugger didn't handle it, please handle! */
281 if (Return == kdHandleException) return FALSE;
282
283 /* Debugger handled it */
284 return TRUE;
285 }
286
287 BOOLEAN
288 NTAPI
289 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
290 IN PCONTEXT Context,
291 IN KPROCESSOR_MODE PreviousMode)
292 {
293 /* KDBG has its own mechanism for ignoring user mode exceptions */
294 return FALSE;
295 }
296
297 /* PUBLIC FUNCTIONS *********************************************************/
298
299 /*
300 * @implemented
301 */
302 BOOLEAN
303 NTAPI
304 KdRefreshDebuggerNotPresent(VOID)
305 {
306 UNIMPLEMENTED;
307
308 /* Just return whatever was set previously -- FIXME! */
309 return KdDebuggerNotPresent;
310 }
311
312 /*
313 * @implemented
314 */
315 NTSTATUS
316 NTAPI
317 KdDisableDebugger(VOID)
318 {
319 KIRQL OldIrql;
320
321 /* Raise IRQL */
322 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
323
324 /* TODO: Disable any breakpoints */
325
326 /* Disable the Debugger */
327 KdDebuggerEnabled = FALSE;
328 SharedUserData->KdDebuggerEnabled = FALSE;
329
330 /* Lower the IRQL */
331 KeLowerIrql(OldIrql);
332
333 /* Return success */
334 return STATUS_SUCCESS;
335 }
336
337 /*
338 * @implemented
339 */
340 NTSTATUS
341 NTAPI
342 KdEnableDebugger(VOID)
343 {
344 KIRQL OldIrql;
345
346 /* Raise IRQL */
347 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
348
349 /* TODO: Re-enable any breakpoints */
350
351 /* Enable the Debugger */
352 KdDebuggerEnabled = TRUE;
353 SharedUserData->KdDebuggerEnabled = TRUE;
354
355 /* Lower the IRQL */
356 KeLowerIrql(OldIrql);
357
358 /* Return success */
359 return STATUS_SUCCESS;
360 }
361
362 /*
363 * @implemented
364 */
365 BOOLEAN
366 NTAPI
367 KdPollBreakIn(VOID)
368 {
369 return KdpBreakPending;
370 }
371
372 /*
373 * @unimplemented
374 */
375 NTSTATUS
376 NTAPI
377 KdPowerTransition(ULONG PowerState)
378 {
379 UNIMPLEMENTED;
380 return STATUS_NOT_IMPLEMENTED;
381 }
382
383 /*
384 * @unimplemented
385 */
386 NTSTATUS
387 NTAPI
388 KdChangeOption(IN KD_OPTION Option,
389 IN ULONG InBufferLength OPTIONAL,
390 IN PVOID InBuffer,
391 IN ULONG OutBufferLength OPTIONAL,
392 OUT PVOID OutBuffer,
393 OUT PULONG OutBufferRequiredLength OPTIONAL)
394 {
395 UNIMPLEMENTED;
396 return STATUS_NOT_IMPLEMENTED;
397 }
398
399
400 NTSTATUS
401 NTAPI
402 NtQueryDebugFilterState(IN ULONG ComponentId,
403 IN ULONG Level)
404 {
405 ULONG i;
406
407 /* Convert Level to mask if it isn't already one */
408 if (Level < 32)
409 Level = 1 << Level;
410
411 /* Check if it is not the default component */
412 if (ComponentId != MAXULONG)
413 {
414 /* No, search for an existing entry in the table */
415 for (i = 0; i < KdComponentTableEntries; i++)
416 {
417 /* Check if it is the right component */
418 if (ComponentId == KdComponentTable[i].ComponentId)
419 {
420 /* Check if mask are matching */
421 return (Level & KdComponentTable[i].Level) ? TRUE : FALSE;
422 }
423 }
424 }
425
426 /* Entry not found in the table, use default mask */
427 return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE;
428 }
429
430 NTSTATUS
431 NTAPI
432 NtSetDebugFilterState(IN ULONG ComponentId,
433 IN ULONG Level,
434 IN BOOLEAN State)
435 {
436 ULONG i;
437
438 /* Convert Level to mask if it isn't already one */
439 if (Level < 32)
440 Level = 1 << Level;
441 Level &= ~DPFLTR_MASK;
442
443 /* Check if it is the default component */
444 if (ComponentId == MAXULONG)
445 {
446 /* Yes, modify the default mask */
447 if (State)
448 Kd_DEFAULT_MASK |= Level;
449 else
450 Kd_DEFAULT_MASK &= ~Level;
451
452 return STATUS_SUCCESS;
453 }
454
455 /* Search for an existing entry */
456 for (i = 0; i < KdComponentTableEntries; i++ )
457 {
458 if (ComponentId == KdComponentTable[i].ComponentId)
459 break;
460 }
461
462 /* Check if we have found an existing entry */
463 if (i == KdComponentTableEntries)
464 {
465 /* Check if we have enough space in the table */
466 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
467 return STATUS_INVALID_PARAMETER_1;
468
469 /* Add a new entry */
470 ++KdComponentTableEntries;
471 KdComponentTable[i].ComponentId = ComponentId;
472 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
473 }
474
475 /* Update entry table */
476 if (State)
477 KdComponentTable[i].Level |= Level;
478 else
479 KdComponentTable[i].Level &= ~Level;
480
481 return STATUS_SUCCESS;
482 }
483
484 /*
485 * @unimplemented
486 */
487 NTSTATUS
488 NTAPI
489 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
490 IN PVOID InputBuffer,
491 IN ULONG InputBufferLength,
492 OUT PVOID OutputBuffer,
493 IN ULONG OutputBufferLength,
494 IN OUT PULONG ReturnLength,
495 IN KPROCESSOR_MODE PreviousMode)
496 {
497 /* HACK */
498 return KdpServiceDispatcher(Command,
499 InputBuffer,
500 InputBufferLength,
501 PreviousMode);
502 }
503
504 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
505
506 /* EOF */