reactos/ntoskrnl/ke/i386/exp.c

1187 lines
37 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/i386/exp.c
* PURPOSE: Exception Dispatching and Context<->Trap Frame Conversion
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Gregor Anich
* Skywing (skywing@valhallalegends.com)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* FUNCTIONS *****************************************************************/
VOID
INIT_FUNCTION
NTAPI
KeInitExceptions(VOID)
{
ULONG i;
USHORT FlippedSelector;
/* Loop the IDT */
for (i = 0; i <= MAXIMUM_IDTVECTOR; i++)
{
/* Save the current Selector */
FlippedSelector = KiIdt[i].Selector;
/* Flip Selector and Extended Offset */
KiIdt[i].Selector = KiIdt[i].ExtendedOffset;
KiIdt[i].ExtendedOffset = FlippedSelector;
}
}
ULONG
FASTCALL
KiUpdateDr7(IN ULONG Dr7)
{
ULONG DebugMask = KeGetCurrentThread()->Header.DebugActive;
/* Check if debugging is enabled */
if (DebugMask & DR_MASK(DR7_OVERRIDE_V))
{
/* Sanity checks */
ASSERT((DebugMask & DR_REG_MASK) != 0);
ASSERT((Dr7 & ~DR7_RESERVED_MASK) == DR7_OVERRIDE_MASK);
return 0;
}
/* Return DR7 itself */
return Dr7;
}
BOOLEAN
FASTCALL
KiRecordDr7(OUT PULONG Dr7Ptr,
OUT PULONG DrMask)
{
ULONG NewMask, Mask;
UCHAR Result;
/* Check if the caller gave us a mask */
if (!DrMask)
{
/* He didn't, use the one from the thread */
Mask = KeGetCurrentThread()->Header.DebugActive;
}
else
{
/* He did, read it */
Mask = *DrMask;
}
/* Sanity check */
ASSERT((*Dr7Ptr & DR7_RESERVED_MASK) == 0);
/* Check if DR7 is empty */
NewMask = Mask;
if (!(*Dr7Ptr))
{
/* Assume failure */
Result = FALSE;
/* Check the DR mask */
NewMask &= ~(DR_MASK(7));
if (NewMask & DR_REG_MASK)
{
/* Set the active mask */
NewMask |= DR_MASK(DR7_OVERRIDE_V);
/* Set DR7 override */
*Dr7Ptr |= DR7_OVERRIDE_MASK;
}
else
{
/* Sanity check */
ASSERT(NewMask == 0);
}
}
else
{
/* Check if we have a mask or not */
Result = NewMask ? TRUE: FALSE;
/* Update the mask to disable debugging */
NewMask &= ~(DR_MASK(DR7_OVERRIDE_V));
NewMask |= DR_MASK(7);
}
/* Check if caller wants the new mask */
if (DrMask)
{
/* Update it */
*DrMask = NewMask;
}
else
{
/* Check if the mask changed */
if (Mask != NewMask)
{
/* Update it */
KeGetCurrentThread()->Header.DebugActive = (UCHAR)NewMask;
}
}
/* Return the result */
return Result;
}
ULONG
NTAPI
KiEspFromTrapFrame(IN PKTRAP_FRAME TrapFrame)
{
/* Check if this is user-mode or V86 */
if (KiUserTrap(TrapFrame) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* Return it directly */
return TrapFrame->HardwareEsp;
}
else
{
/* Edited frame */
if (!(TrapFrame->SegCs & FRAME_EDITED))
{
/* Return edited value */
return TrapFrame->TempEsp;
}
else
{
/* Virgin frame, calculate */
return (ULONG)&TrapFrame->HardwareEsp;
}
}
}
VOID
NTAPI
KiEspToTrapFrame(IN PKTRAP_FRAME TrapFrame,
IN ULONG Esp)
{
KIRQL OldIrql;
ULONG Previous;
/* Raise to APC_LEVEL if needed */
OldIrql = KeGetCurrentIrql();
if (OldIrql < APC_LEVEL) KeRaiseIrql(APC_LEVEL, &OldIrql);
/* Get the old ESP */
Previous = KiEspFromTrapFrame(TrapFrame);
/* Check if this is user-mode or V86 */
if (KiUserTrap(TrapFrame) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* Write it directly */
TrapFrame->HardwareEsp = Esp;
}
else
{
/* Don't allow ESP to be lowered, this is illegal */
if (Esp < Previous) KeBugCheckEx(SET_OF_INVALID_CONTEXT,
Esp,
Previous,
(ULONG_PTR)TrapFrame,
0);
/* Create an edit frame, check if it was alrady */
if (!(TrapFrame->SegCs & FRAME_EDITED))
{
/* Update the value */
TrapFrame->TempEsp = Esp;
}
else
{
/* Check if ESP changed */
if (Previous != Esp)
{
/* Save CS */
TrapFrame->TempSegCs = TrapFrame->SegCs;
TrapFrame->SegCs &= ~FRAME_EDITED;
/* Save ESP */
TrapFrame->TempEsp = Esp;
}
}
}
/* Restore IRQL */
if (OldIrql < APC_LEVEL) KeLowerIrql(OldIrql);
}
ULONG
NTAPI
KiSsFromTrapFrame(IN PKTRAP_FRAME TrapFrame)
{
/* Check if this was V86 Mode */
if (TrapFrame->EFlags & EFLAGS_V86_MASK)
{
/* Just return it */
return TrapFrame->HardwareSegSs;
}
else if (KiUserTrap(TrapFrame))
{
/* User mode, return the User SS */
return TrapFrame->HardwareSegSs | RPL_MASK;
}
else
{
/* Kernel mode */
return KGDT_R0_DATA;
}
}
VOID
NTAPI
KiSsToTrapFrame(IN PKTRAP_FRAME TrapFrame,
IN ULONG Ss)
{
/* Remove the high-bits */
Ss &= 0xFFFF;
/* If this was V86 Mode */
if (TrapFrame->EFlags & EFLAGS_V86_MASK)
{
/* Just write it */
TrapFrame->HardwareSegSs = Ss;
}
else if (KiUserTrap(TrapFrame))
{
/* Usermode, save the User SS */
TrapFrame->HardwareSegSs = Ss | RPL_MASK;
}
}
USHORT
NTAPI
KiTagWordFnsaveToFxsave(USHORT TagWord)
{
INT FxTagWord = ~TagWord;
/*
* Empty is now 00, any 2 bits containing 1 mean valid
* Now convert the rest (11->0 and the rest to 1)
*/
FxTagWord = (FxTagWord | (FxTagWord >> 1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
FxTagWord = (FxTagWord | (FxTagWord >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
FxTagWord = (FxTagWord | (FxTagWord >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
FxTagWord = (FxTagWord | (FxTagWord >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
return FxTagWord;
}
VOID
NTAPI
Ki386AdjustEsp0(IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread;
ULONG_PTR Stack;
ULONG EFlags;
/* Get the current thread's stack */
Thread = KeGetCurrentThread();
Stack = (ULONG_PTR)Thread->InitialStack;
/* Check if we are in V8086 mode */
if (!(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* Bias the stack for the V86 segments */
Stack -= (FIELD_OFFSET(KTRAP_FRAME, V86Gs) -
FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs));
}
/* Bias the stack for the FPU area */
Stack -= sizeof(FX_SAVE_AREA);
/* Disable interrupts */
EFlags = __readeflags();
_disable();
/* Set new ESP0 value in the TSS */
KeGetPcr()->TSS->Esp0 = Stack;
/* Restore old interrupt state */
__writeeflags(EFlags);
}
VOID
NTAPI
KeContextToTrapFrame(IN PCONTEXT Context,
IN OUT PKEXCEPTION_FRAME ExceptionFrame,
IN OUT PKTRAP_FRAME TrapFrame,
IN ULONG ContextFlags,
IN KPROCESSOR_MODE PreviousMode)
{
PFX_SAVE_AREA FxSaveArea;
ULONG i;
BOOLEAN V86Switch = FALSE;
KIRQL OldIrql;
ULONG DrMask = 0;
/* Do this at APC_LEVEL */
OldIrql = KeGetCurrentIrql();
if (OldIrql < APC_LEVEL) KeRaiseIrql(APC_LEVEL, &OldIrql);
/* Start with the basic Registers */
if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
{
/* Check if we went through a V86 switch */
if ((Context->EFlags & EFLAGS_V86_MASK) !=
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* We did, remember this for later */
V86Switch = TRUE;
}
/* Copy EFLAGS and sanitize them*/
TrapFrame->EFlags = Ke386SanitizeFlags(Context->EFlags, PreviousMode);
/* Copy EBP and EIP */
TrapFrame->Ebp = Context->Ebp;
TrapFrame->Eip = Context->Eip;
/* Check if we were in V86 Mode */
if (TrapFrame->EFlags & EFLAGS_V86_MASK)
{
/* Simply copy the CS value */
TrapFrame->SegCs = Context->SegCs;
}
else
{
/* We weren't in V86, so sanitize the CS */
TrapFrame->SegCs = Ke386SanitizeSeg(Context->SegCs, PreviousMode);
/* Don't let it under 8, that's invalid */
if ((PreviousMode != KernelMode) && (TrapFrame->SegCs < 8))
{
/* Force it to User CS */
TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
}
}
/* Handle SS Specially for validation */
KiSsToTrapFrame(TrapFrame, Context->SegSs);
/* Write ESP back; take into account Edited Trap Frames */
KiEspToTrapFrame(TrapFrame, Context->Esp);
/* Handle our V86 Bias if we went through a switch */
if (V86Switch) Ki386AdjustEsp0(TrapFrame);
}
/* Process the Integer Registers */
if ((ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
{
/* Copy them manually */
TrapFrame->Eax = Context->Eax;
TrapFrame->Ebx = Context->Ebx;
TrapFrame->Ecx = Context->Ecx;
TrapFrame->Edx = Context->Edx;
TrapFrame->Esi = Context->Esi;
TrapFrame->Edi = Context->Edi;
}
/* Process the Context Segments */
if ((ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
{
/* Check if we were in V86 Mode */
if (TrapFrame->EFlags & EFLAGS_V86_MASK)
{
/* Copy the V86 Segments directly */
TrapFrame->V86Ds = Context->SegDs;
TrapFrame->V86Es = Context->SegEs;
TrapFrame->V86Fs = Context->SegFs;
TrapFrame->V86Gs = Context->SegGs;
}
else if (!KiUserTrap(TrapFrame))
{
/* For kernel mode, write the standard values */
TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
TrapFrame->SegFs = Ke386SanitizeSeg(Context->SegFs, PreviousMode);
TrapFrame->SegGs = 0;
}
else
{
/* For user mode, return the values directly */
TrapFrame->SegDs = Context->SegDs;
TrapFrame->SegEs = Context->SegEs;
TrapFrame->SegFs = Context->SegFs;
/* Handle GS specially */
if (TrapFrame->SegCs == (KGDT_R3_CODE | RPL_MASK))
{
/* Don't use it, if user */
TrapFrame->SegGs = 0;
}
else
{
/* Copy it if kernel */
TrapFrame->SegGs = Context->SegGs;
}
}
}
/* Handle the extended registers */
if (((ContextFlags & CONTEXT_EXTENDED_REGISTERS) ==
CONTEXT_EXTENDED_REGISTERS) && KiUserTrap(TrapFrame))
{
/* Get the FX Area */
FxSaveArea = (PFX_SAVE_AREA)(TrapFrame + 1);
/* Check if NPX is present */
if (KeI386NpxPresent)
{
/* Flush the NPX State */
KiFlushNPXState(NULL);
/* Copy the FX State */
RtlCopyMemory(&FxSaveArea->U.FxArea,
&Context->ExtendedRegisters[0],
MAXIMUM_SUPPORTED_EXTENSION);
/* Remove reserved bits from MXCSR */
FxSaveArea->U.FxArea.MXCsr &= KiMXCsrMask;
/* Mask out any invalid flags */
FxSaveArea->Cr0NpxState &= ~(CR0_EM | CR0_MP | CR0_TS);
/* Check if this is a VDM app */
if (PsGetCurrentProcess()->VdmObjects)
{
/* Allow the EM flag */
FxSaveArea->Cr0NpxState |= Context->FloatSave.Cr0NpxState &
(CR0_EM | CR0_MP);
}
}
}
/* Handle the floating point state */
if (((ContextFlags & CONTEXT_FLOATING_POINT) ==
CONTEXT_FLOATING_POINT) && KiUserTrap(TrapFrame))
{
/* Get the FX Area */
FxSaveArea = (PFX_SAVE_AREA)(TrapFrame + 1);
/* Check if NPX is present */
if (KeI386NpxPresent)
{
/* Flush the NPX State */
KiFlushNPXState(NULL);
/* Check if we have Fxsr support */
if (KeI386FxsrPresent)
{
/* Convert the Fn Floating Point state to Fx */
FxSaveArea->U.FxArea.ControlWord =
(USHORT)Context->FloatSave.ControlWord;
FxSaveArea->U.FxArea.StatusWord =
(USHORT)Context->FloatSave.StatusWord;
FxSaveArea->U.FxArea.TagWord =
KiTagWordFnsaveToFxsave((USHORT)Context->FloatSave.TagWord);
FxSaveArea->U.FxArea.ErrorOpcode =
(USHORT)((Context->FloatSave.ErrorSelector >> 16) & 0xFFFF);
FxSaveArea->U.FxArea.ErrorOffset =
Context->FloatSave.ErrorOffset;
FxSaveArea->U.FxArea.ErrorSelector =
Context->FloatSave.ErrorSelector & 0xFFFF;
FxSaveArea->U.FxArea.DataOffset =
Context->FloatSave.DataOffset;
FxSaveArea->U.FxArea.DataSelector =
Context->FloatSave.DataSelector;
/* Clear out the Register Area */
RtlZeroMemory(&FxSaveArea->U.FxArea.RegisterArea[0],
SIZE_OF_FX_REGISTERS);
/* Loop the 8 floating point registers */
for (i = 0; i < 8; i++)
{
/* Copy from Fn to Fx */
RtlCopyMemory(FxSaveArea->U.FxArea.RegisterArea + (i * 16),
Context->FloatSave.RegisterArea + (i * 10),
10);
}
}
else
{
/* Copy the structure */
FxSaveArea->U.FnArea.ControlWord = Context->FloatSave.
ControlWord;
FxSaveArea->U.FnArea.StatusWord = Context->FloatSave.
StatusWord;
FxSaveArea->U.FnArea.TagWord = Context->FloatSave.TagWord;
FxSaveArea->U.FnArea.ErrorOffset = Context->FloatSave.
ErrorOffset;
FxSaveArea->U.FnArea.ErrorSelector = Context->FloatSave.
ErrorSelector;
FxSaveArea->U.FnArea.DataOffset = Context->FloatSave.
DataOffset;
FxSaveArea->U.FnArea.DataSelector = Context->FloatSave.
DataSelector;
/* Loop registers */
for (i = 0; i < SIZE_OF_80387_REGISTERS; i++)
{
/* Copy registers */
FxSaveArea->U.FnArea.RegisterArea[i] =
Context->FloatSave.RegisterArea[i];
}
}
/* Mask out any invalid flags */
FxSaveArea->Cr0NpxState &= ~(CR0_EM | CR0_MP | CR0_TS);
/* Check if this is a VDM app */
if (PsGetCurrentProcess()->VdmObjects)
{
/* Allow the EM flag */
FxSaveArea->Cr0NpxState |= Context->FloatSave.Cr0NpxState &
(CR0_EM | CR0_MP);
}
}
else
{
/* FIXME: Handle FPU Emulation */
//ASSERT(FALSE);
UNIMPLEMENTED;
}
}
/* Handle the Debug Registers */
if ((ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
{
/* Copy Dr0 - Dr4 */
TrapFrame->Dr0 = Context->Dr0;
TrapFrame->Dr1 = Context->Dr1;
TrapFrame->Dr2 = Context->Dr2;
TrapFrame->Dr3 = Context->Dr3;
/* If we're in user-mode */
if (PreviousMode != KernelMode)
{
/* Make sure, no Dr address is above user space */
if (Context->Dr0 > (ULONG)MmHighestUserAddress) TrapFrame->Dr0 = 0;
if (Context->Dr1 > (ULONG)MmHighestUserAddress) TrapFrame->Dr1 = 0;
if (Context->Dr2 > (ULONG)MmHighestUserAddress) TrapFrame->Dr2 = 0;
if (Context->Dr3 > (ULONG)MmHighestUserAddress) TrapFrame->Dr3 = 0;
}
/* Now sanitize and save DR6 */
TrapFrame->Dr6 = Context->Dr6 & DR6_LEGAL;
/* Update the Dr active mask */
if (TrapFrame->Dr0) DrMask |= DR_MASK(0);
if (TrapFrame->Dr1) DrMask |= DR_MASK(1);
if (TrapFrame->Dr2) DrMask |= DR_MASK(2);
if (TrapFrame->Dr3) DrMask |= DR_MASK(3);
if (TrapFrame->Dr6) DrMask |= DR_MASK(6);
/* Sanitize and save DR7 */
TrapFrame->Dr7 = Context->Dr7 & DR7_LEGAL;
KiRecordDr7(&TrapFrame->Dr7, &DrMask);
/* If we're in user-mode */
if (PreviousMode != KernelMode)
{
/* Save the mask */
KeGetCurrentThread()->Header.DebugActive = (UCHAR)DrMask;
}
}
/* Check if thread has IOPL and force it enabled if so */
if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
/* Restore IRQL */
if (OldIrql < APC_LEVEL) KeLowerIrql(OldIrql);
}
VOID
NTAPI
KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN OUT PCONTEXT Context)
{
PFX_SAVE_AREA FxSaveArea;
struct _AlignHack
{
UCHAR Hack[15];
FLOATING_SAVE_AREA UnalignedArea;
} FloatSaveBuffer;
FLOATING_SAVE_AREA *FloatSaveArea;
KIRQL OldIrql;
ULONG i;
/* Do this at APC_LEVEL */
OldIrql = KeGetCurrentIrql();
if (OldIrql < APC_LEVEL) KeRaiseIrql(APC_LEVEL, &OldIrql);
/* Start with the Control flags */
if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
{
/* EBP, EIP and EFLAGS */
Context->Ebp = TrapFrame->Ebp;
Context->Eip = TrapFrame->Eip;
Context->EFlags = TrapFrame->EFlags;
/* Return the correct CS */
if (!(TrapFrame->SegCs & FRAME_EDITED) &&
!(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* Get it from the Temp location */
Context->SegCs = TrapFrame->TempSegCs & 0xFFFF;
}
else
{
/* Return it directly */
Context->SegCs = TrapFrame->SegCs & 0xFFFF;
}
/* Get the Ss and ESP */
Context->SegSs = KiSsFromTrapFrame(TrapFrame);
Context->Esp = KiEspFromTrapFrame(TrapFrame);
}
/* Handle the Segments */
if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
{
/* Do V86 Mode first */
if (TrapFrame->EFlags & EFLAGS_V86_MASK)
{
/* Return from the V86 location */
Context->SegGs = TrapFrame->V86Gs & 0xFFFF;
Context->SegFs = TrapFrame->V86Fs & 0xFFFF;
Context->SegEs = TrapFrame->V86Es & 0xFFFF;
Context->SegDs = TrapFrame->V86Ds & 0xFFFF;
}
else
{
/* Check if this was a Kernel Trap */
if (TrapFrame->SegCs == KGDT_R0_CODE)
{
/* Set valid selectors */
TrapFrame->SegGs = 0;
TrapFrame->SegFs = KGDT_R0_PCR;
TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
}
/* Return the segments */
Context->SegGs = TrapFrame->SegGs & 0xFFFF;
Context->SegFs = TrapFrame->SegFs & 0xFFFF;
Context->SegEs = TrapFrame->SegEs & 0xFFFF;
Context->SegDs = TrapFrame->SegDs & 0xFFFF;
}
}
/* Handle the simple registers */
if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
{
/* Return them directly */
Context->Eax = TrapFrame->Eax;
Context->Ebx = TrapFrame->Ebx;
Context->Ecx = TrapFrame->Ecx;
Context->Edx = TrapFrame->Edx;
Context->Esi = TrapFrame->Esi;
Context->Edi = TrapFrame->Edi;
}
/* Handle extended registers */
if (((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) ==
CONTEXT_EXTENDED_REGISTERS) && KiUserTrap(TrapFrame))
{
/* Get the FX Save Area */
FxSaveArea = (PFX_SAVE_AREA)(TrapFrame + 1);
/* Make sure NPX is present */
if (KeI386NpxPresent)
{
/* Flush the NPX State */
KiFlushNPXState(NULL);
/* Copy the registers */
RtlCopyMemory(&Context->ExtendedRegisters[0],
&FxSaveArea->U.FxArea,
MAXIMUM_SUPPORTED_EXTENSION);
}
}
/* Handle Floating Point */
if (((Context->ContextFlags & CONTEXT_FLOATING_POINT) ==
CONTEXT_FLOATING_POINT) && KiUserTrap(TrapFrame))
{
/* Get the FX Save Area */
FxSaveArea = (PFX_SAVE_AREA)(TrapFrame + 1);
/* Make sure we have an NPX */
if (KeI386NpxPresent)
{
/* Check if we have Fxsr support */
if (KeI386FxsrPresent)
{
/* Align the floating area to 16-bytes */
FloatSaveArea = (FLOATING_SAVE_AREA*)
((ULONG_PTR)&FloatSaveBuffer.UnalignedArea &~ 0xF);
/* Get the State */
KiFlushNPXState(FloatSaveArea);
}
else
{
/* We don't, use the FN area and flush the NPX State */
FloatSaveArea = (FLOATING_SAVE_AREA*)&FxSaveArea->U.FnArea;
KiFlushNPXState(NULL);
}
/* Copy structure */
Context->FloatSave.ControlWord = FloatSaveArea->ControlWord;
Context->FloatSave.StatusWord = FloatSaveArea->StatusWord;
Context->FloatSave.TagWord = FloatSaveArea->TagWord;
Context->FloatSave.ErrorOffset = FloatSaveArea->ErrorOffset;
Context->FloatSave.ErrorSelector = FloatSaveArea->ErrorSelector;
Context->FloatSave.DataOffset = FloatSaveArea->DataOffset;
Context->FloatSave.DataSelector = FloatSaveArea->DataSelector;
Context->FloatSave.Cr0NpxState = FxSaveArea->Cr0NpxState;
/* Loop registers */
for (i = 0; i < SIZE_OF_80387_REGISTERS; i++)
{
/* Copy them */
Context->FloatSave.RegisterArea[i] =
FloatSaveArea->RegisterArea[i];
}
}
else
{
/* FIXME: Handle Emulation */
ASSERT(FALSE);
}
}
/* Handle debug registers */
if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) ==
CONTEXT_DEBUG_REGISTERS)
{
/* Make sure DR7 is valid */
if (TrapFrame->Dr7 & ~DR7_RESERVED_MASK)
{
/* Copy the debug registers */
Context->Dr0 = TrapFrame->Dr0;
Context->Dr1 = TrapFrame->Dr1;
Context->Dr2 = TrapFrame->Dr2;
Context->Dr3 = TrapFrame->Dr3;
Context->Dr6 = TrapFrame->Dr6;
/* Update DR7 */
Context->Dr7 = KiUpdateDr7(TrapFrame->Dr7);
}
else
{
/* Otherwise clear DR registers */
Context->Dr0 =
Context->Dr1 =
Context->Dr2 =
Context->Dr3 =
Context->Dr6 =
Context->Dr7 = 0;
}
}
/* Restore IRQL */
if (OldIrql < APC_LEVEL) KeLowerIrql(OldIrql);
}
BOOLEAN
FASTCALL
KeInvalidAccessAllowed(IN PVOID TrapInformation OPTIONAL)
{
ULONG Eip;
PKTRAP_FRAME TrapFrame = TrapInformation;
VOID NTAPI ExpInterlockedPopEntrySListFault(VOID);
/* Don't do anything if we didn't get a trap frame */
if (!TrapInformation) return FALSE;
/* Check where we came from */
switch (TrapFrame->SegCs)
{
/* Kernel mode */
case KGDT_R0_CODE:
/* Allow S-LIST Routine to fail */
Eip = (ULONG)&ExpInterlockedPopEntrySListFault;
break;
/* User code */
case KGDT_R3_CODE | RPL_MASK:
/* Allow S-LIST Routine to fail */
//Eip = (ULONG)KeUserPopEntrySListFault;
Eip = 0;
break;
default:
/* Anything else gets a bugcheck */
Eip = 0;
}
/* Return TRUE if we want to keep the system up */
return (TrapFrame->Eip == Eip) ? TRUE : FALSE;
}
VOID
NTAPI
KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN FirstChance)
{
CONTEXT Context;
EXCEPTION_RECORD LocalExceptRecord;
/* Increase number of Exception Dispatches */
KeGetCurrentPrcb()->KeExceptionDispatchCount++;
/* Set the context flags */
Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
/* Check if User Mode or if the kernel debugger is enabled */
if ((PreviousMode == UserMode) || (KeGetPcr()->KdVersionBlock))
{
/* Add the FPU Flag */
Context.ContextFlags |= CONTEXT_FLOATING_POINT;
/* Check for NPX Support */
if (KeI386FxsrPresent)
{
/* Save those too */
Context.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
}
}
/* Get a Context */
KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
/* Look at our exception code */
switch (ExceptionRecord->ExceptionCode)
{
/* Breakpoint */
case STATUS_BREAKPOINT:
/* Decrement EIP by one */
Context.Eip--;
break;
/* Internal exception */
case KI_EXCEPTION_ACCESS_VIOLATION:
/* Set correct code */
ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
if (PreviousMode == UserMode)
{
/* FIXME: Handle no execute */
}
break;
}
/* Sanity check */
ASSERT(!((PreviousMode == KernelMode) &&
(Context.EFlags & EFLAGS_V86_MASK)));
/* Handle kernel-mode first, it's simpler */
if (PreviousMode == KernelMode)
{
/* Check if this is a first-chance exception */
if (FirstChance != FALSE)
{
/* Break into the debugger for the first time */
if (KiDebugRoutine(TrapFrame,
ExceptionFrame,
ExceptionRecord,
&Context,
PreviousMode,
FALSE))
{
/* Exception was handled */
goto Handled;
}
/* If the Debugger couldn't handle it, dispatch the exception */
if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled;
}
/* This is a second-chance exception, only for the debugger */
if (KiDebugRoutine(TrapFrame,
ExceptionFrame,
ExceptionRecord,
&Context,
PreviousMode,
TRUE))
{
/* Exception was handled */
goto Handled;
}
/* Third strike; you're out */
KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
ExceptionRecord->ExceptionCode,
(ULONG_PTR)ExceptionRecord->ExceptionAddress,
(ULONG_PTR)TrapFrame,
0);
}
else
{
/* User mode exception, was it first-chance? */
if (FirstChance)
{
/*
* Break into the kernel debugger unless a user mode debugger
* is present or user mode exceptions are ignored, except if this
* is a debug service which we must always pass to KD
*/
if ((!(PsGetCurrentProcess()->DebugPort) &&
!(KdIgnoreUmExceptions)) ||
(KdIsThisAKdTrap(ExceptionRecord,
&Context,
PreviousMode)))
{
/* Call the kernel debugger */
if (KiDebugRoutine(TrapFrame,
ExceptionFrame,
ExceptionRecord,
&Context,
PreviousMode,
FALSE))
{
/* Exception was handled */
goto Handled;
}
}
/* Forward exception to user mode debugger */
if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return;
/* Set up the user-stack */
DispatchToUser:
_SEH2_TRY
{
ULONG Size;
ULONG_PTR Stack, NewStack;
/* Make sure we have a valid SS and that this isn't V86 mode */
if ((TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK)) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK))
{
/* Raise an exception instead */
LocalExceptRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
LocalExceptRecord.ExceptionFlags = 0;
LocalExceptRecord.NumberParameters = 0;
RtlRaiseException(&LocalExceptRecord);
}
/* Align context size and get stack pointer */
Size = (sizeof(CONTEXT) + 3) & ~3;
Stack = (Context.Esp & ~3) - Size;
/* Probe stack and copy Context */
ProbeForWrite((PVOID)Stack, Size, sizeof(ULONG));
RtlCopyMemory((PVOID)Stack, &Context, sizeof(CONTEXT));
/* Align exception record size and get stack pointer */
Size = (sizeof(EXCEPTION_RECORD) -
(EXCEPTION_MAXIMUM_PARAMETERS -
ExceptionRecord->NumberParameters) *
sizeof(ULONG) + 3) & ~3;
NewStack = Stack - Size;
/* Probe stack and copy exception record */
ProbeForWrite((PVOID)(NewStack - 2 * sizeof(ULONG_PTR)),
Size + 2 * sizeof(ULONG_PTR),
sizeof(ULONG));
RtlCopyMemory((PVOID)NewStack, ExceptionRecord, Size);
/* Now write the two params for the user-mode dispatcher */
*(PULONG_PTR)(NewStack - 1 * sizeof(ULONG_PTR)) = Stack;
*(PULONG_PTR)(NewStack - 2 * sizeof(ULONG_PTR)) = NewStack;
/* Set new Stack Pointer */
KiSsToTrapFrame(TrapFrame, KGDT_R3_DATA);
KiEspToTrapFrame(TrapFrame, NewStack - 2 * sizeof(ULONG_PTR));
/* Force correct segments */
TrapFrame->SegCs = Ke386SanitizeSeg(KGDT_R3_CODE, PreviousMode);
TrapFrame->SegDs = Ke386SanitizeSeg(KGDT_R3_DATA, PreviousMode);
TrapFrame->SegEs = Ke386SanitizeSeg(KGDT_R3_DATA, PreviousMode);
TrapFrame->SegFs = Ke386SanitizeSeg(KGDT_R3_TEB, PreviousMode);
TrapFrame->SegGs = 0;
/* Set EIP to the User-mode Dispatcher */
TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
/* Dispatch exception to user-mode */
_SEH2_YIELD(return);
}
_SEH2_EXCEPT((RtlCopyMemory(&LocalExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
{
/* Check if we got a stack overflow and raise that instead */
if ((NTSTATUS)LocalExceptRecord.ExceptionCode ==
STATUS_STACK_OVERFLOW)
{
/* Copy the exception address and record */
LocalExceptRecord.ExceptionAddress =
ExceptionRecord->ExceptionAddress;
RtlCopyMemory(ExceptionRecord,
(PVOID)&LocalExceptRecord,
sizeof(EXCEPTION_RECORD));
/* Do the exception again */
_SEH2_YIELD(goto DispatchToUser);
}
}
_SEH2_END;
DPRINT("First chance exception in %.16s, ExceptionCode: %lx, ExceptionAddress: %p, P0: %lx, P1: %lx\n",
PsGetCurrentProcess()->ImageFileName,
ExceptionRecord->ExceptionCode,
ExceptionRecord->ExceptionAddress,
ExceptionRecord->ExceptionInformation[0],
ExceptionRecord->ExceptionInformation[1]);
}
/* Try second chance */
if (DbgkForwardException(ExceptionRecord, TRUE, TRUE))
{
/* Handled, get out */
return;
}
else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE))
{
/* Handled, get out */
return;
}
/* 3rd strike, kill the process */
DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %p, BaseAddress: %p, P0: %lx, P1: %lx\n",
PsGetCurrentProcess()->ImageFileName,
ExceptionRecord->ExceptionCode,
ExceptionRecord->ExceptionAddress,
PsGetCurrentProcess()->SectionBaseAddress,
ExceptionRecord->ExceptionInformation[0],
ExceptionRecord->ExceptionInformation[1]);
ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
ExceptionRecord->ExceptionCode,
(ULONG_PTR)ExceptionRecord->ExceptionAddress,
(ULONG_PTR)TrapFrame,
0);
}
Handled:
/* Convert the context back into Trap/Exception Frames */
KeContextToTrapFrame(&Context,
ExceptionFrame,
TrapFrame,
Context.ContextFlags,
PreviousMode);
return;
}
DECLSPEC_NORETURN
VOID
NTAPI
KiDispatchExceptionFromTrapFrame(IN NTSTATUS Code,
IN ULONG Flags,
IN ULONG_PTR Address,
IN ULONG ParameterCount,
IN ULONG_PTR Parameter1,
IN ULONG_PTR Parameter2,
IN ULONG_PTR Parameter3,
IN PKTRAP_FRAME TrapFrame)
{
EXCEPTION_RECORD ExceptionRecord;
/* Build the exception record */
ExceptionRecord.ExceptionCode = Code;
ExceptionRecord.ExceptionFlags = Flags;
ExceptionRecord.ExceptionRecord = NULL;
ExceptionRecord.ExceptionAddress = (PVOID)Address;
ExceptionRecord.NumberParameters = ParameterCount;
if (ParameterCount)
{
/* Copy extra parameters */
ExceptionRecord.ExceptionInformation[0] = Parameter1;
ExceptionRecord.ExceptionInformation[1] = Parameter2;
ExceptionRecord.ExceptionInformation[2] = Parameter3;
}
/* Now go dispatch the exception */
KiDispatchException(&ExceptionRecord,
NULL,
TrapFrame,
TrapFrame->EFlags & EFLAGS_V86_MASK ?
-1 : KiUserTrap(TrapFrame),
TRUE);
/* Return from this trap */
KiEoiHelper(TrapFrame);
}
DECLSPEC_NORETURN
VOID
FASTCALL
KiSystemFatalException(IN ULONG ExceptionCode,
IN PKTRAP_FRAME TrapFrame)
{
/* Bugcheck the system */
KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP,
ExceptionCode,
0,
0,
0,
TrapFrame);
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
KeRaiseUserException(IN NTSTATUS ExceptionCode)
{
ULONG OldEip;
PTEB Teb = KeGetCurrentThread()->Teb;
PKTRAP_FRAME TrapFrame = KeGetCurrentThread()->TrapFrame;
/* Make sure we can access the TEB */
_SEH2_TRY
{
/* Set the exception code */
Teb->ExceptionCode = ExceptionCode;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Get the old EIP */
OldEip = TrapFrame->Eip;
/* Change it to the user-mode dispatcher */
TrapFrame->Eip = (ULONG_PTR)KeRaiseUserExceptionDispatcher;
/* Return the old EIP */
return (NTSTATUS)OldEip;
}