/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel * FILE: ntoskrnl/kd/kdmain.c * PURPOSE: Kernel Debugger Initialization * * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) */ #include #define NDEBUG #include /* VARIABLES ***************************************************************/ BOOLEAN KdDebuggerEnabled = FALSE; BOOLEAN KdEnteredDebugger = FALSE; BOOLEAN KdDebuggerNotPresent = TRUE; BOOLEAN KdBreakAfterSymbolLoad = FALSE; BOOLEAN KdpBreakPending = FALSE; BOOLEAN KdPitchDebugger = TRUE; BOOLEAN KdIgnoreUmExceptions = FALSE; KD_CONTEXT KdpContext; ULONG Kd_WIN2000_Mask; VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads); typedef struct { ULONG ComponentId; ULONG Level; } KD_COMPONENT_DATA; #define MAX_KD_COMPONENT_TABLE_ENTRIES 128 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES]; ULONG KdComponentTableEntries = 0; ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL; /* PRIVATE FUNCTIONS *********************************************************/ ULONG NTAPI KdpServiceDispatcher(ULONG Service, PVOID Buffer1, ULONG Buffer1Length, KPROCESSOR_MODE PreviousMode) { ULONG Result = 0; switch (Service) { case BREAKPOINT_PRINT: /* DbgPrint */ Result = KdpPrintString(Buffer1, Buffer1Length, PreviousMode); break; #if DBG case ' soR': /* ROS-INTERNAL */ { switch ((ULONG_PTR)Buffer1) { case DumpAllThreads: PspDumpThreads(TRUE); break; case DumpUserThreads: PspDumpThreads(FALSE); break; case KdSpare3: MmDumpArmPfnDatabase(FALSE); break; default: break; } break; } #if defined(_M_IX86) && !defined(_WINKD_) // See ke/i386/traphdlr.c /* Register a debug callback */ case 'CsoR': { switch (Buffer1Length) { case ID_Win32PreServiceHook: KeWin32PreServiceHook = Buffer1; break; case ID_Win32PostServiceHook: KeWin32PostServiceHook = Buffer1; break; } break; } #endif /* Special case for stack frame dumps */ case 'DsoR': { KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length); break; } #if defined(KDBG) /* Register KDBG CLI callback */ case 'RbdK': { Result = KdbRegisterCliCallback(Buffer1, Buffer1Length); break; } #endif /* KDBG */ #endif /* DBG */ default: DPRINT1("Invalid debug service call!\n"); HalDisplayString("Invalid debug service call!\r\n"); break; } return Result; } BOOLEAN NTAPI KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT Context, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN SecondChance) { KD_CONTINUE_TYPE Return = kdHandleException; ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0]; /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */ if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && (ExceptionRecord->NumberParameters > 0) && ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || (ExceptionCommand == BREAKPOINT_PRINT) || (ExceptionCommand == BREAKPOINT_PROMPT))) { /* Check if this is a debug print */ if (ExceptionCommand == BREAKPOINT_PRINT) { /* Print the string */ KdpServiceDispatcher(BREAKPOINT_PRINT, (PVOID)ExceptionRecord->ExceptionInformation[1], ExceptionRecord->ExceptionInformation[2], PreviousMode); /* Return success */ KeSetContextReturnRegister(Context, STATUS_SUCCESS); } #ifdef KDBG else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) { PKD_SYMBOLS_INFO SymbolsInfo; KD_SYMBOLS_INFO CapturedSymbolsInfo; PLDR_DATA_TABLE_ENTRY LdrEntry; SymbolsInfo = (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2]; if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForRead(SymbolsInfo, sizeof(*SymbolsInfo), 1); RtlCopyMemory(&CapturedSymbolsInfo, SymbolsInfo, sizeof(*SymbolsInfo)); SymbolsInfo = &CapturedSymbolsInfo; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SymbolsInfo = NULL; } _SEH2_END; } if (SymbolsInfo != NULL) { /* Load symbols. Currently implemented only for KDBG! */ if (KdbpSymFindModule(SymbolsInfo->BaseOfDll, NULL, -1, &LdrEntry)) { KdbSymProcessSymbols(LdrEntry); } } } else if (ExceptionCommand == BREAKPOINT_PROMPT) { ULONG ReturnValue; LPSTR OutString; USHORT OutStringLength; /* Get the response string and length */ OutString = (LPSTR)Context->Ebx; OutStringLength = (USHORT)Context->Edi; /* Call KDBG */ ReturnValue = KdpPrompt((LPSTR)ExceptionRecord-> ExceptionInformation[1], (USHORT)ExceptionRecord-> ExceptionInformation[2], OutString, OutStringLength, PreviousMode); /* Return the number of characters that we received */ Context->Eax = ReturnValue; } #endif /* This we can handle: simply bump the Program Counter */ KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE); return TRUE; } #ifdef KDBG /* Check if this is an assertion failure */ if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE) { /* Bump EIP to the instruction following the int 2C */ Context->Eip += 2; } #endif /* Get out of here if the Debugger isn't connected */ if (KdDebuggerNotPresent) return FALSE; #ifdef KDBG /* Call KDBG if available */ Return = KdbEnterDebuggerException(ExceptionRecord, PreviousMode, Context, TrapFrame, !SecondChance); #else /* not KDBG */ if (WrapperInitRoutine) { /* Call GDB */ Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord, Context, TrapFrame); } #endif /* not KDBG */ /* Debugger didn't handle it, please handle! */ if (Return == kdHandleException) return FALSE; /* Debugger handled it */ return TRUE; } BOOLEAN NTAPI KdpCallGdb(IN PKTRAP_FRAME TrapFrame, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT Context) { KD_CONTINUE_TYPE Return = kdDoNotHandleException; /* Get out of here if the Debugger isn't connected */ if (KdDebuggerNotPresent) return FALSE; /* FIXME: * Right now, the GDB wrapper seems to handle exceptions differntly * from KDGB and both are called at different times, while the GDB * one is only called once and that's it. I don't really have the knowledge * to fix the GDB stub, so until then, we'll be using this hack */ if (WrapperInitRoutine) { Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord, Context, TrapFrame); } /* Debugger didn't handle it, please handle! */ if (Return == kdHandleException) return FALSE; /* Debugger handled it */ return TRUE; } BOOLEAN NTAPI KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT Context, IN KPROCESSOR_MODE PreviousMode) { /* KDBG has its own mechanism for ignoring user mode exceptions */ return FALSE; } /* PUBLIC FUNCTIONS *********************************************************/ /* * @implemented */ BOOLEAN NTAPI KdRefreshDebuggerNotPresent(VOID) { UNIMPLEMENTED; /* Just return whatever was set previously -- FIXME! */ return KdDebuggerNotPresent; } /* * @implemented */ NTSTATUS NTAPI KdDisableDebugger(VOID) { KIRQL OldIrql; /* Raise IRQL */ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); /* TODO: Disable any breakpoints */ /* Disable the Debugger */ KdDebuggerEnabled = FALSE; SharedUserData->KdDebuggerEnabled = FALSE; /* Lower the IRQL */ KeLowerIrql(OldIrql); /* Return success */ return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS NTAPI KdEnableDebugger(VOID) { KIRQL OldIrql; /* Raise IRQL */ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); /* TODO: Re-enable any breakpoints */ /* Enable the Debugger */ KdDebuggerEnabled = TRUE; SharedUserData->KdDebuggerEnabled = TRUE; /* Lower the IRQL */ KeLowerIrql(OldIrql); /* Return success */ return STATUS_SUCCESS; } /* * @implemented */ BOOLEAN NTAPI KdPollBreakIn(VOID) { return KdpBreakPending; } /* * @unimplemented */ NTSTATUS NTAPI KdPowerTransition(ULONG PowerState) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @unimplemented */ NTSTATUS NTAPI KdChangeOption(IN KD_OPTION Option, IN ULONG InBufferLength OPTIONAL, IN PVOID InBuffer, IN ULONG OutBufferLength OPTIONAL, OUT PVOID OutBuffer, OUT PULONG OutBufferRequiredLength OPTIONAL) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } NTSTATUS NTAPI NtQueryDebugFilterState(IN ULONG ComponentId, IN ULONG Level) { ULONG i; /* Convert Level to mask if it isn't already one */ if (Level < 32) Level = 1 << Level; /* Check if it is not the default component */ if (ComponentId != MAXULONG) { /* No, search for an existing entry in the table */ for (i = 0; i < KdComponentTableEntries; i++) { /* Check if it is the right component */ if (ComponentId == KdComponentTable[i].ComponentId) { /* Check if mask are matching */ return (Level & KdComponentTable[i].Level) ? TRUE : FALSE; } } } /* Entry not found in the table, use default mask */ return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE; } NTSTATUS NTAPI NtSetDebugFilterState(IN ULONG ComponentId, IN ULONG Level, IN BOOLEAN State) { ULONG i; /* Convert Level to mask if it isn't already one */ if (Level < 32) Level = 1 << Level; Level &= ~DPFLTR_MASK; /* Check if it is the default component */ if (ComponentId == MAXULONG) { /* Yes, modify the default mask */ if (State) Kd_DEFAULT_MASK |= Level; else Kd_DEFAULT_MASK &= ~Level; return STATUS_SUCCESS; } /* Search for an existing entry */ for (i = 0; i < KdComponentTableEntries; i++ ) { if (ComponentId == KdComponentTable[i].ComponentId) break; } /* Check if we have found an existing entry */ if (i == KdComponentTableEntries) { /* Check if we have enough space in the table */ if (i == MAX_KD_COMPONENT_TABLE_ENTRIES) return STATUS_INVALID_PARAMETER_1; /* Add a new entry */ ++KdComponentTableEntries; KdComponentTable[i].ComponentId = ComponentId; KdComponentTable[i].Level = Kd_DEFAULT_MASK; } /* Update entry table */ if (State) KdComponentTable[i].Level |= Level; else KdComponentTable[i].Level &= ~Level; return STATUS_SUCCESS; } /* * @unimplemented */ NTSTATUS NTAPI KdSystemDebugControl(IN SYSDBG_COMMAND Command, IN PVOID InputBuffer, IN ULONG InputBufferLength, OUT PVOID OutputBuffer, IN ULONG OutputBufferLength, IN OUT PULONG ReturnLength, IN KPROCESSOR_MODE PreviousMode) { /* HACK */ return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength, PreviousMode); } PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException; /* EOF */