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 breakpoints 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 (LPSTR)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, 178 ReturnStatus); 179 break; 180 181 /* DbgPrompt */ 182 case BREAKPOINT_PROMPT: 183 184 /* Call the worker routine */ 185 ReturnLength = KdpPrompt((LPSTR)ExceptionRecord->ExceptionInformation[1], 186 (USHORT)ExceptionRecord->ExceptionInformation[2], 187 (LPSTR)KdpGetParameterThree(ContextRecord), 188 (USHORT)KdpGetParameterFour(ContextRecord), 189 PreviousMode, 190 TrapFrame, 191 ExceptionFrame); 192 Handled = TRUE; 193 194 /* Update the return value for the caller */ 195 KeSetContextReturnRegister(ContextRecord, ReturnLength); 196 break; 197 198 /* DbgUnLoadImageSymbols */ 199 case BREAKPOINT_UNLOAD_SYMBOLS: 200 201 /* Drop into the load case below, with the unload parameter */ 202 Unload = TRUE; 203 204 /* DbgLoadImageSymbols */ 205 case BREAKPOINT_LOAD_SYMBOLS: 206 207 /* Call the worker routine */ 208 KdpSymbol((PSTRING)ExceptionRecord-> 209 ExceptionInformation[1], 210 (PKD_SYMBOLS_INFO)ExceptionRecord-> 211 ExceptionInformation[2], 212 Unload, 213 PreviousMode, 214 ContextRecord, 215 TrapFrame, 216 ExceptionFrame); 217 Handled = TRUE; 218 break; 219 220 /* DbgCommandString */ 221 case BREAKPOINT_COMMAND_STRING: 222 223 /* Call the worker routine */ 224 KdpCommandString((PSTRING)ExceptionRecord-> 225 ExceptionInformation[1], 226 (PSTRING)ExceptionRecord-> 227 ExceptionInformation[2], 228 PreviousMode, 229 ContextRecord, 230 TrapFrame, 231 ExceptionFrame); 232 Handled = TRUE; 233 break; 234 235 /* Anything else, do nothing */ 236 default: 237 238 /* Invalid debug service! Don't handle this! */ 239 Handled = FALSE; 240 break; 241 } 242 243 /* 244 * If the PC was not updated, we'll increment it ourselves so execution 245 * continues past the breakpoint. 246 */ 247 if (ProgramCounter == KeGetContextPc(ContextRecord)) 248 { 249 /* Update it */ 250 KeSetContextPc(ContextRecord, 251 ProgramCounter + KD_BREAKPOINT_SIZE); 252 } 253 } 254 else 255 { 256 /* Call the worker routine */ 257 Handled = KdpReport(TrapFrame, 258 ExceptionFrame, 259 ExceptionRecord, 260 ContextRecord, 261 PreviousMode, 262 SecondChanceException); 263 } 264 265 /* Return TRUE or FALSE to caller */ 266 return Handled; 267 } 268 269 BOOLEAN 270 NTAPI 271 KdpStub(IN PKTRAP_FRAME TrapFrame, 272 IN PKEXCEPTION_FRAME ExceptionFrame, 273 IN PEXCEPTION_RECORD ExceptionRecord, 274 IN PCONTEXT ContextRecord, 275 IN KPROCESSOR_MODE PreviousMode, 276 IN BOOLEAN SecondChanceException) 277 { 278 ULONG_PTR ExceptionCommand; 279 280 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */ 281 ExceptionCommand = ExceptionRecord->ExceptionInformation[0]; 282 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 283 (ExceptionRecord->NumberParameters > 0) && 284 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || 285 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || 286 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || 287 (ExceptionCommand == BREAKPOINT_PRINT))) 288 { 289 /* This we can handle: simply bump the Program Counter */ 290 KeSetContextPc(ContextRecord, 291 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE); 292 return TRUE; 293 } 294 else if (KdPitchDebugger) 295 { 296 /* There's no debugger, fail. */ 297 return FALSE; 298 } 299 else if ((KdAutoEnableOnEvent) && 300 (KdPreviouslyEnabled) && 301 !(KdDebuggerEnabled) && 302 (NT_SUCCESS(KdEnableDebugger())) && 303 (KdDebuggerEnabled)) 304 { 305 /* Debugging was Auto-Enabled. We can now send this to KD. */ 306 return KdpTrap(TrapFrame, 307 ExceptionFrame, 308 ExceptionRecord, 309 ContextRecord, 310 PreviousMode, 311 SecondChanceException); 312 } 313 else 314 { 315 /* FIXME: All we can do in this case is trace this exception */ 316 return FALSE; 317 } 318 } 319 320 BOOLEAN 321 NTAPI 322 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, 323 IN PCONTEXT Context, 324 IN KPROCESSOR_MODE PreviousMode) 325 { 326 /* 327 * Determine if this is a valid debug service call and make sure that 328 * it isn't a software breakpoint 329 */ 330 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 331 (ExceptionRecord->NumberParameters > 0) && 332 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) 333 { 334 /* Then we have to handle it */ 335 return TRUE; 336 } 337 else 338 { 339 /* We don't have to handle it */ 340 return FALSE; 341 } 342 } 343