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 KIRQL OldIrql = DISPATCH_LEVEL; 148 149 /* Raise if we have to. */ 150 if (KeGetCurrentIrql() < DISPATCH_LEVEL) 151 OldIrql = KeRaiseIrqlToDpcLevel(); 152 153 /* 154 * Check if we got a STATUS_BREAKPOINT with a SubID for Print, Prompt or 155 * Load/Unload symbols. Make sure it isn't a software breakpoints as those 156 * are handled by KdpReport. 157 */ 158 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 159 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) 160 { 161 /* Save Program Counter */ 162 ProgramCounter = KeGetContextPc(ContextRecord); 163 164 /* Check what kind of operation was requested from us */ 165 Unload = FALSE; 166 switch (ExceptionRecord->ExceptionInformation[0]) 167 { 168 /* DbgPrint */ 169 case BREAKPOINT_PRINT: 170 171 /* Call the worker routine */ 172 ReturnStatus = KdpPrint((ULONG)KdpGetParameterThree(ContextRecord), 173 (ULONG)KdpGetParameterFour(ContextRecord), 174 (PCHAR)ExceptionRecord->ExceptionInformation[1], 175 (USHORT)ExceptionRecord->ExceptionInformation[2], 176 PreviousMode, 177 TrapFrame, 178 ExceptionFrame, 179 &Handled); 180 181 /* Update the return value for the caller */ 182 KeSetContextReturnRegister(ContextRecord, ReturnStatus); 183 break; 184 185 /* DbgPrompt */ 186 case BREAKPOINT_PROMPT: 187 188 /* Call the worker routine */ 189 ReturnLength = KdpPrompt((PCHAR)ExceptionRecord->ExceptionInformation[1], 190 (USHORT)ExceptionRecord->ExceptionInformation[2], 191 (PCHAR)KdpGetParameterThree(ContextRecord), 192 (USHORT)KdpGetParameterFour(ContextRecord), 193 PreviousMode, 194 TrapFrame, 195 ExceptionFrame); 196 Handled = TRUE; 197 198 /* Update the return value for the caller */ 199 KeSetContextReturnRegister(ContextRecord, ReturnLength); 200 break; 201 202 /* DbgUnLoadImageSymbols */ 203 case BREAKPOINT_UNLOAD_SYMBOLS: 204 205 /* Drop into the load case below, with the unload parameter */ 206 Unload = TRUE; 207 208 /* DbgLoadImageSymbols */ 209 case BREAKPOINT_LOAD_SYMBOLS: 210 211 /* Call the worker routine */ 212 KdpSymbol((PSTRING)ExceptionRecord->ExceptionInformation[1], 213 (PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2], 214 Unload, 215 PreviousMode, 216 ContextRecord, 217 TrapFrame, 218 ExceptionFrame); 219 Handled = TRUE; 220 break; 221 222 /* DbgCommandString */ 223 case BREAKPOINT_COMMAND_STRING: 224 225 /* Call the worker routine */ 226 KdpCommandString((PSTRING)ExceptionRecord->ExceptionInformation[1], 227 (PSTRING)ExceptionRecord->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 if (OldIrql < DISPATCH_LEVEL) 266 KeLowerIrql(OldIrql); 267 268 /* Return TRUE or FALSE to caller */ 269 return Handled; 270 } 271 272 BOOLEAN 273 NTAPI 274 KdpStub(IN PKTRAP_FRAME TrapFrame, 275 IN PKEXCEPTION_FRAME ExceptionFrame, 276 IN PEXCEPTION_RECORD ExceptionRecord, 277 IN PCONTEXT ContextRecord, 278 IN KPROCESSOR_MODE PreviousMode, 279 IN BOOLEAN SecondChanceException) 280 { 281 ULONG_PTR ExceptionCommand; 282 283 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */ 284 ExceptionCommand = ExceptionRecord->ExceptionInformation[0]; 285 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 286 (ExceptionRecord->NumberParameters > 0) && 287 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) || 288 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) || 289 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) || 290 (ExceptionCommand == BREAKPOINT_PRINT))) 291 { 292 /* This we can handle: simply bump the Program Counter */ 293 KeSetContextPc(ContextRecord, 294 KeGetContextPc(ContextRecord) + KD_BREAKPOINT_SIZE); 295 return TRUE; 296 } 297 else if (KdPitchDebugger) 298 { 299 /* There's no debugger, fail. */ 300 return FALSE; 301 } 302 else if ((KdAutoEnableOnEvent) && 303 (KdPreviouslyEnabled) && 304 !(KdDebuggerEnabled) && 305 (NT_SUCCESS(KdEnableDebugger())) && 306 (KdDebuggerEnabled)) 307 { 308 /* Debugging was Auto-Enabled. We can now send this to KD. */ 309 return KdpTrap(TrapFrame, 310 ExceptionFrame, 311 ExceptionRecord, 312 ContextRecord, 313 PreviousMode, 314 SecondChanceException); 315 } 316 else 317 { 318 /* FIXME: All we can do in this case is trace this exception */ 319 return FALSE; 320 } 321 } 322 323 BOOLEAN 324 NTAPI 325 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord, 326 IN PCONTEXT Context, 327 IN KPROCESSOR_MODE PreviousMode) 328 { 329 #ifdef _WINKD_ 330 /* 331 * Determine if this is a valid debug service call and make sure that 332 * it isn't a software breakpoint 333 */ 334 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && 335 (ExceptionRecord->NumberParameters > 0) && 336 (ExceptionRecord->ExceptionInformation[0] != BREAKPOINT_BREAK)) 337 { 338 /* Then we have to handle it */ 339 return TRUE; 340 } 341 else 342 { 343 /* We don't have to handle it */ 344 return FALSE; 345 } 346 #else 347 /* KDBG has its own mechanism for ignoring user mode exceptions */ 348 return FALSE; 349 #endif 350 } 351