1449 lines
45 KiB
C
1449 lines
45 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
exceptn.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implement the code necessary to dispatch expections to the
|
|||
|
proper mode and invoke the exception dispatcher.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler (davec) 30-Apr-1989
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
14-Feb-1990 shielint
|
|||
|
|
|||
|
Modified for NT386 interrupt manager
|
|||
|
|
|||
|
6-April-1990 bryanwi
|
|||
|
|
|||
|
Fix non-canonical stack case for 386.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
#define FN_BITS_PER_TAGWORD 16
|
|||
|
#define FN_TAG_EMPTY 0x3
|
|||
|
#define FN_TAG_MASK 0x3
|
|||
|
#define FX_TAG_VALID 0x1
|
|||
|
#define NUMBER_OF_FP_REGISTERS 8
|
|||
|
#define BYTES_PER_FP_REGISTER 10
|
|||
|
#define BYTES_PER_FX_REGISTER 16
|
|||
|
|
|||
|
extern UCHAR VdmUserCr0MapIn[];
|
|||
|
extern BOOLEAN KeI386FxsrPresent;
|
|||
|
extern BOOLEAN KeI386XMMIPresent;
|
|||
|
|
|||
|
VOID
|
|||
|
Ki386AdjustEsp0(
|
|||
|
IN PKTRAP_FRAME TrapFrame
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KiEm87StateToNpxFrame(
|
|||
|
OUT PFLOATING_SAVE_AREA NpxFrmae
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KiNpxFrameToEm87State(
|
|||
|
IN PFLOATING_SAVE_AREA NpxFrmae
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
KiEspFromTrapFrame(
|
|||
|
IN PKTRAP_FRAME TrapFrame
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine fetches the correct esp from a trapframe, accounting
|
|||
|
for whether the frame is a user or kernel mode frame, and whether
|
|||
|
it has been edited.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame from which volatile context
|
|||
|
should be copied into the context record.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Value of Esp.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
|
|||
|
(TrapFrame->EFlags & EFLAGS_V86_MASK)) {
|
|||
|
|
|||
|
// User mode frame, real value of Esp is always in HardwareEsp.
|
|||
|
|
|||
|
return TrapFrame->HardwareEsp;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if ((TrapFrame->SegCs & FRAME_EDITED) == 0) {
|
|||
|
|
|||
|
// Kernel mode frame which has had esp edited,
|
|||
|
// value of Esp is in TempEsp.
|
|||
|
|
|||
|
return TrapFrame->TempEsp;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// Kernel mode frame has has not had esp edited, compute esp.
|
|||
|
|
|||
|
return (ULONG)&TrapFrame->HardwareEsp;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiEspToTrapFrame(
|
|||
|
IN PKTRAP_FRAME TrapFrame,
|
|||
|
IN ULONG Esp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the specified value Esp into the trap frame,
|
|||
|
accounting for whether the frame is a user or kernel mode frame,
|
|||
|
and whether it has been edited before.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame from which volatile context
|
|||
|
should be copied into the context record.
|
|||
|
|
|||
|
Esp - New value for Esp.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG OldEsp;
|
|||
|
|
|||
|
OldEsp = KiEspFromTrapFrame(TrapFrame);
|
|||
|
|
|||
|
if (((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
|
|||
|
(TrapFrame->EFlags & EFLAGS_V86_MASK)) {
|
|||
|
|
|||
|
//
|
|||
|
// User mode trap frame
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->HardwareEsp = Esp;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Kernel mode esp can't be lowered or iret emulation will fail
|
|||
|
//
|
|||
|
|
|||
|
if (Esp < OldEsp)
|
|||
|
KeBugCheckEx(SET_OF_INVALID_CONTEXT,
|
|||
|
Esp, OldEsp, (ULONG_PTR)TrapFrame, 0);
|
|||
|
|
|||
|
//
|
|||
|
// Edit frame, setting edit marker as needed.
|
|||
|
//
|
|||
|
|
|||
|
if ((TrapFrame->SegCs & FRAME_EDITED) == 0) {
|
|||
|
|
|||
|
// Kernel frame that has already been edited,
|
|||
|
// store value in TempEsp.
|
|||
|
|
|||
|
TrapFrame->TempEsp = Esp;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// Kernel frame for which Esp is being edited first time.
|
|||
|
// Save real SegCs, set marked in SegCs, save Esp value.
|
|||
|
|
|||
|
if (OldEsp != Esp) {
|
|||
|
TrapFrame->TempSegCs = TrapFrame->SegCs;
|
|||
|
TrapFrame->SegCs = TrapFrame->SegCs & ~FRAME_EDITED;
|
|||
|
TrapFrame->TempEsp = Esp;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
KiSegSsFromTrapFrame(
|
|||
|
IN PKTRAP_FRAME TrapFrame
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine fetches the correct ss from a trapframe, accounting
|
|||
|
for whether the frame is a user or kernel mode frame.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame from which volatile context
|
|||
|
should be copied into the context record.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Value of SegSs.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK){
|
|||
|
return TrapFrame->HardwareSegSs;
|
|||
|
} else if ((TrapFrame->SegCs & MODE_MASK) != KernelMode) {
|
|||
|
|
|||
|
//
|
|||
|
// It's user mode. The HardwareSegSs contains R3 data selector.
|
|||
|
//
|
|||
|
|
|||
|
return TrapFrame->HardwareSegSs | RPL_MASK;
|
|||
|
} else {
|
|||
|
return KGDT_R0_DATA;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiSegSsToTrapFrame(
|
|||
|
IN PKTRAP_FRAME TrapFrame,
|
|||
|
IN ULONG SegSs
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
It turns out that in a flat system there are only two legal values
|
|||
|
for SS. Therefore, this procedure forces the appropriate one
|
|||
|
of those values to be used. The legal SS value is a function of
|
|||
|
which CS value is already set.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame from which volatile context
|
|||
|
should be copied into the context record.
|
|||
|
|
|||
|
SegSs - value of SS caller would like to set.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Nothing.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
SegSs &= SEGMENT_MASK; // Throw away the high order trash bits
|
|||
|
|
|||
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
|
|||
|
TrapFrame->HardwareSegSs = SegSs;
|
|||
|
} else if ((TrapFrame->SegCs & MODE_MASK) == UserMode) {
|
|||
|
|
|||
|
//
|
|||
|
// If user mode, we simply put SegSs to trapfram. If the SegSs
|
|||
|
// is a bogus value. The trap0d handler will be able to detect
|
|||
|
// this and handle it appropriately.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->HardwareSegSs = SegSs | RPL_MASK;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// else {
|
|||
|
// The frame is a kernel mode frame, which does not have
|
|||
|
// a place to store SS. Therefore, do nothing.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeContextFromKframes (
|
|||
|
IN PKTRAP_FRAME TrapFrame,
|
|||
|
IN PKEXCEPTION_FRAME ExceptionFrame,
|
|||
|
IN OUT PCONTEXT ContextFrame
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine moves the selected contents of the specified trap and exception frames
|
|||
|
frames into the specified context frame according to the specified context
|
|||
|
flags.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame from which volatile context
|
|||
|
should be copied into the context record.
|
|||
|
|
|||
|
ExceptionFrame - Supplies a pointer to an exception frame from which context
|
|||
|
should be copied into the context record. This argument is ignored since
|
|||
|
there is no exception frame on NT386.
|
|||
|
|
|||
|
ContextFrame - Supplies a pointer to the context frame that receives the
|
|||
|
context copied from the trap and exception frames.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PFX_SAVE_AREA NpxFrame;
|
|||
|
BOOLEAN StateSaved;
|
|||
|
ULONG i;
|
|||
|
struct _FPSaveBuffer {
|
|||
|
UCHAR Buffer[15];
|
|||
|
FLOATING_SAVE_AREA SaveArea;
|
|||
|
} FloatSaveBuffer;
|
|||
|
PFLOATING_SAVE_AREA PSaveArea;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( ExceptionFrame );
|
|||
|
|
|||
|
//
|
|||
|
// Set control information if specified.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFrame->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
|
|||
|
|
|||
|
//
|
|||
|
// Set registers ebp, eip, cs, eflag, esp and ss.
|
|||
|
//
|
|||
|
|
|||
|
ContextFrame->Ebp = TrapFrame->Ebp;
|
|||
|
ContextFrame->Eip = TrapFrame->Eip;
|
|||
|
|
|||
|
if (((TrapFrame->SegCs & FRAME_EDITED) == 0) &&
|
|||
|
((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0)) {
|
|||
|
ContextFrame->SegCs = TrapFrame->TempSegCs & SEGMENT_MASK;
|
|||
|
} else {
|
|||
|
ContextFrame->SegCs = TrapFrame->SegCs & SEGMENT_MASK;
|
|||
|
}
|
|||
|
ContextFrame->EFlags = TrapFrame->EFlags;
|
|||
|
ContextFrame->SegSs = KiSegSsFromTrapFrame(TrapFrame);
|
|||
|
ContextFrame->Esp = KiEspFromTrapFrame(TrapFrame);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set segment register contents if specified.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFrame->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) {
|
|||
|
|
|||
|
//
|
|||
|
// Set segment registers gs, fs, es, ds.
|
|||
|
//
|
|||
|
// These values are junk most of the time, but useful
|
|||
|
// for debugging under certain conditions. Therefore,
|
|||
|
// we report whatever was in the frame.
|
|||
|
//
|
|||
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
|
|||
|
ContextFrame->SegGs = TrapFrame->V86Gs & SEGMENT_MASK;
|
|||
|
ContextFrame->SegFs = TrapFrame->V86Fs & SEGMENT_MASK;
|
|||
|
ContextFrame->SegEs = TrapFrame->V86Es & SEGMENT_MASK;
|
|||
|
ContextFrame->SegDs = TrapFrame->V86Ds & SEGMENT_MASK;
|
|||
|
}
|
|||
|
else {
|
|||
|
if (TrapFrame->SegCs == KGDT_R0_CODE) {
|
|||
|
//
|
|||
|
// Trap frames created from R0_CODE traps do not save
|
|||
|
// the following selectors. Set them in the frame now.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->SegGs = 0;
|
|||
|
TrapFrame->SegFs = KGDT_R0_PCR;
|
|||
|
TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
|
|||
|
TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
|
|||
|
}
|
|||
|
|
|||
|
ContextFrame->SegGs = TrapFrame->SegGs & SEGMENT_MASK;
|
|||
|
ContextFrame->SegFs = TrapFrame->SegFs & SEGMENT_MASK;
|
|||
|
ContextFrame->SegEs = TrapFrame->SegEs & SEGMENT_MASK;
|
|||
|
ContextFrame->SegDs = TrapFrame->SegDs & SEGMENT_MASK;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set integer register contents if specified.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFrame->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
|
|||
|
|
|||
|
//
|
|||
|
// Set integer registers edi, esi, ebx, edx, ecx, eax
|
|||
|
//
|
|||
|
|
|||
|
ContextFrame->Edi = TrapFrame->Edi;
|
|||
|
ContextFrame->Esi = TrapFrame->Esi;
|
|||
|
ContextFrame->Ebx = TrapFrame->Ebx;
|
|||
|
ContextFrame->Ecx = TrapFrame->Ecx;
|
|||
|
ContextFrame->Edx = TrapFrame->Edx;
|
|||
|
ContextFrame->Eax = TrapFrame->Eax;
|
|||
|
}
|
|||
|
|
|||
|
if (((ContextFrame->ContextFlags & CONTEXT_EXTENDED_REGISTERS) ==
|
|||
|
CONTEXT_EXTENDED_REGISTERS) &&
|
|||
|
((TrapFrame->SegCs & MODE_MASK) == UserMode)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the base TrapFrame, and the NpxFrame is on the base
|
|||
|
// of the kernel stack, just above it in memory.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame = (PFX_SAVE_AREA)(TrapFrame + 1);
|
|||
|
|
|||
|
if (KeI386NpxPresent) {
|
|||
|
KiFlushNPXState (NULL);
|
|||
|
RtlCopyMemory( (PVOID)&(ContextFrame->ExtendedRegisters[0]),
|
|||
|
(PVOID)&(NpxFrame->U.FxArea),
|
|||
|
MAXIMUM_SUPPORTED_EXTENSION
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fetch floating register contents if requested, and type of target
|
|||
|
// is user. (system frames have no fp state, so ignore request)
|
|||
|
//
|
|||
|
if ( ((ContextFrame->ContextFlags & CONTEXT_FLOATING_POINT) ==
|
|||
|
CONTEXT_FLOATING_POINT) &&
|
|||
|
((TrapFrame->SegCs & MODE_MASK) == UserMode)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the base TrapFrame, and the NpxFrame is on the base
|
|||
|
// of the kernel stack, just above it in memory.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame = (PFX_SAVE_AREA)(TrapFrame + 1);
|
|||
|
|
|||
|
if (KeI386NpxPresent) {
|
|||
|
|
|||
|
//
|
|||
|
// Force the coprocessors state to the save area and copy it
|
|||
|
// to the context frame.
|
|||
|
//
|
|||
|
|
|||
|
if (KeI386FxsrPresent == TRUE) {
|
|||
|
|
|||
|
//
|
|||
|
// FP state save was done using fxsave. Get the save
|
|||
|
// area in fnsave format
|
|||
|
//
|
|||
|
// Save area must be 16 byte aligned so we cushion it with
|
|||
|
// 15 bytes (in the locals declaration above) and round
|
|||
|
// down to align.
|
|||
|
//
|
|||
|
|
|||
|
ULONG_PTR Temp;
|
|||
|
Temp = (ULONG_PTR)&FloatSaveBuffer.SaveArea;
|
|||
|
Temp &= ~0xf;
|
|||
|
PSaveArea = (PFLOATING_SAVE_AREA)Temp;
|
|||
|
KiFlushNPXState (PSaveArea);
|
|||
|
} else {
|
|||
|
|
|||
|
PSaveArea = (PFLOATING_SAVE_AREA)&(NpxFrame->U.FnArea);
|
|||
|
KiFlushNPXState (NULL);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ContextFrame->FloatSave.ControlWord = PSaveArea->ControlWord;
|
|||
|
ContextFrame->FloatSave.StatusWord = PSaveArea->StatusWord;
|
|||
|
ContextFrame->FloatSave.TagWord = PSaveArea->TagWord;
|
|||
|
ContextFrame->FloatSave.ErrorOffset = PSaveArea->ErrorOffset;
|
|||
|
ContextFrame->FloatSave.ErrorSelector = PSaveArea->ErrorSelector;
|
|||
|
ContextFrame->FloatSave.DataOffset = PSaveArea->DataOffset;
|
|||
|
ContextFrame->FloatSave.DataSelector = PSaveArea->DataSelector;
|
|||
|
ContextFrame->FloatSave.Cr0NpxState = NpxFrame->Cr0NpxState;
|
|||
|
|
|||
|
for (i = 0; i < SIZE_OF_80387_REGISTERS; i++) {
|
|||
|
ContextFrame->FloatSave.RegisterArea[i] = PSaveArea->RegisterArea[i];
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The 80387 is being emulated by the R3 emulator.
|
|||
|
// ** The only time the Npx state is ever obtained or set is
|
|||
|
// ** for userlevel handling. Current Irql must be 0 or 1.
|
|||
|
// Go slurp the emulator's R3 data and generate the
|
|||
|
// floating point context
|
|||
|
//
|
|||
|
|
|||
|
StateSaved = KiEm87StateToNpxFrame(&ContextFrame->FloatSave);
|
|||
|
if (StateSaved) {
|
|||
|
ContextFrame->FloatSave.Cr0NpxState = NpxFrame->Cr0NpxState;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The floatingpoint state can not be determined.
|
|||
|
// Remove the floatingpoint flag from the context frame flags.
|
|||
|
//
|
|||
|
|
|||
|
ContextFrame->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Fetch Dr register contents if requested. Values may be trash.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFrame->ContextFlags & CONTEXT_DEBUG_REGISTERS) ==
|
|||
|
CONTEXT_DEBUG_REGISTERS) {
|
|||
|
|
|||
|
ContextFrame->Dr0 = TrapFrame->Dr0;
|
|||
|
ContextFrame->Dr1 = TrapFrame->Dr1;
|
|||
|
ContextFrame->Dr2 = TrapFrame->Dr2;
|
|||
|
ContextFrame->Dr3 = TrapFrame->Dr3;
|
|||
|
ContextFrame->Dr6 = TrapFrame->Dr6;
|
|||
|
|
|||
|
//
|
|||
|
// If it's a user mode frame, and the thread doesn't have DRs set,
|
|||
|
// and we just return the trash in the frame, we risk accidentally
|
|||
|
// making the thread active with trash values on a set. Therefore,
|
|||
|
// Dr7 must be set to 0 if we get a non-active user mode frame.
|
|||
|
//
|
|||
|
|
|||
|
if ((((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
|
|||
|
((TrapFrame->EFlags & EFLAGS_V86_MASK) != 0)) &&
|
|||
|
(KeGetCurrentThread()->DebugActive == TRUE)) {
|
|||
|
|
|||
|
ContextFrame->Dr7 = TrapFrame->Dr7;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ContextFrame->Dr7 = 0L;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeContextToKframes (
|
|||
|
IN OUT PKTRAP_FRAME TrapFrame,
|
|||
|
IN OUT PKEXCEPTION_FRAME ExceptionFrame,
|
|||
|
IN PCONTEXT ContextFrame,
|
|||
|
IN ULONG ContextFlags,
|
|||
|
IN KPROCESSOR_MODE PreviousMode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine moves the selected contents of the specified context frame into
|
|||
|
the specified trap and exception frames according to the specified context
|
|||
|
flags.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame that receives the volatile
|
|||
|
context from the context record.
|
|||
|
|
|||
|
ExceptionFrame - Supplies a pointer to an exception frame that receives
|
|||
|
the nonvolatile context from the context record. This argument is
|
|||
|
ignored since there is no exception frame on NT386.
|
|||
|
|
|||
|
ContextFrame - Supplies a pointer to a context frame that contains the
|
|||
|
context that is to be copied into the trap and exception frames.
|
|||
|
|
|||
|
ContextFlags - Supplies the set of flags that specify which parts of the
|
|||
|
context frame are to be copied into the trap and exception frames.
|
|||
|
|
|||
|
PreviousMode - Supplies the processor mode for which the trap and exception
|
|||
|
frames are being built.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PFX_SAVE_AREA NpxFrame;
|
|||
|
ULONG i;
|
|||
|
ULONG j;
|
|||
|
ULONG TagWord;
|
|||
|
BOOLEAN StateSaved;
|
|||
|
BOOLEAN ModeChanged;
|
|||
|
#if DBG
|
|||
|
PKPCR Pcr;
|
|||
|
KIRQL OldIrql;
|
|||
|
#endif
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( ExceptionFrame );
|
|||
|
|
|||
|
//
|
|||
|
// Set control information if specified.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
|
|||
|
|
|||
|
if ((ContextFrame->EFlags & EFLAGS_V86_MASK) !=
|
|||
|
(TrapFrame->EFlags & EFLAGS_V86_MASK)) {
|
|||
|
ModeChanged = TRUE;
|
|||
|
} else {
|
|||
|
ModeChanged = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set registers eflag, ebp, eip, cs, esp and ss.
|
|||
|
// Eflags is set first, so that the auxilliary routines
|
|||
|
// can check the v86 bit to determine as well as cs, to
|
|||
|
// determine if the frame is kernel or user mode. (v86 mode cs
|
|||
|
// can have any value)
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->EFlags = SANITIZE_FLAGS(ContextFrame->EFlags, PreviousMode);
|
|||
|
TrapFrame->Ebp = ContextFrame->Ebp;
|
|||
|
TrapFrame->Eip = ContextFrame->Eip;
|
|||
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
|
|||
|
TrapFrame->SegCs = ContextFrame->SegCs;
|
|||
|
} else {
|
|||
|
TrapFrame->SegCs = SANITIZE_SEG(ContextFrame->SegCs, PreviousMode);
|
|||
|
if (PreviousMode != KernelMode && TrapFrame->SegCs < 8) {
|
|||
|
|
|||
|
//
|
|||
|
// If user mode and the selector value is less than 8, we
|
|||
|
// know it is an invalid selector. Set it to flat user
|
|||
|
// mode selector. Another reason we need to check for this
|
|||
|
// is that any cs value less than 8 causes our exit kernel
|
|||
|
// macro to treat its exit trap fram as an edited frame.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
|
|||
|
}
|
|||
|
}
|
|||
|
KiSegSsToTrapFrame(TrapFrame, ContextFrame->SegSs);
|
|||
|
KiEspToTrapFrame(TrapFrame, ContextFrame->Esp);
|
|||
|
if (ModeChanged) {
|
|||
|
Ki386AdjustEsp0(TrapFrame); // realign esp0 in the tss
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set segment register contents if specified.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) {
|
|||
|
|
|||
|
//
|
|||
|
// Set segment registers gs, fs, es, ds.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// There's only one legal value for DS and ES, so simply set it.
|
|||
|
// This allows KeContextFromKframes to report the real values in
|
|||
|
// the frame. (which are junk most of the time, but sometimes useful
|
|||
|
// for debugging.)
|
|||
|
// Only 2 legal values for FS, let either one be set.
|
|||
|
// Force GS to be 0 to deal with entry via SysCall and exit
|
|||
|
// via exception.
|
|||
|
//
|
|||
|
// For V86 mode, the FS, GS, DS, and ES registers must be properly
|
|||
|
// set from the supplied context.
|
|||
|
//
|
|||
|
|
|||
|
if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
|
|||
|
TrapFrame->V86Fs = ContextFrame->SegFs;
|
|||
|
TrapFrame->V86Es = ContextFrame->SegEs;
|
|||
|
TrapFrame->V86Ds = ContextFrame->SegDs;
|
|||
|
TrapFrame->V86Gs = ContextFrame->SegGs;
|
|||
|
} else if (((TrapFrame->SegCs & MODE_MASK) == KernelMode)) {
|
|||
|
|
|||
|
//
|
|||
|
// set up the standard selectors
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->SegFs = SANITIZE_SEG(ContextFrame->SegFs, PreviousMode);
|
|||
|
TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
|
|||
|
TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
|
|||
|
TrapFrame->SegGs = 0;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If user mode, we simply return whatever left in context frame
|
|||
|
// and let trap 0d handle it (if later we trap while popping the
|
|||
|
// trap frame.) V86 mode also get handled here.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->SegFs = ContextFrame->SegFs;
|
|||
|
TrapFrame->SegEs = ContextFrame->SegEs;
|
|||
|
TrapFrame->SegDs = ContextFrame->SegDs;
|
|||
|
if (TrapFrame->SegCs == (KGDT_R3_CODE | RPL_MASK)) {
|
|||
|
TrapFrame->SegGs = 0;
|
|||
|
} else {
|
|||
|
TrapFrame->SegGs = ContextFrame->SegGs;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Set integer registers contents if specified.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
|
|||
|
|
|||
|
//
|
|||
|
// Set integer registers edi, esi, ebx, edx, ecx, eax.
|
|||
|
//
|
|||
|
// Can NOT call RtlCopyMemory here because the regs aren't
|
|||
|
// contiguous in pusha frame, and we don't want to export
|
|||
|
// bits of junk into context record.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->Edi = ContextFrame->Edi;
|
|||
|
TrapFrame->Esi = ContextFrame->Esi;
|
|||
|
TrapFrame->Ebx = ContextFrame->Ebx;
|
|||
|
TrapFrame->Ecx = ContextFrame->Ecx;
|
|||
|
TrapFrame->Edx = ContextFrame->Edx;
|
|||
|
TrapFrame->Eax = ContextFrame->Eax;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set extended register contents if requested, and type of target
|
|||
|
// is user. (system frames have no extended state, so ignore request)
|
|||
|
//
|
|||
|
|
|||
|
if (((ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) &&
|
|||
|
((TrapFrame->SegCs & MODE_MASK) == UserMode)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the base TrapFrame, and the NpxFrame is on the base
|
|||
|
// of the kernel stack, just above it in memory.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame = (PFX_SAVE_AREA)(TrapFrame + 1);
|
|||
|
|
|||
|
if (KeI386NpxPresent) {
|
|||
|
KiFlushNPXState (NULL);
|
|||
|
RtlCopyMemory( (PVOID)&(NpxFrame->U.FxArea),
|
|||
|
(PVOID)&(ContextFrame->ExtendedRegisters[0]),
|
|||
|
MAXIMUM_SUPPORTED_EXTENSION
|
|||
|
);
|
|||
|
//
|
|||
|
// Make sure only valid floating state bits are moved to Cr0NpxState.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->Cr0NpxState &= ~(CR0_EM | CR0_MP | CR0_TS);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure all reserved bits are clear in MXCSR so we don't get a GP
|
|||
|
// fault when doing an FRSTOR on this state.
|
|||
|
//
|
|||
|
NpxFrame->U.FxArea.MXCsr = SANITIZE_MXCSR(NpxFrame->U.FxArea.MXCsr);
|
|||
|
|
|||
|
//
|
|||
|
// Only let VDMs turn on the EM bit. The kernel can't do
|
|||
|
// anything for FLAT apps
|
|||
|
//
|
|||
|
if (PsGetCurrentProcess()->VdmObjects != NULL) {
|
|||
|
NpxFrame->Cr0NpxState |= ContextFrame->FloatSave.Cr0NpxState &
|
|||
|
(CR0_EM | CR0_MP);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set floating register contents if requested, and type of target
|
|||
|
// is user. (system frames have no fp state, so ignore request)
|
|||
|
//
|
|||
|
|
|||
|
if (((ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) &&
|
|||
|
((TrapFrame->SegCs & MODE_MASK) == UserMode)) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the base TrapFrame, and the NpxFrame is on the base
|
|||
|
// of the kernel stack, just above it in memory.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame = (PFX_SAVE_AREA)(TrapFrame + 1);
|
|||
|
|
|||
|
if (KeI386NpxPresent) {
|
|||
|
|
|||
|
//
|
|||
|
// Set coprocessor stack, control and status registers
|
|||
|
//
|
|||
|
|
|||
|
KiFlushNPXState (NULL);
|
|||
|
|
|||
|
if (KeI386FxsrPresent == TRUE) {
|
|||
|
|
|||
|
//
|
|||
|
// Restore FP state in the fxrstor format
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->U.FxArea.ControlWord =
|
|||
|
(USHORT)ContextFrame->FloatSave.ControlWord;
|
|||
|
NpxFrame->U.FxArea.StatusWord =
|
|||
|
(USHORT)ContextFrame->FloatSave.StatusWord;
|
|||
|
|
|||
|
//
|
|||
|
// Construct the tag word from fnsave format to fxsave format
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->U.FxArea.TagWord = 0; // Mark every register invalid
|
|||
|
|
|||
|
TagWord = ContextFrame->FloatSave.TagWord;
|
|||
|
|
|||
|
for (i = 0; i < FN_BITS_PER_TAGWORD; i+=2) {
|
|||
|
|
|||
|
if (((TagWord >> i) & FN_TAG_MASK) != FN_TAG_EMPTY) {
|
|||
|
|
|||
|
//
|
|||
|
// This register is valid
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->U.FxArea.TagWord |= (FX_TAG_VALID << (i/2));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NpxFrame->U.FxArea.ErrorOffset =
|
|||
|
ContextFrame->FloatSave.ErrorOffset;
|
|||
|
NpxFrame->U.FxArea.ErrorSelector =
|
|||
|
(ContextFrame->FloatSave.ErrorSelector & 0xFFFF);
|
|||
|
NpxFrame->U.FxArea.ErrorOpcode =
|
|||
|
(USHORT)((ContextFrame->FloatSave.ErrorSelector >> 16) & 0xFFFF);
|
|||
|
NpxFrame->U.FxArea.DataOffset =
|
|||
|
ContextFrame->FloatSave.DataOffset;
|
|||
|
NpxFrame->U.FxArea.DataSelector =
|
|||
|
ContextFrame->FloatSave.DataSelector;
|
|||
|
|
|||
|
//
|
|||
|
// Fxrstor format has each FP register in 128 bits (16 bytes)
|
|||
|
// where as fnsave saves each FP register in 80 bits (10 bytes)
|
|||
|
//
|
|||
|
RtlZeroMemory ((PVOID)&NpxFrame->U.FxArea.RegisterArea[0],
|
|||
|
SIZE_OF_FX_REGISTERS
|
|||
|
);
|
|||
|
|
|||
|
for (i = 0; i < NUMBER_OF_FP_REGISTERS; i++) {
|
|||
|
for (j = 0; j < BYTES_PER_FP_REGISTER; j++) {
|
|||
|
NpxFrame->U.FxArea.RegisterArea[i*BYTES_PER_FX_REGISTER+j] =
|
|||
|
ContextFrame->FloatSave.RegisterArea[i*BYTES_PER_FP_REGISTER+j];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
NpxFrame->U.FnArea.ControlWord =
|
|||
|
ContextFrame->FloatSave.ControlWord;
|
|||
|
NpxFrame->U.FnArea.StatusWord =
|
|||
|
ContextFrame->FloatSave.StatusWord;
|
|||
|
NpxFrame->U.FnArea.TagWord =
|
|||
|
ContextFrame->FloatSave.TagWord;
|
|||
|
NpxFrame->U.FnArea.ErrorOffset =
|
|||
|
ContextFrame->FloatSave.ErrorOffset;
|
|||
|
NpxFrame->U.FnArea.ErrorSelector =
|
|||
|
ContextFrame->FloatSave.ErrorSelector;
|
|||
|
NpxFrame->U.FnArea.DataOffset =
|
|||
|
ContextFrame->FloatSave.DataOffset;
|
|||
|
NpxFrame->U.FnArea.DataSelector =
|
|||
|
ContextFrame->FloatSave.DataSelector;
|
|||
|
|
|||
|
for (i = 0; i < SIZE_OF_80387_REGISTERS; i++) {
|
|||
|
NpxFrame->U.FnArea.RegisterArea[i] =
|
|||
|
ContextFrame->FloatSave.RegisterArea[i];
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure only valid floating state bits are moved to Cr0NpxState.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->Cr0NpxState &= ~(CR0_EM | CR0_MP | CR0_TS);
|
|||
|
|
|||
|
//
|
|||
|
// Only let VDMs turn on the EM bit. The kernel can't do
|
|||
|
// anything for FLAT apps
|
|||
|
//
|
|||
|
if (PsGetCurrentProcess()->VdmObjects != NULL) {
|
|||
|
NpxFrame->Cr0NpxState |= ContextFrame->FloatSave.Cr0NpxState &
|
|||
|
(CR0_EM | CR0_MP);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (PsGetCurrentProcess()->VdmObjects != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// This is a special hack to allow SetContext for VDMs to
|
|||
|
// turn on/off it's CR0_EM bit.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->Cr0NpxState &= ~(CR0_MP | CR0_TS | CR0_EM | CR0_PE);
|
|||
|
NpxFrame->Cr0NpxState |=
|
|||
|
VdmUserCr0MapIn[ContextFrame->FloatSave.Cr0NpxState & (CR0_EM | CR0_MP)];
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The 80387 is being emulated by the R3 emulator.
|
|||
|
// ** The only time the Npx state is ever obtained or set is
|
|||
|
// ** for userlevel handling. Current Irql must be 0 or 1.
|
|||
|
// And the context being set must be for the current thread.
|
|||
|
// Go smash the floatingpoint context into the R3 emulator's
|
|||
|
// data area.
|
|||
|
//
|
|||
|
#if DBG
|
|||
|
OldIrql = KeRaiseIrqlToSynchLevel();
|
|||
|
Pcr = KeGetPcr();
|
|||
|
ASSERT (Pcr->Prcb->CurrentThread->Teb == Pcr->NtTib.Self);
|
|||
|
KeLowerIrql (OldIrql);
|
|||
|
#endif
|
|||
|
|
|||
|
StateSaved = KiNpxFrameToEm87State(&ContextFrame->FloatSave);
|
|||
|
if (StateSaved) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure only valid floating state bits are moved to
|
|||
|
// Cr0NpxState. Since we are emulating, don't allow
|
|||
|
// resetting CR0_EM.
|
|||
|
//
|
|||
|
|
|||
|
NpxFrame->Cr0NpxState &= ~(CR0_MP | CR0_TS);
|
|||
|
NpxFrame->Cr0NpxState |=
|
|||
|
ContextFrame->FloatSave.Cr0NpxState & CR0_MP;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set debug register state if specified. If previous mode is user
|
|||
|
// mode (i.e. it's a user frame we're setting) and if effect will be to
|
|||
|
// cause at least one of the LE (local enable) bits in Dr7 to be
|
|||
|
// set (i.e. at least one of Dr0,1,2,3 are active) then set DebugActive
|
|||
|
// in the thread object to true. Otherwise set it to false.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) {
|
|||
|
|
|||
|
TrapFrame->Dr0 = SANITIZE_DRADDR(ContextFrame->Dr0, PreviousMode);
|
|||
|
TrapFrame->Dr1 = SANITIZE_DRADDR(ContextFrame->Dr1, PreviousMode);
|
|||
|
TrapFrame->Dr2 = SANITIZE_DRADDR(ContextFrame->Dr2, PreviousMode);
|
|||
|
TrapFrame->Dr3 = SANITIZE_DRADDR(ContextFrame->Dr3, PreviousMode);
|
|||
|
TrapFrame->Dr6 = SANITIZE_DR6(ContextFrame->Dr6, PreviousMode);
|
|||
|
TrapFrame->Dr7 = SANITIZE_DR7(ContextFrame->Dr7, PreviousMode);
|
|||
|
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
KeGetPcr()->DebugActive = KeGetCurrentThread()->DebugActive =
|
|||
|
(BOOLEAN)((ContextFrame->Dr7 & DR7_ACTIVE) != 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If thread is supposed to have IOPL, then force it on in eflags
|
|||
|
//
|
|||
|
if (KeGetCurrentThread()->Iopl) {
|
|||
|
TrapFrame->EFlags |= (EFLAGS_IOPL_MASK & -1); // IOPL = 3
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiDispatchException (
|
|||
|
IN PEXCEPTION_RECORD ExceptionRecord,
|
|||
|
IN PKEXCEPTION_FRAME ExceptionFrame,
|
|||
|
IN PKTRAP_FRAME TrapFrame,
|
|||
|
IN KPROCESSOR_MODE PreviousMode,
|
|||
|
IN BOOLEAN FirstChance
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called to dispatch an exception to the proper mode and
|
|||
|
to cause the exception dispatcher to be called. If the previous mode is
|
|||
|
kernel, then the exception dispatcher is called directly to process the
|
|||
|
exception. Otherwise the exception record, exception frame, and trap
|
|||
|
frame contents are copied to the user mode stack. The contents of the
|
|||
|
exception frame and trap are then modified such that when control is
|
|||
|
returned, execution will commense in user mode in a routine which will
|
|||
|
call the exception dispatcher.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ExceptionRecord - Supplies a pointer to an exception record.
|
|||
|
|
|||
|
ExceptionFrame - Supplies a pointer to an exception frame. For NT386,
|
|||
|
this should be NULL.
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame.
|
|||
|
|
|||
|
PreviousMode - Supplies the previous processor mode.
|
|||
|
|
|||
|
FirstChance - Supplies a boolean value that specifies whether this is
|
|||
|
the first (TRUE) or second (FALSE) chance for the exception.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CONTEXT ContextFrame;
|
|||
|
EXCEPTION_RECORD ExceptionRecord1, ExceptionRecord2;
|
|||
|
LONG Length;
|
|||
|
ULONG UserStack1;
|
|||
|
ULONG UserStack2;
|
|||
|
|
|||
|
//
|
|||
|
// Move machine state from trap and exception frames to a context frame,
|
|||
|
// and increment the number of exceptions dispatched.
|
|||
|
//
|
|||
|
|
|||
|
KeGetCurrentPrcb()->KeExceptionDispatchCount += 1;
|
|||
|
ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
|
|||
|
if (PreviousMode == UserMode) {
|
|||
|
//
|
|||
|
// For usermode exceptions always try to dispatch the floating
|
|||
|
// point state. This allows expection handlers & debuggers to
|
|||
|
// examine/edit the npx context if required. Plus it allows
|
|||
|
// exception handlers to use fp instructions without detroying
|
|||
|
// the npx state at the time of the exception.
|
|||
|
//
|
|||
|
// Note: If there's no 80387, ContextTo/FromKFrames will use the
|
|||
|
// emulator's current state. If the emulator can not give the
|
|||
|
// current state, then the context_floating_point bit will be
|
|||
|
// turned off by ContextFromKFrames.
|
|||
|
//
|
|||
|
|
|||
|
ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT;
|
|||
|
if (KeI386XMMIPresent) {
|
|||
|
ContextFrame.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
|
|||
|
|
|||
|
//
|
|||
|
// if it is BREAK_POINT exception, we subtract 1 from EIP and report
|
|||
|
// the updated EIP to user. This is because Cruiser requires EIP
|
|||
|
// points to the int 3 instruction (not the instruction following int 3).
|
|||
|
// In this case, BreakPoint exception is fatal. Otherwise we will step
|
|||
|
// on the int 3 over and over again, if user does not handle it
|
|||
|
//
|
|||
|
// if the BREAK_POINT occured in V86 mode, the debugger running in the
|
|||
|
// VDM will expect CS:EIP to point after the exception (the way the
|
|||
|
// processor left it. this is also true for protected mode dos
|
|||
|
// app debuggers. We will need a way to detect this.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
// if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
|||
|
// !(ContextFrame.EFlags & EFLAGS_V86_MASK)) {
|
|||
|
|
|||
|
switch (ExceptionRecord->ExceptionCode) {
|
|||
|
case STATUS_BREAKPOINT:
|
|||
|
ContextFrame.Eip--;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Select the method of handling the exception based on the previous mode.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT ((
|
|||
|
!((PreviousMode == KernelMode) &&
|
|||
|
(ContextFrame.EFlags & EFLAGS_V86_MASK))
|
|||
|
));
|
|||
|
|
|||
|
if (PreviousMode == KernelMode) {
|
|||
|
|
|||
|
//
|
|||
|
// Previous mode was kernel.
|
|||
|
//
|
|||
|
// If the kernel debugger is active, then give the kernel debugger the
|
|||
|
// first chance to handle the exception. If the kernel debugger handles
|
|||
|
// the exception, then continue execution. Else attempt to dispatch the
|
|||
|
// exception to a frame based handler. If a frame based handler handles
|
|||
|
// the exception, then continue execution.
|
|||
|
//
|
|||
|
// If a frame based handler does not handle the exception,
|
|||
|
// give the kernel debugger a second chance, if it's present.
|
|||
|
//
|
|||
|
// If the exception is still unhandled, call KeBugCheck().
|
|||
|
//
|
|||
|
|
|||
|
if (FirstChance == TRUE) {
|
|||
|
|
|||
|
if ((KiDebugRoutine != NULL) &&
|
|||
|
(((KiDebugRoutine) (TrapFrame,
|
|||
|
ExceptionFrame,
|
|||
|
ExceptionRecord,
|
|||
|
&ContextFrame,
|
|||
|
PreviousMode,
|
|||
|
FALSE)) != FALSE)) {
|
|||
|
|
|||
|
goto Handled1;
|
|||
|
}
|
|||
|
|
|||
|
// Kernel debugger didn't handle exception.
|
|||
|
|
|||
|
if (RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) {
|
|||
|
goto Handled1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is the second chance to handle the exception.
|
|||
|
//
|
|||
|
|
|||
|
if ((KiDebugRoutine != NULL) &&
|
|||
|
(((KiDebugRoutine) (TrapFrame,
|
|||
|
ExceptionFrame,
|
|||
|
ExceptionRecord,
|
|||
|
&ContextFrame,
|
|||
|
PreviousMode,
|
|||
|
TRUE)) != FALSE)) {
|
|||
|
|
|||
|
goto Handled1;
|
|||
|
}
|
|||
|
|
|||
|
KeBugCheckEx(
|
|||
|
KERNEL_MODE_EXCEPTION_NOT_HANDLED,
|
|||
|
ExceptionRecord->ExceptionCode,
|
|||
|
(ULONG)ExceptionRecord->ExceptionAddress,
|
|||
|
(ULONG)TrapFrame,
|
|||
|
0);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Previous mode was user.
|
|||
|
//
|
|||
|
// If this is the first chance and the current process has a debugger
|
|||
|
// port, then send a message to the debugger port and wait for a reply.
|
|||
|
// If the debugger handles the exception, then continue execution. Else
|
|||
|
// transfer the exception information to the user stack, transition to
|
|||
|
// user mode, and attempt to dispatch the exception to a frame based
|
|||
|
// handler. If a frame based handler handles the exception, then continue
|
|||
|
// execution with the continue system service. Else execute the
|
|||
|
// NtRaiseException system service with FirstChance == FALSE, which
|
|||
|
// will call this routine a second time to process the exception.
|
|||
|
//
|
|||
|
// If this is the second chance and the current process has a debugger
|
|||
|
// port, then send a message to the debugger port and wait for a reply.
|
|||
|
// If the debugger handles the exception, then continue execution. Else
|
|||
|
// if the current process has a subsystem port, then send a message to
|
|||
|
// the subsystem port and wait for a reply. If the subsystem handles the
|
|||
|
// exception, then continue execution. Else terminate the thread.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if (FirstChance == TRUE) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the first chance to handle the exception.
|
|||
|
//
|
|||
|
|
|||
|
if ((KiDebugRoutine != NULL) &&
|
|||
|
((PsGetCurrentProcess()->DebugPort == NULL) ||
|
|||
|
(KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode))))
|
|||
|
{
|
|||
|
//
|
|||
|
// Now dispatch the fault to the kernel debugger.
|
|||
|
//
|
|||
|
|
|||
|
if ((((KiDebugRoutine) (TrapFrame,
|
|||
|
ExceptionFrame,
|
|||
|
ExceptionRecord,
|
|||
|
&ContextFrame,
|
|||
|
PreviousMode,
|
|||
|
FALSE)) != FALSE)) {
|
|||
|
|
|||
|
goto Handled1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
|
|||
|
goto Handled2;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Transfer exception information to the user stack, transition
|
|||
|
// to user mode, and attempt to dispatch the exception to a frame
|
|||
|
// based handler.
|
|||
|
|
|||
|
repeat:
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// If the SS segment is not 32 bit flat, there is no point
|
|||
|
// to dispatch exception to frame based exception handler.
|
|||
|
//
|
|||
|
|
|||
|
if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||
|
|||
|
TrapFrame->EFlags & EFLAGS_V86_MASK ) {
|
|||
|
ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;
|
|||
|
ExceptionRecord2.ExceptionFlags = 0;
|
|||
|
ExceptionRecord2.NumberParameters = 0;
|
|||
|
ExRaiseException(&ExceptionRecord2);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute length of context record and new aligned user stack
|
|||
|
// pointer.
|
|||
|
//
|
|||
|
|
|||
|
Length = (sizeof(CONTEXT) + CONTEXT_ROUND) & ~CONTEXT_ROUND;
|
|||
|
UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - Length;
|
|||
|
|
|||
|
//
|
|||
|
// Probe user stack area for writability and then transfer the
|
|||
|
// context record to the user stack.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForWrite((PCHAR)UserStack1, Length, CONTEXT_ALIGN);
|
|||
|
RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
|
|||
|
|
|||
|
//
|
|||
|
// Compute length of exception record and new aligned stack
|
|||
|
// address.
|
|||
|
//
|
|||
|
|
|||
|
Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS -
|
|||
|
ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) &
|
|||
|
(~3);
|
|||
|
UserStack2 = UserStack1 - Length;
|
|||
|
|
|||
|
//
|
|||
|
// Probe user stack area for writeability and then transfer the
|
|||
|
// context record to the user stack area.
|
|||
|
// N.B. The probing length is Length+8 because there are two
|
|||
|
// arguments need to be pushed to user stack later.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG));
|
|||
|
RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);
|
|||
|
|
|||
|
//
|
|||
|
// Push address of exception record, context record to the
|
|||
|
// user stack. They are the two parameters required by
|
|||
|
// _KiUserExceptionDispatch.
|
|||
|
//
|
|||
|
|
|||
|
*(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1;
|
|||
|
*(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
|
|||
|
|
|||
|
//
|
|||
|
// Set new stack pointer to the trap frame.
|
|||
|
//
|
|||
|
|
|||
|
KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA);
|
|||
|
KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));
|
|||
|
|
|||
|
//
|
|||
|
// Force correct R3 selectors into TrapFrame.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode);
|
|||
|
TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
|
|||
|
TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
|
|||
|
TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode);
|
|||
|
TrapFrame->SegGs = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Set the address of the exception routine that will call the
|
|||
|
// exception dispatcher and then return to the trap handler.
|
|||
|
// The trap handler will restore the exception and trap frame
|
|||
|
// context and continue execution in the routine that will
|
|||
|
// call the exception dispatcher.
|
|||
|
//
|
|||
|
|
|||
|
TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
|
|||
|
return;
|
|||
|
|
|||
|
} except (KiCopyInformation(&ExceptionRecord1,
|
|||
|
(GetExceptionInformation())->ExceptionRecord)) {
|
|||
|
|
|||
|
//
|
|||
|
// If the exception is a stack overflow, then attempt
|
|||
|
// to raise the stack overflow exception. Otherwise,
|
|||
|
// the user's stack is not accessible, or is misaligned,
|
|||
|
// and second chance processing is performed.
|
|||
|
//
|
|||
|
|
|||
|
if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) {
|
|||
|
ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress;
|
|||
|
RtlCopyMemory((PVOID)ExceptionRecord,
|
|||
|
&ExceptionRecord1, sizeof(EXCEPTION_RECORD));
|
|||
|
goto repeat;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is the second chance to handle the exception.
|
|||
|
//
|
|||
|
|
|||
|
if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
|
|||
|
goto Handled2;
|
|||
|
} else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
|
|||
|
goto Handled2;
|
|||
|
} else {
|
|||
|
ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
|
|||
|
KeBugCheckEx(
|
|||
|
KERNEL_MODE_EXCEPTION_NOT_HANDLED,
|
|||
|
ExceptionRecord->ExceptionCode,
|
|||
|
(ULONG)ExceptionRecord->ExceptionAddress,
|
|||
|
(ULONG)TrapFrame,
|
|||
|
0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Move machine state from context frame to trap and exception frames and
|
|||
|
// then return to continue execution with the restored state.
|
|||
|
//
|
|||
|
|
|||
|
Handled1:
|
|||
|
|
|||
|
KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,
|
|||
|
ContextFrame.ContextFlags, PreviousMode);
|
|||
|
|
|||
|
//
|
|||
|
// Exception was handled by the debugger or the associated subsystem
|
|||
|
// and state was modified, if necessary, using the get state and set
|
|||
|
// state capabilities. Therefore the context frame does not need to
|
|||
|
// be transfered to the trap and exception frames.
|
|||
|
//
|
|||
|
|
|||
|
Handled2:
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
KiCopyInformation (
|
|||
|
IN OUT PEXCEPTION_RECORD ExceptionRecord1,
|
|||
|
IN PEXCEPTION_RECORD ExceptionRecord2
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called from an exception filter to copy the exception
|
|||
|
information from one exception record to another when an exception occurs.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ExceptionRecord1 - Supplies a pointer to the destination exception record.
|
|||
|
|
|||
|
ExceptionRecord2 - Supplies a pointer to the source exception record.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A value of EXCEPTION_EXECUTE_HANDLER is returned as the function value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Copy one exception record to another and return value that causes
|
|||
|
// an exception handler to be executed.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory((PVOID)ExceptionRecord1,
|
|||
|
(PVOID)ExceptionRecord2,
|
|||
|
sizeof(EXCEPTION_RECORD));
|
|||
|
|
|||
|
return EXCEPTION_EXECUTE_HANDLER;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
KeRaiseUserException(
|
|||
|
IN NTSTATUS ExceptionCode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function causes an exception to be raised in the calling thread's user-mode
|
|||
|
context. It does this by editing the trap frame the kernel was entered with to
|
|||
|
point to trampoline code that raises the requested exception.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ExceptionCode - Supplies the status value to be used as the exception
|
|||
|
code for the exception that is to be raised.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The status value that should be returned by the caller.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PKTHREAD Thread;
|
|||
|
PKTRAP_FRAME TrapFrame;
|
|||
|
PTEB Teb;
|
|||
|
ULONG PreviousEsp;
|
|||
|
|
|||
|
ASSERT(KeGetPreviousMode() == UserMode);
|
|||
|
|
|||
|
Thread = KeGetCurrentThread();
|
|||
|
TrapFrame = Thread->TrapFrame;
|
|||
|
if (TrapFrame == NULL) {
|
|||
|
return ExceptionCode;
|
|||
|
}
|
|||
|
|
|||
|
Teb = (PTEB)Thread->Teb;
|
|||
|
|
|||
|
//
|
|||
|
// In order to create the correct call stack, we push the old return
|
|||
|
// address onto the stack. The status code to be raised is passed
|
|||
|
// in the TEB.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
Teb->ExceptionCode = ExceptionCode;
|
|||
|
|
|||
|
PreviousEsp = KiEspFromTrapFrame (TrapFrame) - sizeof (ULONG);
|
|||
|
|
|||
|
ProbeForWriteSmallStructure ((PLONG)PreviousEsp, sizeof (LONG), sizeof (UCHAR));
|
|||
|
*(PLONG)PreviousEsp = TrapFrame->Eip;
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
return(ExceptionCode);
|
|||
|
}
|
|||
|
|
|||
|
KiEspToTrapFrame (TrapFrame, PreviousEsp);
|
|||
|
|
|||
|
TrapFrame->Eip = (ULONG)KeRaiseUserExceptionDispatcher;
|
|||
|
|
|||
|
return ExceptionCode;
|
|||
|
}
|