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 BOOLEAN 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 return FALSE; 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 return TRUE; 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 */ 365 if (KiDispatchExceptionToUser(TrapFrame, &Context, ExceptionRecord)) 366 { 367 /* Success, the exception will be handled by KiUserExceptionDispatcher */ 368 return; 369 } 370 371 /* Failed to dispatch, fall through for second chance handling */ 372 } 373 374 /* Try second chance */ 375 if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) 376 { 377 /* Handled, get out */ 378 return; 379 } 380 else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) 381 { 382 /* Handled, get out */ 383 return; 384 } 385 386 /* 3rd strike, kill the process */ 387 DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n", 388 PsGetCurrentProcess()->ImageFileName, 389 ExceptionRecord->ExceptionCode, 390 ExceptionRecord->ExceptionAddress, 391 PsGetCurrentProcess()->SectionBaseAddress); 392 393 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode); 394 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED, 395 ExceptionRecord->ExceptionCode, 396 (ULONG_PTR)ExceptionRecord->ExceptionAddress, 397 (ULONG_PTR)TrapFrame, 398 0); 399 } 400 401 Handled: 402 /* Convert the context back into Trap/Exception Frames */ 403 KeContextToTrapFrame(&Context, 404 ExceptionFrame, 405 TrapFrame, 406 Context.ContextFlags, 407 PreviousMode); 408 return; 409 } 410 411 NTSTATUS 412 NTAPI 413 KeRaiseUserException(IN NTSTATUS ExceptionCode) 414 { 415 UNIMPLEMENTED; 416 return STATUS_UNSUCCESSFUL; 417 } 418 419 420 VOID 421 DECLSPEC_NORETURN 422 KiSystemFatalException(IN ULONG ExceptionCode, 423 IN PKTRAP_FRAME TrapFrame) 424 { 425 /* Bugcheck the system */ 426 KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP, 427 ExceptionCode, 428 0, 429 0, 430 0, 431 TrapFrame); 432 } 433 434 NTSTATUS 435 NTAPI 436 KiNpxNotAvailableFaultHandler( 437 IN PKTRAP_FRAME TrapFrame) 438 { 439 UNIMPLEMENTED; 440 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame); 441 return -1; 442 } 443 444 static 445 BOOLEAN 446 KiIsPrivilegedInstruction(PUCHAR Ip, BOOLEAN Wow64) 447 { 448 ULONG i; 449 450 /* Handle prefixes */ 451 for (i = 0; i < 15; i++) 452 { 453 if (!Wow64) 454 { 455 /* Check for REX prefix */ 456 if ((Ip[0] >= 0x40) && (Ip[0] <= 0x4F)) 457 { 458 Ip++; 459 continue; 460 } 461 } 462 463 switch (Ip[0]) 464 { 465 /* Check prefixes */ 466 case 0x26: // ES 467 case 0x2E: // CS / null 468 case 0x36: // SS 469 case 0x3E: // DS 470 case 0x64: // FS 471 case 0x65: // GS 472 case 0x66: // OP 473 case 0x67: // ADDR 474 case 0xF0: // LOCK 475 case 0xF2: // REP 476 case 0xF3: // REP INS/OUTS 477 Ip++; 478 continue; 479 } 480 481 break; 482 } 483 484 if (i == 15) 485 { 486 /* Too many prefixes. Should only happen, when the code was concurrently modified. */ 487 return FALSE; 488 } 489 490 switch (Ip[0]) 491 { 492 case 0xF4: // HLT 493 case 0xFA: // CLI 494 case 0xFB: // STI 495 return TRUE; 496 497 case 0x0F: 498 { 499 switch (Ip[1]) 500 { 501 case 0x06: // CLTS 502 case 0x07: // SYSRET 503 case 0x08: // INVD 504 case 0x09: // WBINVD 505 case 0x20: // MOV CR, XXX 506 case 0x21: // MOV DR, XXX 507 case 0x22: // MOV XXX, CR 508 case 0x23: // MOV YYY, DR 509 case 0x30: // WRMSR 510 case 0x32: // RDMSR 511 case 0x33: // RDPMC 512 case 0x35: // SYSEXIT 513 case 0x78: // VMREAD 514 case 0x79: // VMWRITE 515 return TRUE; 516 517 case 0x00: 518 { 519 /* Check MODRM Reg field */ 520 switch ((Ip[2] >> 3) & 0x7) 521 { 522 case 2: // LLDT 523 case 3: // LTR 524 return TRUE; 525 } 526 break; 527 } 528 529 case 0x01: 530 { 531 switch (Ip[2]) 532 { 533 case 0xC1: // VMCALL 534 case 0xC2: // VMLAUNCH 535 case 0xC3: // VMRESUME 536 case 0xC4: // VMXOFF 537 case 0xC8: // MONITOR 538 case 0xC9: // MWAIT 539 case 0xD1: // XSETBV 540 case 0xF8: // SWAPGS 541 return TRUE; 542 } 543 544 /* Check MODRM Reg field */ 545 switch ((Ip[2] >> 3) & 0x7) 546 { 547 case 2: // LGDT 548 case 3: // LIDT 549 case 6: // LMSW 550 case 7: // INVLPG / SWAPGS / RDTSCP 551 return TRUE; 552 } 553 break; 554 } 555 556 case 0x38: 557 { 558 switch (Ip[2]) 559 { 560 case 0x80: // INVEPT 561 case 0x81: // INVVPID 562 return TRUE; 563 } 564 break; 565 } 566 567 case 0xC7: 568 { 569 /* Check MODRM Reg field */ 570 switch ((Ip[2] >> 3) & 0x7) 571 { 572 case 0x06: // VMPTRLD, VMCLEAR, VMXON 573 case 0x07: // VMPTRST 574 return TRUE; 575 } 576 break; 577 } 578 } 579 580 break; 581 } 582 } 583 584 return FALSE; 585 } 586 587 static 588 NTSTATUS 589 KiGeneralProtectionFaultUserMode( 590 _In_ PKTRAP_FRAME TrapFrame) 591 { 592 BOOLEAN Wow64 = TrapFrame->SegCs == KGDT64_R3_CMCODE; 593 PUCHAR InstructionPointer; 594 NTSTATUS Status; 595 596 /* We need to decode the instruction at RIP */ 597 InstructionPointer = (PUCHAR)TrapFrame->Rip; 598 599 _SEH2_TRY 600 { 601 /* Probe the instruction address */ 602 ProbeForRead(InstructionPointer, 64, 1); 603 604 /* Check if it's a privileged instruction */ 605 if (KiIsPrivilegedInstruction(InstructionPointer, Wow64)) 606 { 607 Status = STATUS_PRIVILEGED_INSTRUCTION; 608 } 609 else 610 { 611 Status = STATUS_ACCESS_VIOLATION; 612 } 613 } 614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 615 { 616 Status = _SEH2_GetExceptionCode(); 617 } 618 _SEH2_END 619 620 return Status; 621 } 622 623 NTSTATUS 624 NTAPI 625 KiGeneralProtectionFaultHandler( 626 IN PKTRAP_FRAME TrapFrame) 627 { 628 PUCHAR Instructions; 629 630 /* Check for user-mode GPF */ 631 if (TrapFrame->SegCs & 3) 632 { 633 return KiGeneralProtectionFaultUserMode(TrapFrame); 634 } 635 636 /* Check for lazy segment load */ 637 if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK)) 638 { 639 /* Fix it */ 640 TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK); 641 return STATUS_SUCCESS; 642 } 643 else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK)) 644 { 645 /* Fix it */ 646 TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK); 647 return STATUS_SUCCESS; 648 } 649 650 /* Get Instruction Pointer */ 651 Instructions = (PUCHAR)TrapFrame->Rip; 652 653 /* Check for IRET */ 654 if (Instructions[0] == 0x48 && Instructions[1] == 0xCF) 655 { 656 /* Not implemented */ 657 UNIMPLEMENTED; 658 ASSERT(FALSE); 659 } 660 661 /* Check for RDMSR/WRMSR */ 662 if ((Instructions[0] == 0xF) && // 2-byte opcode 663 ((Instructions[1] == 0x30) || // RDMSR 664 (Instructions[1] == 0x32))) // WRMSR 665 { 666 /* Unknown CPU MSR, so raise an access violation */ 667 return STATUS_ACCESS_VIOLATION; 668 } 669 670 ASSERT(FALSE); 671 return STATUS_UNSUCCESSFUL; 672 } 673 674 NTSTATUS 675 NTAPI 676 KiXmmExceptionHandler( 677 IN PKTRAP_FRAME TrapFrame) 678 { 679 ULONG ExceptionCode; 680 681 if ((TrapFrame->MxCsr & _MM_EXCEPT_INVALID) && 682 !(TrapFrame->MxCsr & _MM_MASK_INVALID)) 683 { 684 /* Invalid operation */ 685 ExceptionCode = STATUS_FLOAT_INVALID_OPERATION; 686 } 687 else if ((TrapFrame->MxCsr & _MM_EXCEPT_DENORM) && 688 !(TrapFrame->MxCsr & _MM_MASK_DENORM)) 689 { 690 /* Denormalized operand. Yes, this is what Windows returns. */ 691 ExceptionCode = STATUS_FLOAT_INVALID_OPERATION; 692 } 693 else if ((TrapFrame->MxCsr & _MM_EXCEPT_DIV_ZERO) && 694 !(TrapFrame->MxCsr & _MM_MASK_DIV_ZERO)) 695 { 696 /* Divide by zero */ 697 ExceptionCode = STATUS_FLOAT_DIVIDE_BY_ZERO; 698 } 699 else if ((TrapFrame->MxCsr & _MM_EXCEPT_OVERFLOW) && 700 !(TrapFrame->MxCsr & _MM_MASK_OVERFLOW)) 701 { 702 /* Overflow */ 703 ExceptionCode = STATUS_FLOAT_OVERFLOW; 704 } 705 else if ((TrapFrame->MxCsr & _MM_EXCEPT_UNDERFLOW) && 706 !(TrapFrame->MxCsr & _MM_MASK_UNDERFLOW)) 707 { 708 /* Underflow */ 709 ExceptionCode = STATUS_FLOAT_UNDERFLOW; 710 } 711 else if ((TrapFrame->MxCsr & _MM_EXCEPT_INEXACT) && 712 !(TrapFrame->MxCsr & _MM_MASK_INEXACT)) 713 { 714 /* Precision */ 715 ExceptionCode = STATUS_FLOAT_INEXACT_RESULT; 716 } 717 else 718 { 719 /* Should not happen */ 720 ASSERT(FALSE); 721 } 722 723 return ExceptionCode; 724 } 725