1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/kd64/kdtrap.c 5 * PURPOSE: KD64 Trap Handlers 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Stefan Ginsberg (stefan.ginsberg@reactos.org) 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 // 17 // Retrieves the ComponentId and Level for BREAKPOINT_PRINT 18 // and OutputString and OutputStringLength for BREAKPOINT_PROMPT. 19 // 20 #if defined(_X86_) 21 22 // 23 // EBX/EDI on x86 24 // 25 #define KdpGetParameterThree(Context) ((Context)->Ebx) 26 #define KdpGetParameterFour(Context) ((Context)->Edi) 27 28 #elif defined(_AMD64_) 29 30 // 31 // R8/R9 on AMD64 32 // 33 #define KdpGetParameterThree(Context) ((Context)->R8) 34 #define KdpGetParameterFour(Context) ((Context)->R9) 35 36 #elif defined(_ARM_) 37 38 // 39 // R3/R4 on ARM 40 // 41 #define KdpGetParameterThree(Context) ((Context)->R3) 42 #define KdpGetParameterFour(Context) ((Context)->R4) 43 44 #else 45 #error Unsupported Architecture 46 #endif 47 48 /* FUNCTIONS *****************************************************************/ 49 50 BOOLEAN 51 NTAPI 52 KdpReport(IN PKTRAP_FRAME TrapFrame, 53 IN PKEXCEPTION_FRAME ExceptionFrame, 54 IN PEXCEPTION_RECORD ExceptionRecord, 55 IN PCONTEXT ContextRecord, 56 IN KPROCESSOR_MODE PreviousMode, 57 IN BOOLEAN SecondChanceException) 58 { 59 BOOLEAN Enable, Handled; 60 PKPRCB Prcb; 61 NTSTATUS ExceptionCode; 62 63 /* 64 * Determine whether to pass the exception to the debugger. 65 * First, check if this is a "debug exception", meaning breakpoint 66 * (including debug service), single step and assertion failure exceptions. 67 */ 68 ExceptionCode = ExceptionRecord->ExceptionCode; 69 if ((ExceptionCode == STATUS_BREAKPOINT) || 70 (ExceptionCode == STATUS_SINGLE_STEP) || 71 (ExceptionCode == STATUS_ASSERTION_FAILURE)) 72 { 73 /* This is a debug exception; we always pass them to the debugger */ 74 } 75 else if (NtGlobalFlag & FLG_STOP_ON_EXCEPTION) 76 { 77 /* 78 * Not a debug exception, but the stop-on-exception flag is set, 79 * meaning the debugger requests that we pass it first chance 80 * exceptions. However, some exceptions are always passed to the 81 * exception handler first, namely exceptions with a code that isn't 82 * an error or warning code, and also exceptions with the special 83 * STATUS_PORT_DISCONNECTED code (an error code). 84 */ 85 if ((SecondChanceException == FALSE) && 86 ((ExceptionCode == STATUS_PORT_DISCONNECTED) || 87 (NT_SUCCESS(ExceptionCode)))) 88 { 89 /* Let the exception handler, if any, try to handle it */ 90 return FALSE; 91 } 92 } 93 else if (SecondChanceException == FALSE) 94 { 95 /* 96 * This isn't a debug exception and the stop-on-exception flag isn't set, 97 * so don't bother handling it 98 */ 99 return FALSE; 100 } 101 102 /* Enter the debugger */ 103 Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); 104 105 /* 106 * Get the KPRCB and save the CPU Control State manually instead of 107 * using KiSaveProcessorState, since we already have a valid CONTEXT. 108 */ 109 Prcb = KeGetCurrentPrcb(); 110 KiSaveProcessorControlState(&Prcb->ProcessorState); 111 KdpMoveMemory(&Prcb->ProcessorState.ContextFrame, 112 ContextRecord, 113 sizeof(CONTEXT)); 114 115 /* Report the new state */ 116 Handled = KdpReportExceptionStateChange(ExceptionRecord, 117 &Prcb->ProcessorState. 118 ContextFrame, 119 SecondChanceException); 120 121 /* Now restore the processor state, manually again. */ 122 KdpMoveMemory(ContextRecord, 123 &Prcb->ProcessorState.ContextFrame, 124 sizeof(CONTEXT)); 125 KiRestoreProcessorControlState(&Prcb->ProcessorState); 126 127 /* Exit the debugger and clear the CTRL-C state */ 128 KdExitDebugger(Enable); 129 KdpControlCPressed = FALSE; 130 return Handled; 131 } 132 133 BOOLEAN 134 NTAPI 135 KdpTrap(IN PKTRAP_FRAME TrapFrame, 136 IN PKEXCEPTION_FRAME ExceptionFrame, 137 IN PEXCEPTION_RECORD ExceptionRecord, 138 IN PCONTEXT ContextRecord, 139 IN KPROCESSOR_MODE PreviousMode, 140 IN BOOLEAN SecondChanceException) 141 { 142 BOOLEAN Unload; 143 ULONG_PTR ProgramCounter; 144 BOOLEAN Handled; 145 NTSTATUS ReturnStatus; 146 USHORT ReturnLength; 147 148 /* 149 * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or 150 * Load/Unload symbols. Make sure it isn't a software breakpoint as those 151 * are handled by KdpReport. 152 */ 153 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 154 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) 155 { 156 /* Save Program Counter */ 157 ProgramCounter = KeGetContextPc(ContextRecord); 158 159 /* Check what kind of operation was requested from us */ 160 Unload = FALSE; 161 switch (ExceptionRecord->ExceptionInformation[0]) 162 { 163 /* DbgPrint */ 164 case BREAKPOINT_PRINT: 165 166 /* Call the worker routine */ 167 ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord), 168 (ULONG)KdpGetParameterFour(ContextRecord), 169 (PCHAR)ExceptionRecord->ExceptionInformation[1], 170 (USHORT)ExceptionRecord->ExceptionInformation[2], 171 PreviousMode, 172 TrapFrame, 173 ExceptionFrame, 174 &Handled); 175 176 /* Update the return value for the caller */ 177 KeSetContextReturnRegister(ContextRecord, ReturnStatus); 178 break; 179 180 /* DbgPrompt */ 181 case BREAKPOINT_PROMPT: 182 183 /* Call the worker routine */ 184 ReturnLength = KdpPrompt((PCHAR)ExceptionRecord->ExceptionInformation[1], 185 (USHORT)ExceptionRecord->ExceptionInformation[2], 186 (PCHAR)KdpGetParameterThree(ContextRecord), 187 (USHORT)KdpGetParameterFour(ContextRecord), 188 PreviousMode, 189 TrapFrame, 190 ExceptionFrame); 191 Handled = TRUE; 192 193 /* Update the return value for the caller */ 194 KeSetContextReturnRegister(ContextRecord, ReturnLength); 195 break; 196 197 /* DbgUnLoadImageSymbols */ 198 case BREAKPOINT_UNLOAD_SYMBOLS: 199 200 /* Drop into the load case below, with the unload parameter */ 201 Unload = TRUE; 202 203 /* DbgLoadImageSymbols */ 204 case BREAKPOINT_LOAD_SYMBOLS: 205 206 /* Call the worker routine */ 207 KdpSymbol((PSTRING)ExceptionRecord->ExceptionInformation[1], 208 (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2], 209 Unload, 210 PreviousMode, 211 ContextRecord, 212 TrapFrame, 213 ExceptionFrame); 214 Handled = TRUE; 215 break; 216 217 /* DbgCommandString */ 218 case BREAKPOINT_COMMAND_STRING: 219 220 /* Call the worker routine */ 221 KdpCommandString((PSTRING)ExceptionRecord->ExceptionInformation[1], 222 (PSTRING)ExceptionRecord->ExceptionInformation[2], 223 PreviousMode, 224 ContextRecord, 225 TrapFrame, 226 ExceptionFrame); 227 Handled = TRUE; 228 break; 229 230 /* Anything else, do nothing */ 231 default: 232 233 /* Invalid debug service! Don't handle this! */ 234 Handled = FALSE; 235 break; 236 } 237 238 /* 239 * If the PC was not updated, we'll increment it ourselves so execution 240 * continues past the breakpoint. 241 */ 242 if (ProgramCounter == KeGetContextPc(ContextRecord)) 243 { 244 /* Update it */ 245 KeSetContextPc(ContextRecord, 246 ProgramCounter + KD_BREAKPOINT_SIZE); 247 } 248 } 249 else 250 { 251 /* Call the worker routine */ 252 Handled = KdpReport(TrapFrame, 253 ExceptionFrame, 254 ExceptionRecord, 255 ContextRecord, 256 PreviousMode, 257 SecondChanceException); 258 } 259 260 /* Return TRUE or FALSE to caller */ 261 return Handled; 262 } 263 264 BOOLEAN 265 NTAPI 266 KdpStub(IN PKTRAP_FRAME TrapFrame, 267 IN PKEXCEPTION_FRAME ExceptionFrame, 268 IN PEXCEPTION_RECORD ExceptionRecord, 269 IN PCONTEXT ContextRecord, 270 IN KPROCESSOR_MODE PreviousMode, 271 IN BOOLEAN SecondChanceException) 272 { 273 ULONG_PTR ExceptionCommand; 274 275 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */ 276 ExceptionCommand = ExceptionRecord->ExceptionInformation[0]; 277 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 278 (ExceptionRecord->NumberParameters > 0) && 279 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || 280 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || 281 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || 282 (ExceptionCommand == BREAKPOINT_PRINT))) 283 { 284 /* This we can handle: simply bump the Program Counter */ 285 KeSetContextPc(ContextRecord, 286 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE); 287 return TRUE; 288 } 289 else if (KdPitchDebugger) 290 { 291 /* There's no debugger, fail. */ 292 return FALSE; 293 } 294 else if ((KdAutoEnableOnEvent) && 295 (KdPreviouslyEnabled) && 296 !(KdDebuggerEnabled) && 297 (NT_SUCCESS(KdEnableDebugger())) && 298 (KdDebuggerEnabled)) 299 { 300 /* Debugging was Auto-Enabled. We can now send this to KD. */ 301 return KdpTrap(TrapFrame, 302 ExceptionFrame, 303 ExceptionRecord, 304 ContextRecord, 305 PreviousMode, 306 SecondChanceException); 307 } 308 else 309 { 310 /* FIXME: All we can do in this case is trace this exception */ 311 return FALSE; 312 } 313 } 314 315 BOOLEAN 316 NTAPI 317 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, 318 IN PCONTEXT Context, 319 IN KPROCESSOR_MODE PreviousMode) 320 { 321 #ifdef _WINKD_ 322 /* 323 * Determine if this is a valid debug service call and make sure that 324 * it isn't a software breakpoint 325 */ 326 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 327 (ExceptionRecord->NumberParameters > 0) && 328 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) 329 { 330 /* Then we have to handle it */ 331 return TRUE; 332 } 333 else 334 { 335 /* We don't have to handle it */ 336 return FALSE; 337 } 338 #else 339 /* KDBG has its own mechanism for ignoring user mode exceptions */ 340 return FALSE; 341 #endif 342 } 343