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