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 CODE_SEG("INIT") 57 VOID 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 ULONG64 UserRsp; 104 PKUSER_EXCEPTION_STACK UserStack; 105 106 /* Make sure we have a valid SS */ 107 if (TrapFrame->SegSs != (KGDT64_R3_DATA | RPL_MASK)) 108 { 109 /* Raise an access violation instead */ 110 LocalExceptRecord.ExceptionCode = STATUS_ACCESS_VIOLATION; 111 LocalExceptRecord.ExceptionFlags = 0; 112 LocalExceptRecord.NumberParameters = 0; 113 ExceptionRecord = &LocalExceptRecord; 114 } 115 116 /* Get new stack pointer and align it to 16 bytes */ 117 UserRsp = (Context->Rsp - sizeof(KUSER_EXCEPTION_STACK)) & ~15; 118 119 /* Get pointer to the usermode context, exception record and machine frame */ 120 UserStack = (PKUSER_EXCEPTION_STACK)UserRsp; 121 122 /* Set up the user-stack */ 123 _SEH2_TRY 124 { 125 /* Probe the user stack frame and zero it out */ 126 ProbeForWrite(UserStack, sizeof(*UserStack), TYPE_ALIGNMENT(KUSER_EXCEPTION_STACK)); 127 RtlZeroMemory(UserStack, sizeof(*UserStack)); 128 129 /* Copy Context and ExceptionFrame */ 130 UserStack->Context = *Context; 131 UserStack->ExceptionRecord = *ExceptionRecord; 132 133 /* Setup the machine frame */ 134 UserStack->MachineFrame.Rip = Context->Rip; 135 UserStack->MachineFrame.SegCs = Context->SegCs; 136 UserStack->MachineFrame.EFlags = Context->EFlags; 137 UserStack->MachineFrame.Rsp = Context->Rsp; 138 UserStack->MachineFrame.SegSs = Context->SegSs; 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)&UserStack->ExceptionRecord; 152 TrapFrame->Rdx = (ULONG64)&UserStack->Context; 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 /* Zero out the context to avoid leaking kernel stack memor to user mode */ 249 RtlZeroMemory(&Context, sizeof(Context)); 250 251 /* Set the context flags */ 252 Context.ContextFlags = CONTEXT_ALL; 253 254 /* Get the Context from the trap and exception frame */ 255 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context); 256 257 /* Look at our exception code */ 258 switch (ExceptionRecord->ExceptionCode) 259 { 260 /* Breakpoint */ 261 case STATUS_BREAKPOINT: 262 263 /* Decrement RIP by one */ 264 Context.Rip--; 265 break; 266 267 /* Internal exception */ 268 case KI_EXCEPTION_ACCESS_VIOLATION: 269 270 /* Set correct code */ 271 ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION; 272 if (PreviousMode == UserMode) 273 { 274 /* FIXME: Handle no execute */ 275 } 276 break; 277 } 278 279 /* Handle kernel-mode first, it's simpler */ 280 if (PreviousMode == KernelMode) 281 { 282 /* Check if this is a first-chance exception */ 283 if (FirstChance) 284 { 285 /* Break into the debugger for the first time */ 286 if (KiDebugRoutine(TrapFrame, 287 ExceptionFrame, 288 ExceptionRecord, 289 &Context, 290 PreviousMode, 291 FALSE)) 292 { 293 /* Exception was handled */ 294 goto Handled; 295 } 296 297 /* If the Debugger couldn't handle it, dispatch the exception */ 298 if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled; 299 } 300 301 /* This is a second-chance exception, only for the debugger */ 302 if (KiDebugRoutine(TrapFrame, 303 ExceptionFrame, 304 ExceptionRecord, 305 &Context, 306 PreviousMode, 307 TRUE)) 308 { 309 /* Exception was handled */ 310 goto Handled; 311 } 312 313 /* Third strike; you're out */ 314 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 315 ExceptionRecord->ExceptionCode, 316 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 317 (ULONG_PTR)TrapFrame, 318 0); 319 } 320 else 321 { 322 /* User mode exception, was it first-chance? */ 323 if (FirstChance) 324 { 325 /* 326 * Break into the kernel debugger unless a user mode debugger 327 * is present or user mode exceptions are ignored, except if this 328 * is a debug service which we must always pass to KD 329 */ 330 if ((!(PsGetCurrentProcess()->DebugPort) && 331 !(KdIgnoreUmExceptions)) || 332 (KdIsThisAKdTrap(ExceptionRecord, &Context, PreviousMode))) 333 { 334 /* Make sure the debugger can access debug directories */ 335 KiPrepareUserDebugData(); 336 337 /* Call the kernel debugger */ 338 if (KiDebugRoutine(TrapFrame, 339 ExceptionFrame, 340 ExceptionRecord, 341 &Context, 342 PreviousMode, 343 FALSE)) 344 { 345 /* Exception was handled */ 346 goto Handled; 347 } 348 } 349 350 /* Forward exception to user mode debugger */ 351 if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return; 352 353 /* Forward exception to user mode (does not return) */ 354 KiDispatchExceptionToUser(TrapFrame, &Context, ExceptionRecord); 355 NT_ASSERT(FALSE); 356 } 357 358 /* Try second chance */ 359 if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) 360 { 361 /* Handled, get out */ 362 return; 363 } 364 else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) 365 { 366 /* Handled, get out */ 367 return; 368 } 369 370 /* 3rd strike, kill the process */ 371 DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n", 372 PsGetCurrentProcess()->ImageFileName, 373 ExceptionRecord->ExceptionCode, 374 ExceptionRecord->ExceptionAddress, 375 PsGetCurrentProcess()->SectionBaseAddress); 376 377 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode); 378 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 379 ExceptionRecord->ExceptionCode, 380 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 381 (ULONG_PTR)TrapFrame, 382 0); 383 } 384 385 Handled: 386 /* Convert the context back into Trap/Exception Frames */ 387 KeContextToTrapFrame(&Context, 388 ExceptionFrame, 389 TrapFrame, 390 Context.ContextFlags, 391 PreviousMode); 392 return; 393 } 394 395 NTSTATUS 396 NTAPI 397 KeRaiseUserException(IN NTSTATUS ExceptionCode) 398 { 399 UNIMPLEMENTED; 400 return STATUS_UNSUCCESSFUL; 401 } 402 403 404 VOID 405 DECLSPEC_NORETURN 406 KiSystemFatalException(IN ULONG ExceptionCode, 407 IN PKTRAP_FRAME TrapFrame) 408 { 409 /* Bugcheck the system */ 410 KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP, 411 ExceptionCode, 412 0, 413 0, 414 0, 415 TrapFrame); 416 } 417 418 NTSTATUS 419 NTAPI 420 KiNpxNotAvailableFaultHandler( 421 IN PKTRAP_FRAME TrapFrame) 422 { 423 UNIMPLEMENTED; 424 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame); 425 return -1; 426 } 427 428 429 NTSTATUS 430 NTAPI 431 KiGeneralProtectionFaultHandler( 432 IN PKTRAP_FRAME TrapFrame) 433 { 434 PUCHAR Instructions; 435 436 /* Check for user-mode GPF */ 437 if (TrapFrame->SegCs & 3) 438 { 439 UNIMPLEMENTED; 440 ASSERT(FALSE); 441 } 442 443 /* Check for lazy segment load */ 444 if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK)) 445 { 446 /* Fix it */ 447 TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK); 448 return STATUS_SUCCESS; 449 } 450 else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK)) 451 { 452 /* Fix it */ 453 TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK); 454 return STATUS_SUCCESS; 455 } 456 457 /* Check for nested exception */ 458 if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) && 459 (TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler)) 460 { 461 /* Not implemented */ 462 UNIMPLEMENTED; 463 ASSERT(FALSE); 464 } 465 466 /* Get Instruction Pointer */ 467 Instructions = (PUCHAR)TrapFrame->Rip; 468 469 /* Check for IRET */ 470 if (Instructions[0] == 0x48 && Instructions[1] == 0xCF) 471 { 472 /* Not implemented */ 473 UNIMPLEMENTED; 474 ASSERT(FALSE); 475 } 476 477 /* Check for RDMSR/WRMSR */ 478 if ((Instructions[0] == 0xF) && // 2-byte opcode 479 ((Instructions[1] == 0x30) || // RDMSR 480 (Instructions[1] == 0x32))) // WRMSR 481 { 482 /* Unknown CPU MSR, so raise an access violation */ 483 return STATUS_ACCESS_VIOLATION; 484 } 485 486 ASSERT(FALSE); 487 return STATUS_UNSUCCESSFUL; 488 } 489 490 NTSTATUS 491 NTAPI 492 KiXmmExceptionHandler( 493 IN PKTRAP_FRAME TrapFrame) 494 { 495 UNIMPLEMENTED; 496 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame); 497 return -1; 498 } 499