xref: /reactos/ntoskrnl/kd/kdmain.c (revision 845faec4)
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 */
507