2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/ke/i386/traphdlr.c
5 * PURPOSE: Kernel Trap Handlers
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 VOID
KiFastCallEntry(VOID
);
16 VOID
KiFastCallEntryWithSingleStep(VOID
);
18 /* GLOBALS ********************************************************************/
20 UCHAR KiTrapPrefixTable
[] =
23 0xF3, /* REP INS/OUTS */
35 UCHAR KiTrapIoTable
[] =
51 PFAST_SYSTEM_CALL_EXIT KiFastCallExitHandler
;
52 #if DBG && !defined(_WINKD_)
53 PKDBG_PRESERVICEHOOK KeWin32PreServiceHook
= NULL
;
54 PKDBG_POSTSERVICEHOOK KeWin32PostServiceHook
= NULL
;
57 BOOLEAN StopChecking
= FALSE
;
61 /* TRAP EXIT CODE *************************************************************/
65 KiVdmTrap(IN PKTRAP_FRAME TrapFrame
)
67 /* Either the V8086 flag is on, or this is user-mode with a VDM */
68 return ((TrapFrame
->EFlags
& EFLAGS_V86_MASK
) ||
69 ((KiUserTrap(TrapFrame
)) && (PsGetCurrentProcess()->VdmObjects
)));
74 KiV86Trap(IN PKTRAP_FRAME TrapFrame
)
76 /* Check if the V8086 flag is on */
77 return ((TrapFrame
->EFlags
& EFLAGS_V86_MASK
) != 0);
82 KiIsFrameEdited(IN PKTRAP_FRAME TrapFrame
)
84 /* An edited frame changes esp. It is marked by clearing the bits
85 defined by FRAME_EDITED in the SegCs field of the trap frame */
86 return ((TrapFrame
->SegCs
& FRAME_EDITED
) == 0);
91 KiCommonExit(IN PKTRAP_FRAME TrapFrame
, BOOLEAN SkipPreviousMode
)
93 /* Disable interrupts until we return */
96 /* Check for APC delivery */
97 KiCheckForApcDelivery(TrapFrame
);
99 /* Restore the SEH handler chain */
100 KeGetPcr()->NtTib
.ExceptionList
= TrapFrame
->ExceptionList
;
102 /* Check if there are active debug registers */
103 if (__builtin_expect(TrapFrame
->Dr7
& ~DR7_RESERVED_MASK
, 0))
105 /* Check if the frame was from user mode or v86 mode */
106 if (KiUserTrap(TrapFrame
) ||
107 (TrapFrame
->EFlags
& EFLAGS_V86_MASK
))
109 /* Handle debug registers */
110 KiHandleDebugRegistersOnTrapExit(TrapFrame
);
114 /* Debugging checks */
115 KiExitTrapDebugChecks(TrapFrame
, SkipPreviousMode
);
121 KiEoiHelper(IN PKTRAP_FRAME TrapFrame
)
123 /* Common trap exit code */
124 KiCommonExit(TrapFrame
, TRUE
);
126 /* Check if this was a V8086 trap */
127 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) KiTrapReturnNoSegments(TrapFrame
);
129 /* Check for user mode exit */
130 if (KiUserTrap(TrapFrame
)) KiTrapReturn(TrapFrame
);
132 /* Check for edited frame */
133 if (KiIsFrameEdited(TrapFrame
)) KiEditedTrapReturn(TrapFrame
);
135 /* Check if we have single stepping enabled */
136 if (TrapFrame
->EFlags
& EFLAGS_TF
) KiTrapReturnNoSegments(TrapFrame
);
138 /* Exit the trap to kernel mode */
139 KiTrapReturnNoSegmentsRet8(TrapFrame
);
145 KiServiceExit(IN PKTRAP_FRAME TrapFrame
,
148 ASSERT((TrapFrame
->EFlags
& EFLAGS_V86_MASK
) == 0);
149 ASSERT(!KiIsFrameEdited(TrapFrame
));
151 /* Copy the status into EAX */
152 TrapFrame
->Eax
= Status
;
154 /* Common trap exit code */
155 KiCommonExit(TrapFrame
, FALSE
);
157 /* Restore previous mode */
158 KeGetCurrentThread()->PreviousMode
= (CCHAR
)TrapFrame
->PreviousPreviousMode
;
160 /* Check for user mode exit */
161 if (KiUserTrap(TrapFrame
))
163 /* Check if we were single stepping */
164 if (TrapFrame
->EFlags
& EFLAGS_TF
)
166 /* Must use the IRET handler */
167 KiSystemCallTrapReturn(TrapFrame
);
171 /* We can use the sysexit handler */
172 KiFastCallExitHandler(TrapFrame
);
176 /* Exit to kernel mode */
177 KiSystemCallReturn(TrapFrame
);
183 KiServiceExit2(IN PKTRAP_FRAME TrapFrame
)
185 /* Common trap exit code */
186 KiCommonExit(TrapFrame
, FALSE
);
188 /* Restore previous mode */
189 KeGetCurrentThread()->PreviousMode
= (CCHAR
)TrapFrame
->PreviousPreviousMode
;
191 /* Check if this was a V8086 trap */
192 if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) KiTrapReturnNoSegments(TrapFrame
);
194 /* Check for user mode exit */
195 if (KiUserTrap(TrapFrame
)) KiTrapReturn(TrapFrame
);
197 /* Check for edited frame */
198 if (KiIsFrameEdited(TrapFrame
)) KiEditedTrapReturn(TrapFrame
);
200 /* Check if we have single stepping enabled */
201 if (TrapFrame
->EFlags
& EFLAGS_TF
) KiTrapReturnNoSegments(TrapFrame
);
203 /* Exit the trap to kernel mode */
204 KiTrapReturnNoSegmentsRet8(TrapFrame
);
208 /* TRAP HANDLERS **************************************************************/
213 KiDebugHandler(IN PKTRAP_FRAME TrapFrame
,
218 /* Check for VDM trap */
219 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
221 /* Enable interrupts if the trap came from user-mode */
222 if (KiUserTrap(TrapFrame
)) _enable();
224 /* Dispatch the exception */
225 KiDispatchExceptionFromTrapFrame(STATUS_BREAKPOINT
,
237 KiNpxHandler(IN PKTRAP_FRAME TrapFrame
,
239 IN PFX_SAVE_AREA SaveArea
)
241 ULONG Cr0
, Mask
, Error
, ErrorOffset
, DataOffset
;
243 /* Check for VDM trap */
244 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
246 /* Check for kernel trap */
247 if (!KiUserTrap(TrapFrame
))
249 /* Kernel might've tripped a delayed error */
250 SaveArea
->Cr0NpxState
|= CR0_TS
;
252 /* Only valid if it happened during a restore */
253 //if ((PVOID)TrapFrame->Eip == FrRestore)
255 /* It did, so just skip the instruction */
256 //TrapFrame->Eip += 3; /* sizeof(FRSTOR) */
257 //KiEoiHelper(TrapFrame);
261 /* User or kernel trap -- get ready to issue an exception */
262 //if (Thread->NpxState == NPX_STATE_NOT_LOADED)
266 Cr0
&= ~(CR0_MP
| CR0_EM
| CR0_TS
);
270 Ke386SaveFpuState(SaveArea
);
272 /* Mark CR0 state dirty */
273 Cr0
|= NPX_STATE_NOT_LOADED
;
274 Cr0
|= SaveArea
->Cr0NpxState
;
277 /* Update NPX state */
278 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
279 KeGetCurrentPrcb()->NpxThread
= NULL
;
282 /* Clear the TS bit and re-enable interrupts */
283 SaveArea
->Cr0NpxState
&= ~CR0_TS
;
286 /* Check if we should get the FN or FX error */
287 if (KeI386FxsrPresent
)
290 Mask
= SaveArea
->U
.FxArea
.ControlWord
;
291 Error
= SaveArea
->U
.FxArea
.StatusWord
;
293 /* Get the FPU exception address too */
294 ErrorOffset
= SaveArea
->U
.FxArea
.ErrorOffset
;
295 DataOffset
= SaveArea
->U
.FxArea
.DataOffset
;
300 Mask
= SaveArea
->U
.FnArea
.ControlWord
;
301 Error
= SaveArea
->U
.FnArea
.StatusWord
;
303 /* Get the FPU exception address too */
304 ErrorOffset
= SaveArea
->U
.FnArea
.ErrorOffset
;
305 DataOffset
= SaveArea
->U
.FnArea
.DataOffset
;
308 /* Get legal exceptions that software should handle */
309 /* We do this by first masking off from the Mask the bits we need, */
310 /* This is done so we can keep the FSW_STACK_FAULT bit in Error. */
311 Mask
&= (FSW_INVALID_OPERATION
|
319 /* Check for invalid operation */
320 if (Error
& FSW_INVALID_OPERATION
)
322 /* NOTE: Stack fault is handled differently than any other case. */
323 /* 1. It's only raised for invalid operation. */
324 /* 2. It's only raised if invalid operation is not masked. */
325 if (Error
& FSW_STACK_FAULT
)
327 /* Issue stack check fault */
328 KiDispatchException2Args(STATUS_FLOAT_STACK_CHECK
,
336 KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION
,
342 /* Check for divide by zero */
343 if (Error
& FSW_ZERO_DIVIDE
)
346 KiDispatchException1Args(STATUS_FLOAT_DIVIDE_BY_ZERO
,
352 /* Check for denormal */
353 if (Error
& FSW_DENORMAL
)
356 KiDispatchException1Args(STATUS_FLOAT_INVALID_OPERATION
,
362 /* Check for overflow */
363 if (Error
& FSW_OVERFLOW
)
366 KiDispatchException1Args(STATUS_FLOAT_OVERFLOW
,
372 /* Check for underflow */
373 if (Error
& FSW_UNDERFLOW
)
376 KiDispatchException1Args(STATUS_FLOAT_UNDERFLOW
,
382 /* Check for precision fault */
383 if (Error
& FSW_PRECISION
)
386 KiDispatchException1Args(STATUS_FLOAT_INEXACT_RESULT
,
392 /* Unknown FPU fault */
393 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 1, Error
, 0, 0, TrapFrame
);
399 KiTrap00Handler(IN PKTRAP_FRAME TrapFrame
)
401 /* Save trap frame */
402 KiEnterTrap(TrapFrame
);
404 /* Check for VDM trap */
405 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
407 /* Enable interrupts */
410 /* Dispatch the exception */
411 KiDispatchException0Args(STATUS_INTEGER_DIVIDE_BY_ZERO
,
419 KiTrap01Handler(IN PKTRAP_FRAME TrapFrame
)
421 /* Save trap frame */
422 KiEnterTrap(TrapFrame
);
424 /* Check for VDM trap */
425 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
427 /* Check if this was a single step after sysenter */
428 if (TrapFrame
->Eip
== (ULONG
)KiFastCallEntry
)
430 /* Disable single stepping */
431 TrapFrame
->EFlags
&= ~EFLAGS_TF
;
433 /* Re-enter at the alternative sysenter entry point */
434 TrapFrame
->Eip
= (ULONG
)KiFastCallEntryWithSingleStep
;
437 KiEoiHelper(TrapFrame
);
440 /* Enable interrupts if the trap came from user-mode */
441 if (KiUserTrap(TrapFrame
)) _enable();
443 /* Mask out trap flag and dispatch the exception */
444 TrapFrame
->EFlags
&= ~EFLAGS_TF
;
445 KiDispatchException0Args(STATUS_SINGLE_STEP
,
459 KTRAP_FRAME TrapFrame
;
463 // In some sort of strange recursion case, we might end up here with the IF
464 // flag incorrectly on the interrupt frame -- during a normal NMI this would
465 // normally already be set.
467 // For sanity's sake, make sure interrupts are disabled for sure.
468 // NMIs will already be since the CPU does it for us.
473 // Get the current TSS, thread, and process
476 Thread
= ((PKIPCR
)PCR
)->PrcbData
.CurrentThread
;
477 Process
= Thread
->ApcState
.Process
;
480 // Save data usually not in the TSS
482 Tss
->CR3
= Process
->DirectoryTableBase
[0];
483 Tss
->IoMapBase
= Process
->IopmOffset
;
484 Tss
->LDT
= Process
->LdtDescriptor
.LimitLow
? KGDT_LDT
: 0;
487 // Now get the base address of the NMI TSS
489 TssGdt
= &((PKIPCR
)KeGetPcr())->GDT
[KGDT_NMI_TSS
/ sizeof(KGDTENTRY
)];
490 NmiTss
= (PKTSS
)(ULONG_PTR
)(TssGdt
->BaseLow
|
491 TssGdt
->HighWord
.Bytes
.BaseMid
<< 16 |
492 TssGdt
->HighWord
.Bytes
.BaseHi
<< 24);
495 // Switch to it and activate it, masking off the nested flag
497 // Note that in reality, we are already on the NMI tss -- we just need to
498 // update the PCR to reflect this
501 __writeeflags(__readeflags() &~ EFLAGS_NESTED_TASK
);
502 TssGdt
->HighWord
.Bits
.Dpl
= 0;
503 TssGdt
->HighWord
.Bits
.Pres
= 1;
504 TssGdt
->HighWord
.Bits
.Type
= I386_TSS
;
507 // Now build the trap frame based on the original TSS
509 // The CPU does a hardware "Context switch" / task switch of sorts and so it
510 // takes care of saving our context in the normal TSS.
512 // We just have to go get the values...
514 RtlZeroMemory(&TrapFrame
, sizeof(KTRAP_FRAME
));
515 TrapFrame
.HardwareSegSs
= Tss
->Ss0
;
516 TrapFrame
.HardwareEsp
= Tss
->Esp0
;
517 TrapFrame
.EFlags
= Tss
->EFlags
;
518 TrapFrame
.SegCs
= Tss
->Cs
;
519 TrapFrame
.Eip
= Tss
->Eip
;
520 TrapFrame
.Ebp
= Tss
->Ebp
;
521 TrapFrame
.Ebx
= Tss
->Ebx
;
522 TrapFrame
.Esi
= Tss
->Esi
;
523 TrapFrame
.Edi
= Tss
->Edi
;
524 TrapFrame
.SegFs
= Tss
->Fs
;
525 TrapFrame
.ExceptionList
= PCR
->NtTib
.ExceptionList
;
526 TrapFrame
.PreviousPreviousMode
= -1;
527 TrapFrame
.Eax
= Tss
->Eax
;
528 TrapFrame
.Ecx
= Tss
->Ecx
;
529 TrapFrame
.Edx
= Tss
->Edx
;
530 TrapFrame
.SegDs
= Tss
->Ds
;
531 TrapFrame
.SegEs
= Tss
->Es
;
532 TrapFrame
.SegGs
= Tss
->Gs
;
533 TrapFrame
.DbgEip
= Tss
->Eip
;
534 TrapFrame
.DbgEbp
= Tss
->Ebp
;
537 // Store the trap frame in the KPRCB
539 KiSaveProcessorState(&TrapFrame
, NULL
);
542 // Call any registered NMI handlers and see if they handled it or not
547 // They did not, so call the platform HAL routine to bugcheck the system
549 // Make sure the HAL believes it's running at HIGH IRQL... we can't use
550 // the normal APIs here as playing with the IRQL could change the system
554 PCR
->Irql
= HIGH_LEVEL
;
560 // Although the CPU disabled NMIs, we just did a BIOS Call, which could've
561 // totally changed things.
563 // We have to make sure we're still in our original NMI -- a nested NMI
564 // will point back to the NMI TSS, and in that case we're hosed.
566 if (PCR
->TSS
->Backlink
!= KGDT_NMI_TSS
)
569 // Restore original TSS
574 // Set it back to busy
576 TssGdt
->HighWord
.Bits
.Dpl
= 0;
577 TssGdt
->HighWord
.Bits
.Pres
= 1;
578 TssGdt
->HighWord
.Bits
.Type
= I386_ACTIVE_TSS
;
581 // Restore nested flag
583 __writeeflags(__readeflags() | EFLAGS_NESTED_TASK
);
586 // Handled, return from interrupt
592 // Unhandled: crash the system
594 KiSystemFatalException(EXCEPTION_NMI
, NULL
);
600 KiTrap03Handler(IN PKTRAP_FRAME TrapFrame
)
602 /* Save trap frame */
603 KiEnterTrap(TrapFrame
);
605 /* Continue with the common handler */
606 KiDebugHandler(TrapFrame
, BREAKPOINT_BREAK
, 0, 0);
612 KiTrap04Handler(IN PKTRAP_FRAME TrapFrame
)
614 /* Save trap frame */
615 KiEnterTrap(TrapFrame
);
617 /* Check for VDM trap */
618 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
620 /* Enable interrupts */
623 /* Dispatch the exception */
624 KiDispatchException0Args(STATUS_INTEGER_OVERFLOW
,
632 KiTrap05Handler(IN PKTRAP_FRAME TrapFrame
)
634 /* Save trap frame */
635 KiEnterTrap(TrapFrame
);
637 /* Check for VDM trap */
638 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
640 /* Check for kernel-mode fault */
641 if (!KiUserTrap(TrapFrame
)) KiSystemFatalException(EXCEPTION_BOUND_CHECK
, TrapFrame
);
643 /* Enable interrupts */
646 /* Dispatch the exception */
647 KiDispatchException0Args(STATUS_ARRAY_BOUNDS_EXCEEDED
,
655 KiTrap06Handler(IN PKTRAP_FRAME TrapFrame
)
661 /* Check for V86 GPF */
662 if (__builtin_expect(KiV86Trap(TrapFrame
), 1))
665 KiEnterV86Trap(TrapFrame
);
667 /* Must be a VDM process */
668 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects
, 0))
670 /* Enable interrupts */
673 /* Setup illegal instruction fault */
674 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
679 /* Go to APC level */
680 OldIrql
= KfRaiseIrql(APC_LEVEL
);
684 if (!VdmDispatchBop(TrapFrame
))
686 /* Should only happen in VDM mode */
687 UNIMPLEMENTED_FATAL();
690 /* Bring IRQL back */
691 KfLowerIrql(OldIrql
);
694 /* Do a quick V86 exit if possible */
695 KiExitV86Trap(TrapFrame
);
698 /* Save trap frame */
699 KiEnterTrap(TrapFrame
);
701 /* Enable interrupts */
702 Instruction
= (PUCHAR
)TrapFrame
->Eip
;
705 /* Check for user trap */
706 if (KiUserTrap(TrapFrame
))
710 /* Scan next 4 opcodes */
711 for (i
= 0; i
< 4; i
++)
713 /* Check for LOCK instruction */
714 if (Instruction
[i
] == 0xF0)
716 /* Send invalid lock sequence exception */
717 KiDispatchException0Args(STATUS_INVALID_LOCK_SEQUENCE
,
723 /* FIXME: SEH ends here */
726 /* Kernel-mode or user-mode fault (but not LOCK) */
727 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
736 KiTrap07Handler(IN PKTRAP_FRAME TrapFrame
)
738 PKTHREAD Thread
, NpxThread
;
739 PFX_SAVE_AREA SaveArea
, NpxSaveArea
;
742 /* Save trap frame */
743 KiEnterTrap(TrapFrame
);
745 /* Try to handle NPX delay load */
748 /* Get the current thread */
749 Thread
= KeGetCurrentThread();
751 /* Get the NPX frame */
752 SaveArea
= KiGetThreadNpxArea(Thread
);
754 /* Check if emulation is enabled */
755 if (SaveArea
->Cr0NpxState
& CR0_EM
)
757 /* Not implemented */
758 UNIMPLEMENTED_FATAL();
761 /* Save CR0 and check NPX state */
763 if (Thread
->NpxState
!= NPX_STATE_LOADED
)
766 Cr0
&= ~(CR0_MP
| CR0_EM
| CR0_TS
);
769 /* Get the NPX thread */
770 NpxThread
= KeGetCurrentPrcb()->NpxThread
;
773 /* Get the NPX frame */
774 NpxSaveArea
= KiGetThreadNpxArea(NpxThread
);
777 DPRINT("FIXME: Save FPU state: %p\n", NpxSaveArea
);
778 //Ke386SaveFpuState(NpxSaveArea);
780 /* Update NPX state */
781 NpxThread
->NpxState
= NPX_STATE_NOT_LOADED
;
785 //Ke386LoadFpuState(SaveArea);
787 /* Update NPX state */
788 Thread
->NpxState
= NPX_STATE_LOADED
;
789 KeGetCurrentPrcb()->NpxThread
= Thread
;
791 /* Enable interrupts */
794 /* Check if CR0 needs to be reloaded due to context switch */
795 if (!SaveArea
->Cr0NpxState
) KiEoiHelper(TrapFrame
);
797 /* Otherwise, we need to reload CR0, disable interrupts */
802 Cr0
|= SaveArea
->Cr0NpxState
;
805 /* Now restore interrupts and check for TS */
807 if (Cr0
& CR0_TS
) KiEoiHelper(TrapFrame
);
809 /* We're still here -- clear TS and try again */
810 __writecr0(__readcr0() &~ CR0_TS
);
815 /* This is an actual fault, not a lack of FPU state */
820 /* TS should not be set */
824 * If it's incorrectly set, then maybe the state is actually still valid
825 * but we could've lock track of that due to a BIOS call.
826 * Make sure MP is still set, which should verify the theory.
830 /* Indeed, the state is actually still valid, so clear TS */
831 __writecr0(__readcr0() &~ CR0_TS
);
832 KiEoiHelper(TrapFrame
);
835 /* Otherwise, something strange is going on */
836 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 2, Cr0
, 0, 0, TrapFrame
);
839 /* It's not a delayed load, so process this trap as an NPX fault */
840 KiNpxHandler(TrapFrame
, Thread
, SaveArea
);
846 KiTrap08Handler(IN PKTRAP_FRAME TrapFrame
)
848 /* FIXME: Not handled */
849 KiSystemFatalException(EXCEPTION_DOUBLE_FAULT
, TrapFrame
);
855 KiTrap09Handler(IN PKTRAP_FRAME TrapFrame
)
857 /* Save trap frame */
858 KiEnterTrap(TrapFrame
);
860 /* Enable interrupts and kill the system */
862 KiSystemFatalException(EXCEPTION_NPX_OVERRUN
, TrapFrame
);
868 KiTrap0AHandler(IN PKTRAP_FRAME TrapFrame
)
870 /* Save trap frame */
871 KiEnterTrap(TrapFrame
);
873 /* Check for VDM trap */
874 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
876 /* Kill the system */
877 KiSystemFatalException(EXCEPTION_INVALID_TSS
, TrapFrame
);
883 KiTrap0BHandler(IN PKTRAP_FRAME TrapFrame
)
885 /* Save trap frame */
886 KiEnterTrap(TrapFrame
);
888 /* FIXME: Kill the system */
890 KiSystemFatalException(EXCEPTION_SEGMENT_NOT_PRESENT
, TrapFrame
);
896 KiTrap0CHandler(IN PKTRAP_FRAME TrapFrame
)
898 /* Save trap frame */
899 KiEnterTrap(TrapFrame
);
901 /* FIXME: Kill the system */
903 KiSystemFatalException(EXCEPTION_STACK_FAULT
, TrapFrame
);
909 KiTrap0DHandler(IN PKTRAP_FRAME TrapFrame
)
912 BOOLEAN Privileged
= FALSE
;
914 UCHAR Instruction
= 0;
917 /* Check for V86 GPF */
918 if (__builtin_expect(KiV86Trap(TrapFrame
), 1))
921 KiEnterV86Trap(TrapFrame
);
923 /* Must be a VDM process */
924 if (__builtin_expect(!PsGetCurrentProcess()->VdmObjects
, 0))
926 /* Enable interrupts */
929 /* Setup illegal instruction fault */
930 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
935 /* Go to APC level */
936 OldIrql
= KfRaiseIrql(APC_LEVEL
);
939 /* Handle the V86 opcode */
940 if (__builtin_expect(Ki386HandleOpcodeV86(TrapFrame
) == 0xFF, 0))
942 /* Should only happen in VDM mode */
943 UNIMPLEMENTED_FATAL();
946 /* Bring IRQL back */
947 KfLowerIrql(OldIrql
);
950 /* Do a quick V86 exit if possible */
951 KiExitV86Trap(TrapFrame
);
954 /* Save trap frame */
955 KiEnterTrap(TrapFrame
);
957 /* Check for user-mode GPF */
958 if (KiUserTrap(TrapFrame
))
960 /* Should not be VDM */
961 ASSERT(KiVdmTrap(TrapFrame
) == FALSE
);
963 /* Enable interrupts and check error code */
965 if (!TrapFrame
->ErrCode
)
968 Instructions
= (PUCHAR
)TrapFrame
->Eip
;
970 /* Scan next 15 bytes */
971 for (i
= 0; i
< 15; i
++)
973 /* Skip prefix instructions */
974 for (j
= 0; j
< sizeof(KiTrapPrefixTable
); j
++)
976 /* Is this a prefix instruction? */
977 if (Instructions
[i
] == KiTrapPrefixTable
[j
])
984 /* Is this NOT any prefix instruction? */
985 if (j
== sizeof(KiTrapPrefixTable
))
987 /* We can go ahead and handle the fault now */
988 Instruction
= Instructions
[i
];
993 /* If all we found was prefixes, then this instruction is too long */
996 /* Setup illegal instruction fault */
997 KiDispatchException0Args(STATUS_ILLEGAL_INSTRUCTION
,
1002 /* Check for privileged instructions */
1003 DPRINT("Instruction (%lu) at fault: %lx %lx %lx %lx\n",
1006 Instructions
[i
+ 1],
1007 Instructions
[i
+ 2],
1008 Instructions
[i
+ 3]);
1009 if (Instruction
== 0xF4) // HLT
1011 /* HLT is privileged */
1014 else if (Instruction
== 0x0F)
1016 /* Test if it's any of the privileged two-byte opcodes */
1017 if (((Instructions
[i
+ 1] == 0x00) && // LLDT or LTR
1018 (((Instructions
[i
+ 2] & 0x38) == 0x10) || // LLDT
1019 (Instructions
[i
+ 2] == 0x18))) || // LTR
1020 ((Instructions
[i
+ 1] == 0x01) && // LGDT or LIDT or LMSW
1021 (((Instructions
[i
+ 2] & 0x38) == 0x10) || // LGDT
1022 (Instructions
[i
+ 2] == 0x18) || // LIDT
1023 (Instructions
[i
+ 2] == 0x30))) || // LMSW
1024 (Instructions
[i
+ 1] == 0x08) || // INVD
1025 (Instructions
[i
+ 1] == 0x09) || // WBINVD
1026 (Instructions
[i
+ 1] == 0x35) || // SYSEXIT
1027 (Instructions
[i
+ 1] == 0x21) || // MOV DR, XXX
1028 (Instructions
[i
+ 1] == 0x06) || // CLTS
1029 (Instructions
[i
+ 1] == 0x20) || // MOV CR, XXX
1030 (Instructions
[i
+ 1] == 0x22) || // MOV XXX, CR
1031 (Instructions
[i
+ 1] == 0x23) || // MOV YYY, DR
1032 (Instructions
[i
+ 1] == 0x30) || // WRMSR
1033 (Instructions
[i
+ 1] == 0x33)) // RDPMC
1034 // INVLPG, INVLPGA, SYSRET
1036 /* These are all privileged */
1042 /* Get the IOPL and compare with the RPL mask */
1043 Iopl
= (TrapFrame
->EFlags
& EFLAGS_IOPL
) >> 12;
1044 if ((TrapFrame
->SegCs
& RPL_MASK
) > Iopl
)
1046 /* I/O privilege error -- check for known instructions */
1047 if ((Instruction
== 0xFA) || (Instruction
== 0xFB)) // CLI or STI
1049 /* These are privileged */
1054 /* Last hope: an IN/OUT instruction */
1055 for (j
= 0; j
< sizeof(KiTrapIoTable
); j
++)
1057 /* Is this an I/O instruction? */
1058 if (Instruction
== KiTrapIoTable
[j
])
1060 /* Then it's privileged */
1069 /* So now... was the instruction privileged or not? */
1072 /* Whew! We have a privileged instruction, so dispatch the fault */
1073 KiDispatchException0Args(STATUS_PRIVILEGED_INSTRUCTION
,
1079 /* If we got here, send an access violation */
1080 KiDispatchException2Args(STATUS_ACCESS_VIOLATION
,
1088 * Check for a fault during checking of the user instruction.
1090 * Note that the SEH handler will catch invalid EIP, but we could be dealing
1091 * with an invalid CS, which will generate another GPF instead.
1094 if (((PVOID
)TrapFrame
->Eip
>= (PVOID
)KiTrap0DHandler
) &&
1095 ((PVOID
)TrapFrame
->Eip
< (PVOID
)KiTrap0DHandler
))
1097 /* Not implemented */
1098 UNIMPLEMENTED_FATAL();
1102 * NOTE: The ASM trap exit code would restore segment registers by doing
1103 * a POP <SEG>, which could cause an invalid segment if someone had messed
1104 * with the segment values.
1106 * Another case is a bogus SS, which would hit a GPF when doing the iret.
1107 * This could only be done through a buggy or malicious driver, or perhaps
1108 * the kernel debugger.
1110 * The kernel normally restores the "true" segment if this happens.
1112 * However, since we're restoring in C, not ASM, we can't detect
1113 * POP <SEG> since the actual instructions will be different.
1115 * A better technique would be to check the EIP and somehow edit the
1116 * trap frame before restarting the instruction -- but we would need to
1117 * know the extract instruction that was used first.
1119 * We could force a special instrinsic to use stack instructions, or write
1120 * a simple instruction length checker.
1122 * Nevertheless, this is a lot of work for the purpose of avoiding a crash
1123 * when the user is purposedly trying to create one from kernel-mode, so
1124 * we should probably table this for now since it's not a "real" issue.
1128 * NOTE2: Another scenario is the IRET during a V8086 restore (BIOS Call)
1129 * which will cause a GPF since the trap frame is a total mess (on purpose)
1130 * as built in KiEnterV86Mode.
1132 * The idea is to scan for IRET, scan for the known EIP adress, validate CS
1133 * and then manually issue a jump to the V8086 return EIP.
1135 Instructions
= (PUCHAR
)TrapFrame
->Eip
;
1136 if (Instructions
[0] == 0xCF)
1139 * Some evil shit is going on here -- this is not the SS:ESP you're
1140 * looking for! Instead, this is actually CS:EIP you're looking at!
1141 * Why? Because part of the trap frame actually corresponds to the IRET
1142 * stack during the trap exit!
1144 if ((TrapFrame
->HardwareEsp
== (ULONG
)Ki386BiosCallReturnAddress
) &&
1145 (TrapFrame
->HardwareSegSs
== (KGDT_R0_CODE
| RPL_MASK
)))
1147 /* Exit the V86 trap! */
1148 Ki386BiosCallReturnAddress(TrapFrame
);
1152 /* Otherwise, this is another kind of IRET fault */
1153 UNIMPLEMENTED_FATAL();
1157 /* So since we're not dealing with the above case, check for RDMSR/WRMSR */
1158 if ((Instructions
[0] == 0xF) && // 2-byte opcode
1159 ((Instructions
[1] == 0x32) || // RDMSR
1160 (Instructions
[1] == 0x30))) // WRMSR
1162 /* Unknown CPU MSR, so raise an access violation */
1163 KiDispatchException0Args(STATUS_ACCESS_VIOLATION
,
1168 /* Check for lazy segment load */
1169 if (TrapFrame
->SegDs
!= (KGDT_R3_DATA
| RPL_MASK
))
1172 TrapFrame
->SegDs
= (KGDT_R3_DATA
| RPL_MASK
);
1174 else if (TrapFrame
->SegEs
!= (KGDT_R3_DATA
| RPL_MASK
))
1177 TrapFrame
->SegEs
= (KGDT_R3_DATA
| RPL_MASK
);
1181 /* Whatever it is, we can't handle it */
1182 KiSystemFatalException(EXCEPTION_GP_FAULT
, TrapFrame
);
1185 /* Return to where we came from */
1186 KiTrapReturn(TrapFrame
);
1192 KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame
)
1198 /* Save trap frame */
1199 KiEnterTrap(TrapFrame
);
1201 /* Check if this is the base frame */
1202 Thread
= KeGetCurrentThread();
1203 if (KeGetTrapFrame(Thread
) != TrapFrame
)
1205 /* It isn't, check if this is a second nested frame */
1206 if (((ULONG_PTR
)KeGetTrapFrame(Thread
) - (ULONG_PTR
)TrapFrame
) <=
1207 FIELD_OFFSET(KTRAP_FRAME
, EFlags
))
1209 /* The stack is somewhere in between frames, we need to fix it */
1210 UNIMPLEMENTED_FATAL();
1217 /* Enable interupts */
1220 /* Check if we came in with interrupts disabled */
1221 if (!(TrapFrame
->EFlags
& EFLAGS_INTERRUPT_MASK
))
1223 /* This is completely illegal, bugcheck the system */
1224 KeBugCheckWithTf(IRQL_NOT_LESS_OR_EQUAL
,
1227 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1232 /* Check for S-LIST fault in kernel mode */
1233 if (TrapFrame
->Eip
== (ULONG_PTR
)ExpInterlockedPopEntrySListFault
)
1235 PSLIST_HEADER SListHeader
;
1237 /* Sanity check that the assembly is correct:
1238 This must be mov ebx, [eax]
1239 Followed by cmpxchg8b [ebp] */
1240 ASSERT((((UCHAR
*)TrapFrame
->Eip
)[0] == 0x8B) &&
1241 (((UCHAR
*)TrapFrame
->Eip
)[1] == 0x18) &&
1242 (((UCHAR
*)TrapFrame
->Eip
)[2] == 0x0F) &&
1243 (((UCHAR
*)TrapFrame
->Eip
)[3] == 0xC7) &&
1244 (((UCHAR
*)TrapFrame
->Eip
)[4] == 0x4D) &&
1245 (((UCHAR
*)TrapFrame
->Eip
)[5] == 0x00));
1247 /* Get the pointer to the SLIST_HEADER */
1248 SListHeader
= (PSLIST_HEADER
)TrapFrame
->Ebp
;
1250 /* Check if the Next member of the SLIST_HEADER was changed */
1251 if (SListHeader
->Next
.Next
!= (PSLIST_ENTRY
)TrapFrame
->Eax
)
1253 /* Restart the operation */
1254 TrapFrame
->Eip
= (ULONG_PTR
)ExpInterlockedPopEntrySListResume
;
1256 /* Continue execution */
1257 KiEoiHelper(TrapFrame
);
1262 /* Do what windows does and issue an invalid access violation */
1263 KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION
,
1265 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1272 /* Call the access fault handler */
1273 Status
= MmAccessFault(TrapFrame
->ErrCode
& 1,
1275 KiUserTrap(TrapFrame
),
1277 if (NT_SUCCESS(Status
)) KiEoiHelper(TrapFrame
);
1279 /* Check for syscall fault */
1281 if ((TrapFrame
->Eip
== (ULONG_PTR
)CopyParams
) ||
1282 (TrapFrame
->Eip
== (ULONG_PTR
)ReadBatch
))
1284 /* Not yet implemented */
1285 UNIMPLEMENTED_FATAL();
1288 /* Check for VDM trap */
1289 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
1291 /* Either kernel or user trap (non VDM) so dispatch exception */
1292 if (Status
== STATUS_ACCESS_VIOLATION
)
1294 /* This status code is repurposed so we can recognize it later */
1295 KiDispatchException2Args(KI_EXCEPTION_ACCESS_VIOLATION
,
1297 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1301 else if ((Status
== STATUS_GUARD_PAGE_VIOLATION
) ||
1302 (Status
== STATUS_STACK_OVERFLOW
))
1304 /* These faults only have two parameters */
1305 KiDispatchException2Args(Status
,
1307 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1312 /* Only other choice is an in-page error, with 3 parameters */
1313 KiDispatchExceptionFromTrapFrame(STATUS_IN_PAGE_ERROR
,
1316 TrapFrame
->ErrCode
& 2 ? TRUE
: FALSE
,
1325 KiTrap0FHandler(IN PKTRAP_FRAME TrapFrame
)
1327 /* Save trap frame */
1328 KiEnterTrap(TrapFrame
);
1330 /* FIXME: Kill the system */
1332 KiSystemFatalException(EXCEPTION_RESERVED_TRAP
, TrapFrame
);
1338 KiTrap10Handler(IN PKTRAP_FRAME TrapFrame
)
1341 PFX_SAVE_AREA SaveArea
;
1343 /* Save trap frame */
1344 KiEnterTrap(TrapFrame
);
1346 /* Check if this is the NPX thrad */
1347 Thread
= KeGetCurrentThread();
1348 SaveArea
= KiGetThreadNpxArea(Thread
);
1349 if (Thread
!= KeGetCurrentPrcb()->NpxThread
)
1351 /* It isn't, enable interrupts and set delayed error */
1353 SaveArea
->Cr0NpxState
|= CR0_TS
;
1356 KiEoiHelper(TrapFrame
);
1359 /* Otherwise, proceed with NPX fault handling */
1360 KiNpxHandler(TrapFrame
, Thread
, SaveArea
);
1366 KiTrap11Handler(IN PKTRAP_FRAME TrapFrame
)
1368 /* Save trap frame */
1369 KiEnterTrap(TrapFrame
);
1371 /* Enable interrupts and kill the system */
1373 KiSystemFatalException(EXCEPTION_ALIGNMENT_CHECK
, TrapFrame
);
1379 KiTrap13Handler(IN PKTRAP_FRAME TrapFrame
)
1382 PFX_SAVE_AREA SaveArea
;
1383 ULONG Cr0
, MxCsrMask
, Error
;
1385 /* Save trap frame */
1386 KiEnterTrap(TrapFrame
);
1388 /* Check if this is the NPX thrad */
1389 Thread
= KeGetCurrentThread();
1390 if (Thread
!= KeGetCurrentPrcb()->NpxThread
)
1392 /* It isn't, kill the system */
1393 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, (ULONG_PTR
)Thread
, 0, 0, TrapFrame
);
1396 /* Get the NPX frame */
1397 SaveArea
= KiGetThreadNpxArea(Thread
);
1399 /* Check for VDM trap */
1400 ASSERT((KiVdmTrap(TrapFrame
)) == FALSE
);
1402 /* Check for user trap */
1403 if (!KiUserTrap(TrapFrame
))
1405 /* Kernel should not fault on XMMI */
1406 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 2, TrapFrame
);
1411 Cr0
&= ~(CR0_MP
| CR0_EM
| CR0_TS
);
1414 /* Save FPU state */
1415 Ke386SaveFpuState(SaveArea
);
1417 /* Mark CR0 state dirty */
1418 Cr0
|= NPX_STATE_NOT_LOADED
;
1419 Cr0
|= SaveArea
->Cr0NpxState
;
1422 /* Update NPX state */
1423 Thread
->NpxState
= NPX_STATE_NOT_LOADED
;
1424 KeGetCurrentPrcb()->NpxThread
= NULL
;
1426 /* Clear the TS bit and re-enable interrupts */
1427 SaveArea
->Cr0NpxState
&= ~CR0_TS
;
1430 /* Now look at MxCsr to get the mask of errors we should care about */
1431 MxCsrMask
= ~((USHORT
)SaveArea
->U
.FxArea
.MXCsr
>> 7);
1433 /* Get legal exceptions that software should handle */
1434 Error
= (USHORT
)SaveArea
->U
.FxArea
.MXCsr
& (FSW_INVALID_OPERATION
|
1442 /* Now handle any of those legal errors */
1443 if (Error
& (FSW_INVALID_OPERATION
|
1450 /* By issuing an exception */
1451 KiDispatchException1Args(STATUS_FLOAT_MULTIPLE_TRAPS
,
1457 /* Unknown XMMI fault */
1458 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 1, TrapFrame
);
1461 /* SOFTWARE SERVICES **********************************************************/
1465 KiGetTickCountHandler(IN PKTRAP_FRAME TrapFrame
)
1467 UNIMPLEMENTED_DBGBREAK();
1472 KiCallbackReturnHandler(IN PKTRAP_FRAME TrapFrame
)
1476 /* Pass the register parameters to NtCallbackReturn.
1477 Result pointer is in ecx, result length in edx, status in eax */
1478 Status
= NtCallbackReturn((PVOID
)TrapFrame
->Ecx
,
1482 /* If we got here, something went wrong. Return an error to the caller */
1483 KiServiceExit(TrapFrame
, Status
);
1489 KiRaiseAssertionHandler(IN PKTRAP_FRAME TrapFrame
)
1491 /* Save trap frame */
1492 KiEnterTrap(TrapFrame
);
1494 /* Decrement EIP to point to the INT2C instruction (2 bytes, not 1 like INT3) */
1495 TrapFrame
->Eip
-= 2;
1497 /* Dispatch the exception */
1498 KiDispatchException0Args(STATUS_ASSERTION_FAILURE
,
1506 KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame
)
1508 /* Save trap frame */
1509 KiEnterTrap(TrapFrame
);
1511 /* Increment EIP to skip the INT3 instruction */
1514 /* Continue with the common handler */
1515 KiDebugHandler(TrapFrame
, TrapFrame
->Eax
, TrapFrame
->Ecx
, TrapFrame
->Edx
);
1521 KiDbgPreServiceHook(ULONG SystemCallNumber
, PULONG_PTR Arguments
)
1523 #if DBG && !defined(_WINKD_)
1524 if (SystemCallNumber
>= 0x1000 && KeWin32PreServiceHook
)
1525 KeWin32PreServiceHook(SystemCallNumber
, Arguments
);
1531 KiDbgPostServiceHook(ULONG SystemCallNumber
, ULONG_PTR Result
)
1533 #if DBG && !defined(_WINKD_)
1534 if (SystemCallNumber
>= 0x1000 && KeWin32PostServiceHook
)
1535 return KeWin32PostServiceHook(SystemCallNumber
, Result
);
1543 KiSystemServiceHandler(IN PKTRAP_FRAME TrapFrame
,
1547 PKSERVICE_TABLE_DESCRIPTOR DescriptorTable
;
1548 ULONG Id
, Offset
, StackBytes
, Result
;
1550 ULONG SystemCallNumber
= TrapFrame
->Eax
;
1552 /* Get the current thread */
1553 Thread
= KeGetCurrentThread();
1555 /* Set debug header */
1556 KiFillTrapFrameDebug(TrapFrame
);
1558 /* Chain trap frames */
1559 TrapFrame
->Edx
= (ULONG_PTR
)Thread
->TrapFrame
;
1562 TrapFrame
->ErrCode
= 0;
1564 /* Save previous mode */
1565 TrapFrame
->PreviousPreviousMode
= Thread
->PreviousMode
;
1567 /* Save the SEH chain and terminate it for now */
1568 TrapFrame
->ExceptionList
= KeGetPcr()->NtTib
.ExceptionList
;
1569 KeGetPcr()->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
1571 /* Default to debugging disabled */
1574 /* Check if the frame was from user mode */
1575 if (KiUserTrap(TrapFrame
))
1577 /* Check for active debugging */
1578 if (KeGetCurrentThread()->Header
.DebugActive
& 0xFF)
1580 /* Handle debug registers */
1581 KiHandleDebugRegistersOnTrapEntry(TrapFrame
);
1585 /* Set thread fields */
1586 Thread
->TrapFrame
= TrapFrame
;
1587 Thread
->PreviousMode
= KiUserTrap(TrapFrame
);
1589 /* Enable interrupts */
1592 /* Decode the system call number */
1593 Offset
= (SystemCallNumber
>> SERVICE_TABLE_SHIFT
) & SERVICE_TABLE_MASK
;
1594 Id
= SystemCallNumber
& SERVICE_NUMBER_MASK
;
1596 /* Get descriptor table */
1597 DescriptorTable
= (PVOID
)((ULONG_PTR
)Thread
->ServiceTable
+ Offset
);
1599 /* Validate the system call number */
1600 if (__builtin_expect(Id
>= DescriptorTable
->Limit
, 0))
1602 /* Check if this is a GUI call */
1603 if (!(Offset
& SERVICE_TABLE_TEST
))
1606 Result
= STATUS_INVALID_SYSTEM_SERVICE
;
1610 /* Convert us to a GUI thread -- must wrap in ASM to get new EBP */
1611 Result
= KiConvertToGuiThread();
1613 /* Reload trap frame and descriptor table pointer from new stack */
1614 TrapFrame
= *(volatile PVOID
*)&Thread
->TrapFrame
;
1615 DescriptorTable
= (PVOID
)(*(volatile ULONG_PTR
*)&Thread
->ServiceTable
+ Offset
);
1617 if (!NT_SUCCESS(Result
))
1619 /* Set the last error and fail */
1620 //SetLastWin32Error(RtlNtStatusToDosError(Result));
1624 /* Validate the system call number again */
1625 if (Id
>= DescriptorTable
->Limit
)
1628 Result
= STATUS_INVALID_SYSTEM_SERVICE
;
1633 /* Check if this is a GUI call */
1634 if (__builtin_expect(Offset
& SERVICE_TABLE_TEST
, 0))
1636 /* Get the batch count and flush if necessary */
1637 if (NtCurrentTeb()->GdiBatchCount
) KeGdiFlushUserBatch();
1640 /* Increase system call count */
1641 KeGetCurrentPrcb()->KeSystemCalls
++;
1643 /* FIXME: Increase individual counts on debug systems */
1644 //KiIncreaseSystemCallCount(DescriptorTable, Id);
1646 /* Get stack bytes */
1647 StackBytes
= DescriptorTable
->Number
[Id
];
1649 /* Probe caller stack */
1650 if (__builtin_expect((Arguments
< (PVOID
)MmUserProbeAddress
) && !(KiUserTrap(TrapFrame
)), 0))
1652 /* Access violation */
1653 UNIMPLEMENTED_FATAL();
1656 /* Call pre-service debug hook */
1657 KiDbgPreServiceHook(SystemCallNumber
, Arguments
);
1659 /* Get the handler and make the system call */
1660 Handler
= (PVOID
)DescriptorTable
->Base
[Id
];
1661 Result
= KiSystemCallTrampoline(Handler
, Arguments
, StackBytes
);
1663 /* Call post-service debug hook */
1664 Result
= KiDbgPostServiceHook(SystemCallNumber
, Result
);
1666 /* Make sure we're exiting correctly */
1667 KiExitSystemCallDebugChecks(Id
, TrapFrame
);
1669 /* Restore the old trap frame */
1671 Thread
->TrapFrame
= (PKTRAP_FRAME
)TrapFrame
->Edx
;
1673 /* Exit from system call */
1674 KiServiceExit(TrapFrame
, Result
);
1682 Kei386EoiHelper(VOID
)
1684 /* We should never see this call happening */
1685 ERROR_FATAL("Mismatched NT/HAL version");