1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ke/amd64/except.c 5 * PURPOSE: Exception Dispatching for amd64 6 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org) 7 * Alex Ionescu (alex.ionescu@reactos.org) 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRange[256]; 17 18 /* GLOBALS *******************************************************************/ 19 20 KIDT_INIT KiInterruptInitTable[] = 21 { 22 /* Id, Dpl, IST, ServiceRoutine */ 23 {0x00, 0x00, 0x00, KiDivideErrorFault}, 24 {0x01, 0x00, 0x00, KiDebugTrapOrFault}, 25 {0x02, 0x00, 0x03, KiNmiInterrupt}, 26 {0x03, 0x03, 0x00, KiBreakpointTrap}, 27 {0x04, 0x03, 0x00, KiOverflowTrap}, 28 {0x05, 0x00, 0x00, KiBoundFault}, 29 {0x06, 0x00, 0x00, KiInvalidOpcodeFault}, 30 {0x07, 0x00, 0x00, KiNpxNotAvailableFault}, 31 {0x08, 0x00, 0x01, KiDoubleFaultAbort}, 32 {0x09, 0x00, 0x00, KiNpxSegmentOverrunAbort}, 33 {0x0A, 0x00, 0x00, KiInvalidTssFault}, 34 {0x0B, 0x00, 0x00, KiSegmentNotPresentFault}, 35 {0x0C, 0x00, 0x00, KiStackFault}, 36 {0x0D, 0x00, 0x00, KiGeneralProtectionFault}, 37 {0x0E, 0x00, 0x00, KiPageFault}, 38 {0x10, 0x00, 0x00, KiFloatingErrorFault}, 39 {0x11, 0x00, 0x00, KiAlignmentFault}, 40 {0x12, 0x00, 0x02, KiMcheckAbort}, 41 {0x13, 0x00, 0x00, KiXmmException}, 42 {0x1F, 0x00, 0x00, KiApcInterrupt}, 43 {0x2C, 0x03, 0x00, KiRaiseAssertion}, 44 {0x2D, 0x03, 0x00, KiDebugServiceTrap}, 45 {0x2F, 0x00, 0x00, KiDpcInterrupt}, 46 {0xE1, 0x00, 0x00, KiIpiInterrupt}, 47 {0, 0, 0, 0} 48 }; 49 50 KIDTENTRY64 KiIdt[256]; 51 KDESCRIPTOR KiIdtDescriptor = {{0}, sizeof(KiIdt) - 1, KiIdt}; 52 53 54 /* FUNCTIONS *****************************************************************/ 55 56 VOID 57 INIT_FUNCTION 58 NTAPI 59 KeInitExceptions(VOID) 60 { 61 int i, j; 62 63 /* Initialize the Idt */ 64 for (j = i = 0; i < 256; i++) 65 { 66 ULONG64 Offset; 67 68 if (KiInterruptInitTable[j].InterruptId == i) 69 { 70 Offset = (ULONG64)KiInterruptInitTable[j].ServiceRoutine; 71 KiIdt[i].Dpl = KiInterruptInitTable[j].Dpl; 72 KiIdt[i].IstIndex = KiInterruptInitTable[j].IstIndex; 73 j++; 74 } 75 else 76 { 77 Offset = (ULONG64)&KiUnexpectedRange[i]._Op_push; 78 KiIdt[i].Dpl = 0; 79 KiIdt[i].IstIndex = 0; 80 } 81 KiIdt[i].OffsetLow = Offset & 0xffff; 82 KiIdt[i].Selector = KGDT64_R0_CODE; 83 KiIdt[i].Type = 0x0e; 84 KiIdt[i].Reserved0 = 0; 85 KiIdt[i].Present = 1; 86 KiIdt[i].OffsetMiddle = (Offset >> 16) & 0xffff; 87 KiIdt[i].OffsetHigh = (Offset >> 32); 88 KiIdt[i].Reserved1 = 0; 89 } 90 91 KeGetPcr()->IdtBase = KiIdt; 92 __lidt(&KiIdtDescriptor.Limit); 93 } 94 95 static 96 VOID 97 KiDispatchExceptionToUser( 98 IN PKTRAP_FRAME TrapFrame, 99 IN PCONTEXT Context, 100 IN PEXCEPTION_RECORD ExceptionRecord) 101 { 102 EXCEPTION_RECORD LocalExceptRecord; 103 ULONG Size; 104 ULONG64 UserRsp; 105 PCONTEXT UserContext; 106 PEXCEPTION_RECORD UserExceptionRecord; 107 108 /* Make sure we have a valid SS */ 109 if (TrapFrame->SegSs != (KGDT64_R3_DATA | RPL_MASK)) 110 { 111 /* Raise an access violation instead */ 112 LocalExceptRecord.ExceptionCode = STATUS_ACCESS_VIOLATION; 113 LocalExceptRecord.ExceptionFlags = 0; 114 LocalExceptRecord.NumberParameters = 0; 115 ExceptionRecord = &LocalExceptRecord; 116 } 117 118 /* Calculate the size of the exception record */ 119 Size = FIELD_OFFSET(EXCEPTION_RECORD, ExceptionInformation) + 120 ExceptionRecord->NumberParameters * sizeof(ULONG64); 121 122 /* Get new stack pointer and align it to 16 bytes */ 123 UserRsp = (Context->Rsp - Size - sizeof(CONTEXT)) & ~15; 124 125 /* Get pointers to the usermode context and exception record */ 126 UserContext = (PVOID)UserRsp; 127 UserExceptionRecord = (PVOID)(UserRsp + sizeof(CONTEXT)); 128 129 /* Set up the user-stack */ 130 _SEH2_TRY 131 { 132 /* Probe stack and copy Context */ 133 ProbeForWrite(UserContext, sizeof(CONTEXT), sizeof(ULONG64)); 134 *UserContext = *Context; 135 136 /* Probe stack and copy exception record */ 137 ProbeForWrite(UserExceptionRecord, Size, sizeof(ULONG64)); 138 *UserExceptionRecord = *ExceptionRecord; 139 } 140 _SEH2_EXCEPT((LocalExceptRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord), 141 EXCEPTION_EXECUTE_HANDLER) 142 { 143 // FIXME: handle stack overflow 144 145 /* Nothing we can do here */ 146 _SEH2_YIELD(return); 147 } 148 _SEH2_END; 149 150 /* Now set the two params for the user-mode dispatcher */ 151 TrapFrame->Rcx = (ULONG64)UserContext; 152 TrapFrame->Rdx = (ULONG64)UserExceptionRecord; 153 154 /* Set new Stack Pointer */ 155 TrapFrame->Rsp = UserRsp; 156 157 /* Force correct segments */ 158 TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK; 159 TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK; 160 TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK; 161 TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK; 162 TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK; 163 TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK; 164 165 /* Set RIP to the User-mode Dispatcher */ 166 TrapFrame->Rip = (ULONG64)KeUserExceptionDispatcher; 167 168 /* Exit to usermode */ 169 KiServiceExit2(TrapFrame); 170 } 171 172 static 173 VOID 174 KiPageInDirectory(PVOID ImageBase, USHORT Directory) 175 { 176 volatile CHAR *Pointer; 177 ULONG Size; 178 179 /* Get a pointer to the debug directory */ 180 Pointer = RtlImageDirectoryEntryToData(ImageBase, 1, Directory, &Size); 181 if (!Pointer) return; 182 183 /* Loop all pages */ 184 while ((LONG)Size > 0) 185 { 186 /* Touch it, to page it in */ 187 (void)*Pointer; 188 Pointer += PAGE_SIZE; 189 Size -= PAGE_SIZE; 190 } 191 } 192 193 VOID 194 KiPrepareUserDebugData(void) 195 { 196 PLDR_DATA_TABLE_ENTRY LdrEntry; 197 PPEB_LDR_DATA PebLdr; 198 PLIST_ENTRY ListEntry; 199 PTEB Teb; 200 201 /* Get the Teb for this process */ 202 Teb = KeGetCurrentThread()->Teb; 203 if (!Teb) return; 204 205 _SEH2_TRY 206 { 207 /* Get a pointer to the loader data */ 208 PebLdr = Teb->ProcessEnvironmentBlock->Ldr; 209 if (!PebLdr) _SEH2_YIELD(return); 210 211 /* Now loop all entries in the module list */ 212 for (ListEntry = PebLdr->InLoadOrderModuleList.Flink; 213 ListEntry != &PebLdr->InLoadOrderModuleList; 214 ListEntry = ListEntry->Flink) 215 { 216 /* Get the loader entry */ 217 LdrEntry = CONTAINING_RECORD(ListEntry, 218 LDR_DATA_TABLE_ENTRY, 219 InLoadOrderLinks); 220 221 KiPageInDirectory((PVOID)LdrEntry->DllBase, 222 IMAGE_DIRECTORY_ENTRY_DEBUG); 223 224 KiPageInDirectory((PVOID)LdrEntry->DllBase, 225 IMAGE_DIRECTORY_ENTRY_EXCEPTION); 226 } 227 228 } 229 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 230 { 231 } 232 _SEH2_END; 233 } 234 235 VOID 236 NTAPI 237 KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord, 238 IN PKEXCEPTION_FRAME ExceptionFrame, 239 IN PKTRAP_FRAME TrapFrame, 240 IN KPROCESSOR_MODE PreviousMode, 241 IN BOOLEAN FirstChance) 242 { 243 CONTEXT Context; 244 245 /* Increase number of Exception Dispatches */ 246 KeGetCurrentPrcb()->KeExceptionDispatchCount++; 247 248 /* Set the context flags */ 249 Context.ContextFlags = CONTEXT_ALL; 250 251 /* Get a Context */ 252 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context); 253 254 /* Look at our exception code */ 255 switch (ExceptionRecord->ExceptionCode) 256 { 257 /* Breakpoint */ 258 case STATUS_BREAKPOINT: 259 260 /* Decrement RIP by one */ 261 Context.Rip--; 262 break; 263 264 /* Internal exception */ 265 case KI_EXCEPTION_ACCESS_VIOLATION: 266 267 /* Set correct code */ 268 ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION; 269 if (PreviousMode == UserMode) 270 { 271 /* FIXME: Handle no execute */ 272 } 273 break; 274 } 275 276 /* Handle kernel-mode first, it's simpler */ 277 if (PreviousMode == KernelMode) 278 { 279 /* Check if this is a first-chance exception */ 280 if (FirstChance) 281 { 282 /* Break into the debugger for the first time */ 283 if (KiDebugRoutine(TrapFrame, 284 ExceptionFrame, 285 ExceptionRecord, 286 &Context, 287 PreviousMode, 288 FALSE)) 289 { 290 /* Exception was handled */ 291 goto Handled; 292 } 293 294 /* If the Debugger couldn't handle it, dispatch the exception */ 295 if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled; 296 } 297 298 /* This is a second-chance exception, only for the debugger */ 299 if (KiDebugRoutine(TrapFrame, 300 ExceptionFrame, 301 ExceptionRecord, 302 &Context, 303 PreviousMode, 304 TRUE)) 305 { 306 /* Exception was handled */ 307 goto Handled; 308 } 309 310 /* Third strike; you're out */ 311 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 312 ExceptionRecord->ExceptionCode, 313 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 314 (ULONG_PTR)TrapFrame, 315 0); 316 } 317 else 318 { 319 /* User mode exception, was it first-chance? */ 320 if (FirstChance) 321 { 322 /* 323 * Break into the kernel debugger unless a user mode debugger 324 * is present or user mode exceptions are ignored, except if this 325 * is a debug service which we must always pass to KD 326 */ 327 if ((!(PsGetCurrentProcess()->DebugPort) && 328 !(KdIgnoreUmExceptions)) || 329 (KdIsThisAKdTrap(ExceptionRecord, &Context, PreviousMode))) 330 { 331 /* Make sure the debugger can access debug directories */ 332 KiPrepareUserDebugData(); 333 334 /* Call the kernel debugger */ 335 if (KiDebugRoutine(TrapFrame, 336 ExceptionFrame, 337 ExceptionRecord, 338 &Context, 339 PreviousMode, 340 FALSE)) 341 { 342 /* Exception was handled */ 343 goto Handled; 344 } 345 } 346 347 /* Forward exception to user mode debugger */ 348 if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return; 349 350 //KiDispatchExceptionToUser() 351 __debugbreak(); 352 } 353 354 /* Try second chance */ 355 if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) 356 { 357 /* Handled, get out */ 358 return; 359 } 360 else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) 361 { 362 /* Handled, get out */ 363 return; 364 } 365 366 /* 3rd strike, kill the process */ 367 DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n", 368 PsGetCurrentProcess()->ImageFileName, 369 ExceptionRecord->ExceptionCode, 370 ExceptionRecord->ExceptionAddress, 371 PsGetCurrentProcess()->SectionBaseAddress); 372 373 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode); 374 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 375 ExceptionRecord->ExceptionCode, 376 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 377 (ULONG_PTR)TrapFrame, 378 0); 379 } 380 381 Handled: 382 /* Convert the context back into Trap/Exception Frames */ 383 KeContextToTrapFrame(&Context, 384 ExceptionFrame, 385 TrapFrame, 386 Context.ContextFlags, 387 PreviousMode); 388 return; 389 } 390 391 NTSTATUS 392 NTAPI 393 KeRaiseUserException(IN NTSTATUS ExceptionCode) 394 { 395 UNIMPLEMENTED; 396 return STATUS_UNSUCCESSFUL; 397 } 398 399 400 VOID 401 DECLSPEC_NORETURN 402 KiSystemFatalException(IN ULONG ExceptionCode, 403 IN PKTRAP_FRAME TrapFrame) 404 { 405 /* Bugcheck the system */ 406 KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP, 407 ExceptionCode, 408 0, 409 0, 410 0, 411 TrapFrame); 412 } 413 414 NTSTATUS 415 NTAPI 416 KiNpxNotAvailableFaultHandler( 417 IN PKTRAP_FRAME TrapFrame) 418 { 419 UNIMPLEMENTED; 420 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame); 421 return -1; 422 } 423 424 425 NTSTATUS 426 NTAPI 427 KiGeneralProtectionFaultHandler( 428 IN PKTRAP_FRAME TrapFrame) 429 { 430 PUCHAR Instructions; 431 432 /* Check for user-mode GPF */ 433 if (TrapFrame->SegCs & 3) 434 { 435 UNIMPLEMENTED; 436 ASSERT(FALSE); 437 } 438 439 /* Check for lazy segment load */ 440 if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK)) 441 { 442 /* Fix it */ 443 TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK); 444 return STATUS_SUCCESS; 445 } 446 else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK)) 447 { 448 /* Fix it */ 449 TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK); 450 return STATUS_SUCCESS; 451 } 452 453 /* Check for nested exception */ 454 if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) && 455 (TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler)) 456 { 457 /* Not implemented */ 458 UNIMPLEMENTED; 459 ASSERT(FALSE); 460 } 461 462 /* Get Instruction Pointer */ 463 Instructions = (PUCHAR)TrapFrame->Rip; 464 465 /* Check for IRET */ 466 if (Instructions[0] == 0x48 && Instructions[1] == 0xCF) 467 { 468 /* Not implemented */ 469 UNIMPLEMENTED; 470 ASSERT(FALSE); 471 } 472 473 /* Check for RDMSR/WRMSR */ 474 if ((Instructions[0] == 0xF) && // 2-byte opcode 475 ((Instructions[1] == 0x30) || // RDMSR 476 (Instructions[1] == 0x32))) // WRMSR 477 { 478 /* Unknown CPU MSR, so raise an access violation */ 479 return STATUS_ACCESS_VIOLATION; 480 } 481 482 ASSERT(FALSE); 483 return STATUS_UNSUCCESSFUL; 484 } 485 486 NTSTATUS 487 NTAPI 488 KiXmmExceptionHandler( 489 IN PKTRAP_FRAME TrapFrame) 490 { 491 UNIMPLEMENTED; 492 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame); 493 return -1; 494 } 495