2020-09-30 17:17:25 +02:00

461 lines
12 KiB
C

/*++
Copyright (c) 1989-2001 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.
--*/
#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
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 & 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);
//
// Kernel mode esp can't be lowered or iret emulation will fail
//
if (Esp < OldEsp)
KeBugCheck(SET_OF_INVALID_CONTEXT);
//
// 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;
}
}
}
VOID
KeContextFromKframes (
IN PKTRAP_FRAME TrapFrame,
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.
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;
//
// 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 = KGDT_R0_DATA;
ContextFrame->Esp = KiEspFromTrapFrame(TrapFrame);
}
//
// 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;
}
//
// Set extended register contents if specified.
//
if (((ContextFrame->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) ||
((ContextFrame->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)) {
KiFlushNPXState();
NpxFrame = (PFX_SAVE_AREA)KeGetPcr()->NtTib.StackBase;
RtlCopyMemory( &ContextFrame->FloatSave,
&NpxFrame->FloatSave,
sizeof(FLOATING_SAVE_AREA) );
}
}
VOID
KeContextToKframes (
IN OUT PKTRAP_FRAME TrapFrame,
IN PCONTEXT ContextFrame,
IN ULONG ContextFlags
)
/*++
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.
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.
Return Value:
None.
--*/
{
PFX_SAVE_AREA NpxFrame;
//
// Set control information if specified.
//
if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
//
// 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);
TrapFrame->Ebp = ContextFrame->Ebp;
TrapFrame->Eip = ContextFrame->Eip;
KiEspToTrapFrame(TrapFrame, ContextFrame->Esp);
}
//
// Set integer registers contents if specified.
//
if ((ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
//
// Set integer registers edi, esi, ebx, edx, ecx, eax.
//
// Can NOT call RtlMoveMemory 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.
//
if (((ContextFrame->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) ||
((ContextFrame->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)) {
KiFlushNPXState();
NpxFrame = (PFX_SAVE_AREA)KeGetPcr()->NtTib.StackBase;
RtlCopyMemory( &NpxFrame->FloatSave,
&ContextFrame->FloatSave,
sizeof(FLOATING_SAVE_AREA) );
//
// Make sure only valid floating state bits are moved to Cr0NpxState.
//
NpxFrame->FloatSave.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->FloatSave.MXCsr = SANITIZE_MXCSR(NpxFrame->FloatSave.MXCsr);
}
}
VOID
KiDispatchException (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
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.
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;
//
// Move machine state from trap and exception frames to a context frame,
// and increment the number of exceptions dispatched.
//
ContextFrame.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER |
CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
KeContextFromKframes(TrapFrame, &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.
//
//
switch (ExceptionRecord->ExceptionCode) {
case STATUS_BREAKPOINT:
ContextFrame.Eip--;
break;
}
//
// 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,
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,
TRUE)) != FALSE)) {
goto Handled1;
}
KeBugCheckEx(
KMODE_EXCEPTION_NOT_HANDLED,
ExceptionRecord->ExceptionCode,
(ULONG)ExceptionRecord->ExceptionAddress,
ExceptionRecord->ExceptionInformation[0],
ExceptionRecord->ExceptionInformation[1]
);
//
// Move machine state from context frame to trap and exception frames and
// then return to continue execution with the restored state.
//
Handled1:
KeContextToKframes(TrapFrame, &ContextFrame, ContextFrame.ContextFlags);
}