xref: /reactos/ntoskrnl/ke/amd64/except.c (revision 5b82ada2)
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
KeInitExceptions(VOID)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
KiDispatchExceptionToUser(IN PKTRAP_FRAME TrapFrame,IN PCONTEXT Context,IN PEXCEPTION_RECORD ExceptionRecord)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
KiPageInDirectory(PVOID ImageBase,USHORT Directory)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
KiPrepareUserDebugData(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
KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,IN PKEXCEPTION_FRAME ExceptionFrame,IN PKTRAP_FRAME TrapFrame,IN KPROCESSOR_MODE PreviousMode,IN BOOLEAN FirstChance)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
KeRaiseUserException(IN NTSTATUS ExceptionCode)413 KeRaiseUserException(IN NTSTATUS ExceptionCode)
414 {
415     UNIMPLEMENTED;
416     return STATUS_UNSUCCESSFUL;
417 }
418 
419 
420 VOID
421 DECLSPEC_NORETURN
KiSystemFatalException(IN ULONG ExceptionCode,IN PKTRAP_FRAME TrapFrame)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
KiNpxNotAvailableFaultHandler(IN PKTRAP_FRAME TrapFrame)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
KiIsPrivilegedInstruction(PUCHAR Ip,BOOLEAN Wow64)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
KiGeneralProtectionFaultUserMode(_In_ PKTRAP_FRAME TrapFrame)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
KiGeneralProtectionFaultHandler(IN PKTRAP_FRAME TrapFrame)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
KiXmmExceptionHandler(IN PKTRAP_FRAME TrapFrame)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