/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/kd64/kdtrap.c * PURPOSE: KD64 Trap Handlers * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Stefan Ginsberg (stefan.ginsberg@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include // // Retrieves the ComponentId and Level for BREAKPOINT_PRINT // and OutputString and OutputStringLength for BREAKPOINT_PROMPT. // #if defined(_X86_) // // EBX/EDI on x86 // #define KdpGetParameterThree(Context) ((Context)->Ebx) #define KdpGetParameterFour(Context) ((Context)->Edi) #elif defined(_AMD64_) // // R8/R9 on AMD64 // #define KdpGetParameterThree(Context) ((Context)->R8) #define KdpGetParameterFour(Context) ((Context)->R9) #elif defined(_ARM_) // // R3/R4 on ARM // #define KdpGetParameterThree(Context) ((Context)->R3) #define KdpGetParameterFour(Context) ((Context)->R4) #else #error Unsupported Architecture #endif /* FUNCTIONS *****************************************************************/ BOOLEAN NTAPI KdpReport(IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN SecondChanceException) { BOOLEAN Enable, Handled; PKPRCB Prcb; NTSTATUS ExceptionCode; /* * Determine whether to pass the exception to the debugger. * First, check if this is a "debug exception", meaning breakpoint * (including debug service), single step and assertion failure exceptions. */ ExceptionCode = ExceptionRecord->ExceptionCode; if ((ExceptionCode == STATUS_BREAKPOINT) || (ExceptionCode == STATUS_SINGLE_STEP) || (ExceptionCode == STATUS_ASSERTION_FAILURE)) { /* This is a debug exception; we always pass them to the debugger */ } else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION) { /* * Not a debug exception, but the stop-on-exception flag is set, * meaning the debugger requests that we pass it first chance * exceptions. However, some exceptions are always passed to the * exception handler first, namely exceptions with a code that isn't * an error or warning code, and also exceptions with the special * STATUS_PORT_DISCONNECTED code (an error code). */ if ((SecondChanceException == FALSE) && ((ExceptionCode == STATUS_PORT_DISCONNECTED) || (NT_SUCCESS(ExceptionCode)))) { /* Let the exception handler, if any, try to handle it */ return FALSE; } } else if (SecondChanceException == FALSE) { /* * This isn't a debug exception and the stop-on-exception flag isn't set, * so don't bother handling it */ return FALSE; } /* Enter the debugger */ Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); /* * Get the KPRCB and save the CPU Control State manually instead of * using KiSaveProcessorState, since we already have a valid CONTEXT. */ Prcb = KeGetCurrentPrcb(); KiSaveProcessorControlState(&Prcb->ProcessorState); KdpMoveMemory(&Prcb->ProcessorState.ContextFrame, ContextRecord, sizeof(CONTEXT)); /* Report the new state */ Handled = KdpReportExceptionStateChange(ExceptionRecord, &Prcb->ProcessorState. ContextFrame, SecondChanceException); /* Now restore the processor state, manually again. */ KdpMoveMemory(ContextRecord, &Prcb->ProcessorState.ContextFrame, sizeof(CONTEXT)); KiRestoreProcessorControlState(&Prcb->ProcessorState); /* Exit the debugger and clear the CTRL-C state */ KdExitDebugger(Enable); KdpControlCPressed = FALSE; return Handled; } BOOLEAN NTAPI KdpTrap(IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN SecondChanceException) { BOOLEAN Unload; ULONG_PTR ProgramCounter; BOOLEAN Handled; NTSTATUS ReturnStatus; USHORT ReturnLength; /* * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or * Load/Unload symbols. Make sure it isn't a software breakpoints as those * are handled by KdpReport. */ if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) { /* Save Program Counter */ ProgramCounter = KeGetContextPc(ContextRecord); /* Check what kind of operation was requested from us */ Unload = FALSE; switch (ExceptionRecord->ExceptionInformation[0]) { /* DbgPrint */ case BREAKPOINT_PRINT: /* Call the worker routine */ ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord), (ULONG)KdpGetParameterFour(ContextRecord), (LPSTR)ExceptionRecord->ExceptionInformation[1], (USHORT)ExceptionRecord->ExceptionInformation[2], PreviousMode, TrapFrame, ExceptionFrame, &Handled); /* Update the return value for the caller */ KeSetContextReturnRegister(ContextRecord, ReturnStatus); break; /* DbgPrompt */ case BREAKPOINT_PROMPT: /* Call the worker routine */ ReturnLength = KdpPrompt((LPSTR)ExceptionRecord->ExceptionInformation[1], (USHORT)ExceptionRecord->ExceptionInformation[2], (LPSTR)KdpGetParameterThree(ContextRecord), (USHORT)KdpGetParameterFour(ContextRecord), PreviousMode, TrapFrame, ExceptionFrame); Handled = TRUE; /* Update the return value for the caller */ KeSetContextReturnRegister(ContextRecord, ReturnLength); break; /* DbgUnLoadImageSymbols */ case BREAKPOINT_UNLOAD_SYMBOLS: /* Drop into the load case below, with the unload parameter */ Unload = TRUE; /* DbgLoadImageSymbols */ case BREAKPOINT_LOAD_SYMBOLS: /* Call the worker routine */ KdpSymbol((PSTRING)ExceptionRecord-> ExceptionInformation[1], (PKD_SYMBOLS_INFO)ExceptionRecord-> ExceptionInformation[2], Unload, PreviousMode, ContextRecord, TrapFrame, ExceptionFrame); Handled = TRUE; break; /* DbgCommandString */ case BREAKPOINT_COMMAND_STRING: /* Call the worker routine */ KdpCommandString((PSTRING)ExceptionRecord-> ExceptionInformation[1], (PSTRING)ExceptionRecord-> ExceptionInformation[2], PreviousMode, ContextRecord, TrapFrame, ExceptionFrame); Handled = TRUE; break; /* Anything else, do nothing */ default: /* Invalid debug service! Don't handle this! */ Handled = FALSE; break; } /* * If the PC was not updated, we'll increment it ourselves so execution * continues past the breakpoint. */ if (ProgramCounter == KeGetContextPc(ContextRecord)) { /* Update it */ KeSetContextPc(ContextRecord, ProgramCounter + KD_BREAKPOINT_SIZE); } } else { /* Call the worker routine */ Handled = KdpReport(TrapFrame, ExceptionFrame, ExceptionRecord, ContextRecord, PreviousMode, SecondChanceException); } /* Return TRUE or FALSE to caller */ return Handled; } BOOLEAN NTAPI KdpStub(IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN SecondChanceException) { ULONG_PTR ExceptionCommand; /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */ ExceptionCommand = ExceptionRecord->ExceptionInformation[0]; if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && (ExceptionRecord->NumberParameters > 0) && ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || (ExceptionCommand == BREAKPOINT_PRINT))) { /* This we can handle: simply bump the Program Counter */ KeSetContextPc(ContextRecord, KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE); return TRUE; } else if (KdPitchDebugger) { /* There's no debugger, fail. */ return FALSE; } else if ((KdAutoEnableOnEvent) && (KdPreviouslyEnabled) && !(KdDebuggerEnabled) && (NT_SUCCESS(KdEnableDebugger())) && (KdDebuggerEnabled)) { /* Debugging was Auto-Enabled. We can now send this to KD. */ return KdpTrap(TrapFrame, ExceptionFrame, ExceptionRecord, ContextRecord, PreviousMode, SecondChanceException); } else { /* FIXME: All we can do in this case is trace this exception */ return FALSE; } } BOOLEAN NTAPI KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT Context, IN KPROCESSOR_MODE PreviousMode) { /* * Determine if this is a valid debug service call and make sure that * it isn't a software breakpoint */ if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && (ExceptionRecord->NumberParameters > 0) && (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) { /* Then we have to handle it */ return TRUE; } else { /* We don't have to handle it */ return FALSE; } }