xref: /reactos/ntoskrnl/ke/amd64/except.c (revision 5cadc268)
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_YIELD(return);
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