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 /* Enable interrupts */ 123 _enable(); 124 125 /* Set up the user-stack */ 126 _SEH2_TRY 127 { 128 /* Probe the user stack frame and zero it out */ 129 ProbeForWrite(UserStack, sizeof(*UserStack), TYPE_ALIGNMENT(KUSER_EXCEPTION_STACK)); 130 RtlZeroMemory(UserStack, sizeof(*UserStack)); 131 132 /* Copy Context and ExceptionFrame */ 133 UserStack->Context = *Context; 134 UserStack->ExceptionRecord = *ExceptionRecord; 135 136 /* Setup the machine frame */ 137 UserStack->MachineFrame.Rip = Context->Rip; 138 UserStack->MachineFrame.SegCs = Context->SegCs; 139 UserStack->MachineFrame.EFlags = Context->EFlags; 140 UserStack->MachineFrame.Rsp = Context->Rsp; 141 UserStack->MachineFrame.SegSs = Context->SegSs; 142 } 143 _SEH2_EXCEPT((LocalExceptRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord), 144 EXCEPTION_EXECUTE_HANDLER) 145 { 146 // FIXME: handle stack overflow 147 148 /* Nothing we can do here */ 149 _disable(); 150 _SEH2_YIELD(return); 151 } 152 _SEH2_END; 153 154 /* Now set the two params for the user-mode dispatcher */ 155 TrapFrame->Rcx = (ULONG64)&UserStack->ExceptionRecord; 156 TrapFrame->Rdx = (ULONG64)&UserStack->Context; 157 158 /* Set new Stack Pointer */ 159 TrapFrame->Rsp = UserRsp; 160 161 /* Force correct segments */ 162 TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK; 163 TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK; 164 TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK; 165 TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK; 166 TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK; 167 TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK; 168 169 /* Set RIP to the User-mode Dispatcher */ 170 TrapFrame->Rip = (ULONG64)KeUserExceptionDispatcher; 171 172 _disable(); 173 174 /* Exit to usermode */ 175 KiServiceExit2(TrapFrame); 176 } 177 178 static 179 VOID 180 KiPageInDirectory(PVOID ImageBase, USHORT Directory) 181 { 182 volatile CHAR *Pointer; 183 ULONG Size; 184 185 /* Get a pointer to the debug directory */ 186 Pointer = RtlImageDirectoryEntryToData(ImageBase, 1, Directory, &Size); 187 if (!Pointer) return; 188 189 /* Loop all pages */ 190 while ((LONG)Size > 0) 191 { 192 /* Touch it, to page it in */ 193 (void)*Pointer; 194 Pointer += PAGE_SIZE; 195 Size -= PAGE_SIZE; 196 } 197 } 198 199 VOID 200 KiPrepareUserDebugData(void) 201 { 202 PLDR_DATA_TABLE_ENTRY LdrEntry; 203 PPEB_LDR_DATA PebLdr; 204 PLIST_ENTRY ListEntry; 205 PTEB Teb; 206 207 /* Get the Teb for this process */ 208 Teb = KeGetCurrentThread()->Teb; 209 if (!Teb) return; 210 211 /* Enable interrupts */ 212 _enable(); 213 214 _SEH2_TRY 215 { 216 /* Get a pointer to the loader data */ 217 PebLdr = Teb->ProcessEnvironmentBlock->Ldr; 218 if (!PebLdr) _SEH2_LEAVE; 219 220 /* Now loop all entries in the module list */ 221 for (ListEntry = PebLdr->InLoadOrderModuleList.Flink; 222 ListEntry != &PebLdr->InLoadOrderModuleList; 223 ListEntry = ListEntry->Flink) 224 { 225 /* Get the loader entry */ 226 LdrEntry = CONTAINING_RECORD(ListEntry, 227 LDR_DATA_TABLE_ENTRY, 228 InLoadOrderLinks); 229 230 KiPageInDirectory((PVOID)LdrEntry->DllBase, 231 IMAGE_DIRECTORY_ENTRY_DEBUG); 232 233 KiPageInDirectory((PVOID)LdrEntry->DllBase, 234 IMAGE_DIRECTORY_ENTRY_EXCEPTION); 235 } 236 237 } 238 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 239 { 240 } 241 _SEH2_END; 242 243 _disable(); 244 } 245 246 VOID 247 NTAPI 248 KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord, 249 IN PKEXCEPTION_FRAME ExceptionFrame, 250 IN PKTRAP_FRAME TrapFrame, 251 IN KPROCESSOR_MODE PreviousMode, 252 IN BOOLEAN FirstChance) 253 { 254 CONTEXT Context; 255 256 /* Increase number of Exception Dispatches */ 257 KeGetCurrentPrcb()->KeExceptionDispatchCount++; 258 259 /* Zero out the context to avoid leaking kernel stack memor to user mode */ 260 RtlZeroMemory(&Context, sizeof(Context)); 261 262 /* Set the context flags */ 263 Context.ContextFlags = CONTEXT_ALL; 264 265 /* Get the Context from the trap and exception frame */ 266 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context); 267 268 /* Look at our exception code */ 269 switch (ExceptionRecord->ExceptionCode) 270 { 271 /* Breakpoint */ 272 case STATUS_BREAKPOINT: 273 274 /* Decrement RIP by one */ 275 Context.Rip--; 276 break; 277 278 /* Internal exception */ 279 case KI_EXCEPTION_ACCESS_VIOLATION: 280 281 /* Set correct code */ 282 ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION; 283 if (PreviousMode == UserMode) 284 { 285 /* FIXME: Handle no execute */ 286 } 287 break; 288 } 289 290 /* Handle kernel-mode first, it's simpler */ 291 if (PreviousMode == KernelMode) 292 { 293 /* Check if this is a first-chance exception */ 294 if (FirstChance) 295 { 296 /* Break into the debugger for the first time */ 297 if (KiDebugRoutine(TrapFrame, 298 ExceptionFrame, 299 ExceptionRecord, 300 &Context, 301 PreviousMode, 302 FALSE)) 303 { 304 /* Exception was handled */ 305 goto Handled; 306 } 307 308 /* If the Debugger couldn't handle it, dispatch the exception */ 309 if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled; 310 } 311 312 /* This is a second-chance exception, only for the debugger */ 313 if (KiDebugRoutine(TrapFrame, 314 ExceptionFrame, 315 ExceptionRecord, 316 &Context, 317 PreviousMode, 318 TRUE)) 319 { 320 /* Exception was handled */ 321 goto Handled; 322 } 323 324 /* Third strike; you're out */ 325 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 326 ExceptionRecord->ExceptionCode, 327 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 328 (ULONG_PTR)TrapFrame, 329 0); 330 } 331 else 332 { 333 /* User mode exception, was it first-chance? */ 334 if (FirstChance) 335 { 336 /* 337 * Break into the kernel debugger unless a user mode debugger 338 * is present or user mode exceptions are ignored, except if this 339 * is a debug service which we must always pass to KD 340 */ 341 if ((!(PsGetCurrentProcess()->DebugPort) && 342 !(KdIgnoreUmExceptions)) || 343 (KdIsThisAKdTrap(ExceptionRecord, &Context, PreviousMode))) 344 { 345 /* Make sure the debugger can access debug directories */ 346 KiPrepareUserDebugData(); 347 348 /* Call the kernel debugger */ 349 if (KiDebugRoutine(TrapFrame, 350 ExceptionFrame, 351 ExceptionRecord, 352 &Context, 353 PreviousMode, 354 FALSE)) 355 { 356 /* Exception was handled */ 357 goto Handled; 358 } 359 } 360 361 /* Forward exception to user mode debugger */ 362 if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return; 363 364 /* Forward exception to user mode (does not return, if successful) */ 365 KiDispatchExceptionToUser(TrapFrame, &Context, ExceptionRecord); 366 367 /* Failed to dispatch, fall through for second chance handling */ 368 } 369 370 /* Try second chance */ 371 if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) 372 { 373 /* Handled, get out */ 374 return; 375 } 376 else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) 377 { 378 /* Handled, get out */ 379 return; 380 } 381 382 /* 3rd strike, kill the process */ 383 DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n", 384 PsGetCurrentProcess()->ImageFileName, 385 ExceptionRecord->ExceptionCode, 386 ExceptionRecord->ExceptionAddress, 387 PsGetCurrentProcess()->SectionBaseAddress); 388 389 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode); 390 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 391 ExceptionRecord->ExceptionCode, 392 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 393 (ULONG_PTR)TrapFrame, 394 0); 395 } 396 397 Handled: 398 /* Convert the context back into Trap/Exception Frames */ 399 KeContextToTrapFrame(&Context, 400 ExceptionFrame, 401 TrapFrame, 402 Context.ContextFlags, 403 PreviousMode); 404 return; 405 } 406 407 NTSTATUS 408 NTAPI 409 KeRaiseUserException(IN NTSTATUS ExceptionCode) 410 { 411 UNIMPLEMENTED; 412 return STATUS_UNSUCCESSFUL; 413 } 414 415 416 VOID 417 DECLSPEC_NORETURN 418 KiSystemFatalException(IN ULONG ExceptionCode, 419 IN PKTRAP_FRAME TrapFrame) 420 { 421 /* Bugcheck the system */ 422 KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP, 423 ExceptionCode, 424 0, 425 0, 426 0, 427 TrapFrame); 428 } 429 430 NTSTATUS 431 NTAPI 432 KiNpxNotAvailableFaultHandler( 433 IN PKTRAP_FRAME TrapFrame) 434 { 435 UNIMPLEMENTED; 436 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame); 437 return -1; 438 } 439 440 static 441 BOOLEAN 442 KiIsPrivilegedInstruction(PUCHAR Ip, BOOLEAN Wow64) 443 { 444 ULONG i; 445 446 /* Handle prefixes */ 447 for (i = 0; i < 15; i++) 448 { 449 if (!Wow64) 450 { 451 /* Check for REX prefix */ 452 if ((Ip[0] >= 0x40) && (Ip[0] <= 0x4F)) 453 { 454 Ip++; 455 continue; 456 } 457 } 458 459 switch (Ip[0]) 460 { 461 /* Check prefixes */ 462 case 0x26: // ES 463 case 0x2E: // CS / null 464 case 0x36: // SS 465 case 0x3E: // DS 466 case 0x64: // FS 467 case 0x65: // GS 468 case 0x66: // OP 469 case 0x67: // ADDR 470 case 0xF0: // LOCK 471 case 0xF2: // REP 472 case 0xF3: // REP INS/OUTS 473 Ip++; 474 continue; 475 } 476 477 break; 478 } 479 480 if (i == 15) 481 { 482 /* Too many prefixes. Should only happen, when the code was concurrently modified. */ 483 return FALSE; 484 } 485 486 switch (Ip[0]) 487 { 488 case 0xF4: // HLT 489 case 0xFA: // CLI 490 case 0xFB: // STI 491 return TRUE; 492 493 case 0x0F: 494 { 495 switch (Ip[1]) 496 { 497 case 0x06: // CLTS 498 case 0x07: // SYSRET 499 case 0x08: // INVD 500 case 0x09: // WBINVD 501 case 0x20: // MOV CR, XXX 502 case 0x21: // MOV DR, XXX 503 case 0x22: // MOV XXX, CR 504 case 0x23: // MOV YYY, DR 505 case 0x30: // WRMSR 506 case 0x32: // RDMSR 507 case 0x33: // RDPMC 508 case 0x35: // SYSEXIT 509 case 0x78: // VMREAD 510 case 0x79: // VMWRITE 511 return TRUE; 512 513 case 0x00: 514 { 515 /* Check MODRM Reg field */ 516 switch ((Ip[2] >> 3) & 0x7) 517 { 518 case 2: // LLDT 519 case 3: // LTR 520 return TRUE; 521 } 522 break; 523 } 524 525 case 0x01: 526 { 527 switch (Ip[2]) 528 { 529 case 0xC1: // VMCALL 530 case 0xC2: // VMLAUNCH 531 case 0xC3: // VMRESUME 532 case 0xC4: // VMXOFF 533 case 0xC8: // MONITOR 534 case 0xC9: // MWAIT 535 case 0xD1: // XSETBV 536 case 0xF8: // SWAPGS 537 return TRUE; 538 } 539 540 /* Check MODRM Reg field */ 541 switch ((Ip[2] >> 3) & 0x7) 542 { 543 case 2: // LGDT 544 case 3: // LIDT 545 case 6: // LMSW 546 case 7: // INVLPG / SWAPGS / RDTSCP 547 return TRUE; 548 } 549 break; 550 } 551 552 case 0x38: 553 { 554 switch (Ip[2]) 555 { 556 case 0x80: // INVEPT 557 case 0x81: // INVVPID 558 return TRUE; 559 } 560 break; 561 } 562 563 case 0xC7: 564 { 565 /* Check MODRM Reg field */ 566 switch ((Ip[2] >> 3) & 0x7) 567 { 568 case 0x06: // VMPTRLD, VMCLEAR, VMXON 569 case 0x07: // VMPTRST 570 return TRUE; 571 } 572 break; 573 } 574 } 575 576 break; 577 } 578 } 579 580 return FALSE; 581 } 582 583 static 584 NTSTATUS 585 KiGeneralProtectionFaultUserMode( 586 _In_ PKTRAP_FRAME TrapFrame) 587 { 588 BOOLEAN Wow64 = TrapFrame->SegCs == KGDT64_R3_CMCODE; 589 PUCHAR InstructionPointer; 590 NTSTATUS Status; 591 592 /* We need to decode the instruction at RIP */ 593 InstructionPointer = (PUCHAR)TrapFrame->Rip; 594 595 _SEH2_TRY 596 { 597 /* Probe the instruction address */ 598 ProbeForRead(InstructionPointer, 64, 1); 599 600 /* Check if it's a privileged instruction */ 601 if (KiIsPrivilegedInstruction(InstructionPointer, Wow64)) 602 { 603 Status = STATUS_PRIVILEGED_INSTRUCTION; 604 } 605 else 606 { 607 Status = STATUS_ACCESS_VIOLATION; 608 } 609 } 610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 611 { 612 Status = _SEH2_GetExceptionCode(); 613 } 614 _SEH2_END 615 616 return Status; 617 } 618 619 NTSTATUS 620 NTAPI 621 KiGeneralProtectionFaultHandler( 622 IN PKTRAP_FRAME TrapFrame) 623 { 624 PUCHAR Instructions; 625 626 /* Check for user-mode GPF */ 627 if (TrapFrame->SegCs & 3) 628 { 629 return KiGeneralProtectionFaultUserMode(TrapFrame); 630 } 631 632 /* Check for lazy segment load */ 633 if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK)) 634 { 635 /* Fix it */ 636 TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK); 637 return STATUS_SUCCESS; 638 } 639 else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK)) 640 { 641 /* Fix it */ 642 TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK); 643 return STATUS_SUCCESS; 644 } 645 646 /* Get Instruction Pointer */ 647 Instructions = (PUCHAR)TrapFrame->Rip; 648 649 /* Check for IRET */ 650 if (Instructions[0] == 0x48 && Instructions[1] == 0xCF) 651 { 652 /* Not implemented */ 653 UNIMPLEMENTED; 654 ASSERT(FALSE); 655 } 656 657 /* Check for RDMSR/WRMSR */ 658 if ((Instructions[0] == 0xF) && // 2-byte opcode 659 ((Instructions[1] == 0x30) || // RDMSR 660 (Instructions[1] == 0x32))) // WRMSR 661 { 662 /* Unknown CPU MSR, so raise an access violation */ 663 return STATUS_ACCESS_VIOLATION; 664 } 665 666 ASSERT(FALSE); 667 return STATUS_UNSUCCESSFUL; 668 } 669 670 NTSTATUS 671 NTAPI 672 KiXmmExceptionHandler( 673 IN PKTRAP_FRAME TrapFrame) 674 { 675 ULONG ExceptionCode; 676 677 if ((TrapFrame->MxCsr & _MM_EXCEPT_INVALID) && 678 !(TrapFrame->MxCsr & _MM_MASK_INVALID)) 679 { 680 /* Invalid operation */ 681 ExceptionCode = STATUS_FLOAT_INVALID_OPERATION; 682 } 683 else if ((TrapFrame->MxCsr & _MM_EXCEPT_DENORM) && 684 !(TrapFrame->MxCsr & _MM_MASK_DENORM)) 685 { 686 /* Denormalized operand. Yes, this is what Windows returns. */ 687 ExceptionCode = STATUS_FLOAT_INVALID_OPERATION; 688 } 689 else if ((TrapFrame->MxCsr & _MM_EXCEPT_DIV_ZERO) && 690 !(TrapFrame->MxCsr & _MM_MASK_DIV_ZERO)) 691 { 692 /* Divide by zero */ 693 ExceptionCode = STATUS_FLOAT_DIVIDE_BY_ZERO; 694 } 695 else if ((TrapFrame->MxCsr & _MM_EXCEPT_OVERFLOW) && 696 !(TrapFrame->MxCsr & _MM_MASK_OVERFLOW)) 697 { 698 /* Overflow */ 699 ExceptionCode = STATUS_FLOAT_OVERFLOW; 700 } 701 else if ((TrapFrame->MxCsr & _MM_EXCEPT_UNDERFLOW) && 702 !(TrapFrame->MxCsr & _MM_MASK_UNDERFLOW)) 703 { 704 /* Underflow */ 705 ExceptionCode = STATUS_FLOAT_UNDERFLOW; 706 } 707 else if ((TrapFrame->MxCsr & _MM_EXCEPT_INEXACT) && 708 !(TrapFrame->MxCsr & _MM_MASK_INEXACT)) 709 { 710 /* Precision */ 711 ExceptionCode = STATUS_FLOAT_INEXACT_RESULT; 712 } 713 else 714 { 715 /* Should not happen */ 716 ASSERT(FALSE); 717 } 718 719 return ExceptionCode; 720 } 721