897 lines
28 KiB
C
897 lines
28 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1990 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) 3-Apr-1990
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode only.
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "ki.h"
|
||
|
#pragma hdrstop
|
||
|
#define HEADER_FILE
|
||
|
#include "kxmips.h"
|
||
|
|
||
|
|
||
|
// Define multiply overflow and divide by zero breakpoint instruction values.
|
||
|
|
||
|
|
||
|
#define KDDEBUG_BREAKPOINT ((SPEC_OP << 26) | (BREAKIN_BREAKPOINT << 16) | BREAK_OP)
|
||
|
#define DIVIDE_BREAKPOINT ((SPEC_OP << 26) | (DIVIDE_BY_ZERO_BREAKPOINT << 16) | BREAK_OP)
|
||
|
#define MULTIPLY_BREAKPOINT ((SPEC_OP << 26) | (MULTIPLY_OVERFLOW_BREAKPOINT << 16) | BREAK_OP)
|
||
|
#define OVERFLOW_BREAKPOINT ((SPEC_OP << 26) | (DIVIDE_OVERFLOW_BREAKPOINT << 16) | BREAK_OP)
|
||
|
|
||
|
|
||
|
// Define external kernel breakpoint instruction value.
|
||
|
|
||
|
|
||
|
#define KERNEL_BREAKPOINT_INSTRUCTION 0x16000d
|
||
|
|
||
|
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.
|
||
|
|
||
|
ContextFrame - Supplies a pointer to the context frame that receives the
|
||
|
context copied from the trap and exception frames.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
ULONG ContextFlags;
|
||
|
|
||
|
|
||
|
// Set control information if specified.
|
||
|
|
||
|
|
||
|
ContextFlags = ContextFrame->ContextFlags;
|
||
|
if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
|
||
|
|
||
|
|
||
|
// Set integer register gp, ra, sp, FIR, and PSR.
|
||
|
|
||
|
|
||
|
ContextFrame->XIntGp = TrapFrame->XIntGp;
|
||
|
ContextFrame->XIntSp = TrapFrame->XIntSp;
|
||
|
ContextFrame->Fir = TrapFrame->Fir;
|
||
|
ContextFrame->Psr = TrapFrame->Psr;
|
||
|
ContextFrame->XIntRa = TrapFrame->XIntRa;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set integer register contents if specified.
|
||
|
|
||
|
|
||
|
if ((ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
|
||
|
|
||
|
|
||
|
// Set integer registers zero, and, at - t9, k0, k1, lo, and hi.
|
||
|
|
||
|
|
||
|
ContextFrame->XIntZero = 0;
|
||
|
ContextFrame->XIntAt = TrapFrame->XIntAt;
|
||
|
ContextFrame->XIntV0 = TrapFrame->XIntV0;
|
||
|
ContextFrame->XIntV1 = TrapFrame->XIntV1;
|
||
|
ContextFrame->XIntA0 = TrapFrame->XIntA0;
|
||
|
ContextFrame->XIntA1 = TrapFrame->XIntA1;
|
||
|
ContextFrame->XIntA2 = TrapFrame->XIntA2;
|
||
|
ContextFrame->XIntA3 = TrapFrame->XIntA3;
|
||
|
ContextFrame->XIntT0 = TrapFrame->XIntT0;
|
||
|
ContextFrame->XIntT1 = TrapFrame->XIntT1;
|
||
|
ContextFrame->XIntT2 = TrapFrame->XIntT2;
|
||
|
ContextFrame->XIntT3 = TrapFrame->XIntT3;
|
||
|
ContextFrame->XIntT4 = TrapFrame->XIntT4;
|
||
|
ContextFrame->XIntT5 = TrapFrame->XIntT5;
|
||
|
ContextFrame->XIntT6 = TrapFrame->XIntT6;
|
||
|
ContextFrame->XIntT7 = TrapFrame->XIntT7;
|
||
|
ContextFrame->XIntT8 = TrapFrame->XIntT8;
|
||
|
ContextFrame->XIntT9 = TrapFrame->XIntT9;
|
||
|
ContextFrame->XIntK0 = 0;
|
||
|
ContextFrame->XIntK1 = 0;
|
||
|
ContextFrame->XIntLo = TrapFrame->XIntLo;
|
||
|
ContextFrame->XIntHi = TrapFrame->XIntHi;
|
||
|
|
||
|
|
||
|
// Set integer registers s0 - s7, and s8.
|
||
|
|
||
|
|
||
|
ContextFrame->XIntS0 = TrapFrame->XIntS0;
|
||
|
ContextFrame->XIntS1 = TrapFrame->XIntS1;
|
||
|
ContextFrame->XIntS2 = TrapFrame->XIntS2;
|
||
|
ContextFrame->XIntS3 = TrapFrame->XIntS3;
|
||
|
ContextFrame->XIntS4 = TrapFrame->XIntS4;
|
||
|
ContextFrame->XIntS5 = TrapFrame->XIntS5;
|
||
|
ContextFrame->XIntS6 = TrapFrame->XIntS6;
|
||
|
ContextFrame->XIntS7 = TrapFrame->XIntS7;
|
||
|
ContextFrame->XIntS8 = TrapFrame->XIntS8;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set floating register contents if specified.
|
||
|
|
||
|
|
||
|
if ((ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) {
|
||
|
|
||
|
|
||
|
// Set floating registers f0 - f19.
|
||
|
|
||
|
|
||
|
RtlMoveMemory(&ContextFrame->FltF0, &TrapFrame->FltF0,
|
||
|
sizeof(ULONG) * (20));
|
||
|
|
||
|
|
||
|
// Set floating registers f20 - f31.
|
||
|
|
||
|
|
||
|
RtlMoveMemory(&ContextFrame->FltF20, &ExceptionFrame->FltF20,
|
||
|
sizeof(ULONG) * (12));
|
||
|
|
||
|
|
||
|
// Set floating status register.
|
||
|
|
||
|
|
||
|
ContextFrame->Fsr = TrapFrame->Fsr;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
|
||
|
// Set control information if specified.
|
||
|
|
||
|
|
||
|
if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
|
||
|
|
||
|
|
||
|
// Set integer register gp, sp, ra, FIR, and PSR.
|
||
|
|
||
|
|
||
|
TrapFrame->XIntGp = ContextFrame->XIntGp;
|
||
|
TrapFrame->XIntSp = ContextFrame->XIntSp;
|
||
|
TrapFrame->Fir = ContextFrame->Fir;
|
||
|
TrapFrame->Psr = SANITIZE_PSR(ContextFrame->Psr, PreviousMode);
|
||
|
TrapFrame->XIntRa = ContextFrame->XIntRa;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set integer registers contents if specified.
|
||
|
|
||
|
|
||
|
if ((ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
|
||
|
|
||
|
|
||
|
// Set integer registers at - t9, lo, and hi.
|
||
|
|
||
|
|
||
|
TrapFrame->XIntAt = ContextFrame->XIntAt;
|
||
|
TrapFrame->XIntV0 = ContextFrame->XIntV0;
|
||
|
TrapFrame->XIntV1 = ContextFrame->XIntV1;
|
||
|
TrapFrame->XIntA0 = ContextFrame->XIntA0;
|
||
|
TrapFrame->XIntA1 = ContextFrame->XIntA1;
|
||
|
TrapFrame->XIntA2 = ContextFrame->XIntA2;
|
||
|
TrapFrame->XIntA3 = ContextFrame->XIntA3;
|
||
|
TrapFrame->XIntT0 = ContextFrame->XIntT0;
|
||
|
TrapFrame->XIntT1 = ContextFrame->XIntT1;
|
||
|
TrapFrame->XIntT2 = ContextFrame->XIntT2;
|
||
|
TrapFrame->XIntT3 = ContextFrame->XIntT3;
|
||
|
TrapFrame->XIntT4 = ContextFrame->XIntT4;
|
||
|
TrapFrame->XIntT5 = ContextFrame->XIntT5;
|
||
|
TrapFrame->XIntT6 = ContextFrame->XIntT6;
|
||
|
TrapFrame->XIntT7 = ContextFrame->XIntT7;
|
||
|
TrapFrame->XIntT8 = ContextFrame->XIntT8;
|
||
|
TrapFrame->XIntT9 = ContextFrame->XIntT9;
|
||
|
TrapFrame->XIntLo = ContextFrame->XIntLo;
|
||
|
TrapFrame->XIntHi = ContextFrame->XIntHi;
|
||
|
|
||
|
|
||
|
// Set integer registers s0 - s7, and s8.
|
||
|
|
||
|
|
||
|
TrapFrame->XIntS0 = ContextFrame->XIntS0;
|
||
|
TrapFrame->XIntS1 = ContextFrame->XIntS1;
|
||
|
TrapFrame->XIntS2 = ContextFrame->XIntS2;
|
||
|
TrapFrame->XIntS3 = ContextFrame->XIntS3;
|
||
|
TrapFrame->XIntS4 = ContextFrame->XIntS4;
|
||
|
TrapFrame->XIntS5 = ContextFrame->XIntS5;
|
||
|
TrapFrame->XIntS6 = ContextFrame->XIntS6;
|
||
|
TrapFrame->XIntS7 = ContextFrame->XIntS7;
|
||
|
TrapFrame->XIntS8 = ContextFrame->XIntS8;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set floating register contents if specified.
|
||
|
|
||
|
|
||
|
if ((ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) {
|
||
|
|
||
|
|
||
|
// Set floating registers f0 - f19.
|
||
|
|
||
|
|
||
|
RtlMoveMemory(&TrapFrame->FltF0, &ContextFrame->FltF0,
|
||
|
sizeof(ULONG) * (20));
|
||
|
|
||
|
|
||
|
// Set floating registers f20 - f31.
|
||
|
|
||
|
|
||
|
RtlMoveMemory(&ExceptionFrame->FltF20, &ContextFrame->FltF20,
|
||
|
sizeof(ULONG) * (12));
|
||
|
|
||
|
|
||
|
// Set floating status register.
|
||
|
|
||
|
|
||
|
TrapFrame->Fsr = SANITIZE_FSR(ContextFrame->Fsr, PreviousMode);
|
||
|
}
|
||
|
|
||
|
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 exception is a data misalignment, the previous mode is user, this
|
||
|
is the first chance for handling the exception, and the current thread
|
||
|
has enabled automatic alignment fixup, then an attempt is made to emulate
|
||
|
the unaligned reference. Data misalignment exceptions are never emulated
|
||
|
for kernel mode.
|
||
|
|
||
|
If the exception is a floating exception (N.B. the pseudo status
|
||
|
STATUS_FLOAT_STACK_CHECK is used to signify this and is converted to the
|
||
|
proper code by the floating emulation routine), then an attempt is made
|
||
|
to emulate the floating operation if it is not implemented.
|
||
|
|
||
|
If the exception is neither a data misalignment nor a floating point
|
||
|
exception and the 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.
|
||
|
|
||
|
TrapFrame - Supplies a pointer to a trap frame.
|
||
|
|
||
|
PreviousMode - Supplies the previous processor mode.
|
||
|
|
||
|
FirstChance - Supplies a boolean variable that specifies whether this
|
||
|
is the first (TRUE) or second (FALSE) time that this exception has
|
||
|
been processed.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
CONTEXT ContextFrame;
|
||
|
PULONG Destination;
|
||
|
EXCEPTION_RECORD ExceptionRecord1;
|
||
|
ULONG Index;
|
||
|
LONG Length;
|
||
|
PULONGLONG Source;
|
||
|
BOOLEAN UserApcPending;
|
||
|
ULONG UserStack1;
|
||
|
ULONG UserStack2;
|
||
|
|
||
|
|
||
|
// If the exception is an access violation, and the previous mode is
|
||
|
// user mode, then attempt to emulate a load or store operation if
|
||
|
// the exception address is at the end of a page.
|
||
|
|
||
|
// N.B. The following is a workaround for a r4000 chip bug where an
|
||
|
// address privilege violation is reported as a access violation
|
||
|
// on a load or store instruction that is the last instruction
|
||
|
// in a page.
|
||
|
|
||
|
|
||
|
if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) &&
|
||
|
(((ULONG)ExceptionRecord->ExceptionAddress & 0xffc) == 0xffc) &&
|
||
|
(PreviousMode != KernelMode) &&
|
||
|
(KiEmulateReference(ExceptionRecord, ExceptionFrame, TrapFrame) != FALSE)) {
|
||
|
KeGetCurrentPrcb()->KeAlignmentFixupCount += 1;
|
||
|
goto Handled2;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If the exception is a data bus error, then process the error.
|
||
|
|
||
|
// N.B. A special exception code is used to signal a data bus error.
|
||
|
// This code is equivalent to the bug check code merged with a
|
||
|
// reserved facility code and the reserved bit set.
|
||
|
|
||
|
// N.B. If control returns, then it is assumed that the error has been
|
||
|
// corrected.
|
||
|
|
||
|
|
||
|
if (ExceptionRecord->ExceptionCode == (DATA_BUS_ERROR | 0xdfff0000)) {
|
||
|
|
||
|
|
||
|
// N.B. The following is a workaround for a r4000 chip bug where an
|
||
|
// address privilege violation is reported as a data bus error
|
||
|
// on a load or store instruction that is the last instruction
|
||
|
// in a page.
|
||
|
|
||
|
|
||
|
if ((ExceptionRecord->ExceptionInformation[1] < 0x80000000) &&
|
||
|
(((ULONG)ExceptionRecord->ExceptionAddress & 0xffc) == 0xffc) &&
|
||
|
(PreviousMode != KernelMode)) {
|
||
|
if (KiEmulateReference(ExceptionRecord, ExceptionFrame, TrapFrame) != FALSE) {
|
||
|
KeGetCurrentPrcb()->KeAlignmentFixupCount += 1;
|
||
|
goto Handled2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KiDataBusError(ExceptionRecord, ExceptionFrame, TrapFrame);
|
||
|
goto Handled2;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If the exception is an instruction bus error, then process the error.
|
||
|
|
||
|
// N.B. A special exception code is used to signal an instruction bus
|
||
|
// error. This code is equivalent to the bug check code merged
|
||
|
// with a reserved facility code and the reserved bit set.
|
||
|
|
||
|
// N.B. If control returns, then it is assumed that the error hand been
|
||
|
// corrected.
|
||
|
|
||
|
|
||
|
if (ExceptionRecord->ExceptionCode == (INSTRUCTION_BUS_ERROR | 0xdfff0000)) {
|
||
|
KiInstructionBusError(ExceptionRecord, ExceptionFrame, TrapFrame);
|
||
|
goto Handled2;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If the exception is a data misalignment, this is the first change for
|
||
|
// handling the exception, and the current thread has enabled automatic
|
||
|
// alignment fixup, then attempt to emulate the unaligned reference.
|
||
|
|
||
|
|
||
|
if ((ExceptionRecord->ExceptionCode == STATUS_DATATYPE_MISALIGNMENT) &&
|
||
|
(FirstChance != FALSE) &&
|
||
|
((KeGetCurrentThread()->AutoAlignment != FALSE) ||
|
||
|
(KeGetCurrentThread()->ApcState.Process->AutoAlignment != FALSE) ||
|
||
|
(((ExceptionRecord->ExceptionInformation[1] & 0x7fff0000) == 0x7fff0000) &&
|
||
|
(PreviousMode != KernelMode))) &&
|
||
|
(KiEmulateReference(ExceptionRecord, ExceptionFrame, TrapFrame) != FALSE)) {
|
||
|
KeGetCurrentPrcb()->KeAlignmentFixupCount += 1;
|
||
|
goto Handled2;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If the exception is a floating exception, then attempt to emulate the
|
||
|
// operation.
|
||
|
|
||
|
// N.B. The pseudo status STATUS_FLOAT_STACK_CHECK is used to signify
|
||
|
// that the exception is a floating exception and that this it the
|
||
|
// first chance for handling the exception. The floating emulation
|
||
|
// routine converts the status code to the proper floating status
|
||
|
// value.
|
||
|
|
||
|
|
||
|
if ((ExceptionRecord->ExceptionCode == STATUS_FLOAT_STACK_CHECK) &&
|
||
|
(KiEmulateFloating(ExceptionRecord, ExceptionFrame, TrapFrame) != FALSE)) {
|
||
|
TrapFrame->Fsr = SANITIZE_FSR(TrapFrame->Fsr, PreviousMode);
|
||
|
goto Handled2;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If the exception is a breakpoint, then translate it to an appropriate
|
||
|
// exception code if it is a division by zero or an integer overflow
|
||
|
// caused by multiplication.
|
||
|
|
||
|
|
||
|
if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) {
|
||
|
if (ExceptionRecord->ExceptionInformation[0] == DIVIDE_BREAKPOINT) {
|
||
|
ExceptionRecord->ExceptionCode = STATUS_INTEGER_DIVIDE_BY_ZERO;
|
||
|
|
||
|
} else if ((ExceptionRecord->ExceptionInformation[0] == MULTIPLY_BREAKPOINT) ||
|
||
|
(ExceptionRecord->ExceptionInformation[0] == OVERFLOW_BREAKPOINT)) {
|
||
|
ExceptionRecord->ExceptionCode = STATUS_INTEGER_OVERFLOW;
|
||
|
|
||
|
} else if (ExceptionRecord->ExceptionInformation[0] == KDDEBUG_BREAKPOINT) {
|
||
|
TrapFrame->Fir += 4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move machine state from trap and exception frames to a context frame,
|
||
|
// and increment the number of exceptions dispatched.
|
||
|
|
||
|
|
||
|
ContextFrame.ContextFlags = CONTEXT_FULL;
|
||
|
KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
|
||
|
KeGetCurrentPrcb()->KeExceptionDispatchCount += 1;
|
||
|
|
||
|
|
||
|
// Select the method of handling the exception based on the previous mode.
|
||
|
|
||
|
|
||
|
if (PreviousMode == KernelMode) {
|
||
|
|
||
|
|
||
|
// Previous mode was kernel.
|
||
|
|
||
|
// If this is the first chance, the kernel debugger is active, and
|
||
|
// the exception is a kernel breakpoint, then give the kernel debugger
|
||
|
// a chance to handle the exception.
|
||
|
|
||
|
// If this is the first chance and the kernel debugger is not active
|
||
|
// or does not handle the exception, then attempt to find a frame
|
||
|
// handler to handle the exception.
|
||
|
|
||
|
// If this is the second chance or the exception is not handled, then
|
||
|
// if the kernel debugger is active, then give the kernel debugger a
|
||
|
// second chance to handle the exception. If the kernel debugger does
|
||
|
// not handle the exception, then bug check.
|
||
|
|
||
|
|
||
|
if (FirstChance != FALSE) {
|
||
|
|
||
|
|
||
|
// If the kernel debugger is active, the exception is a breakpoint,
|
||
|
// and the breakpoint is handled by the kernel debugger, then give
|
||
|
// the kernel debugger a chance to handle the exception.
|
||
|
|
||
|
|
||
|
if ((KiDebugRoutine != NULL) &&
|
||
|
(ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
||
|
(KdIsThisAKdTrap(ExceptionRecord,
|
||
|
&ContextFrame,
|
||
|
KernelMode) != FALSE)) {
|
||
|
|
||
|
if (((KiDebugRoutine) (TrapFrame,
|
||
|
ExceptionFrame,
|
||
|
ExceptionRecord,
|
||
|
&ContextFrame,
|
||
|
KernelMode,
|
||
|
FALSE)) != FALSE) {
|
||
|
|
||
|
goto Handled1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// This is the first chance to handle the exception.
|
||
|
|
||
|
|
||
|
if (RtlDispatchException(ExceptionRecord, &ContextFrame) != FALSE) {
|
||
|
goto Handled1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// This is the second chance to handle the exception.
|
||
|
|
||
|
|
||
|
if (KiDebugRoutine != NULL) {
|
||
|
if (((KiDebugRoutine) (TrapFrame,
|
||
|
ExceptionFrame,
|
||
|
ExceptionRecord,
|
||
|
&ContextFrame,
|
||
|
PreviousMode,
|
||
|
TRUE)) != FALSE) {
|
||
|
goto Handled1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
|
||
|
ExceptionRecord->ExceptionCode,
|
||
|
(ULONG)ExceptionRecord->ExceptionAddress,
|
||
|
ExceptionRecord->ExceptionInformation[0],
|
||
|
ExceptionRecord->ExceptionInformation[1]);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
|
||
|
// Previous mode was user.
|
||
|
|
||
|
// If this is the first chance, the kernel debugger is active, the
|
||
|
// exception is a kernel breakpoint, and the current process is not
|
||
|
// being debugged, or the current process is being debugged, but the
|
||
|
// the breakpoint is not a kernel breakpoint instruction, then give
|
||
|
// the kernel debugger a chance to handle the exception.
|
||
|
|
||
|
// 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. Otherwise, execute the raise exception system service
|
||
|
// 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 != FALSE) {
|
||
|
|
||
|
|
||
|
// If the kernel debugger is active, the exception is a kernel
|
||
|
// breakpoint, and the current process is not being debugged,
|
||
|
// or the current process is being debugged, but the breakpoint
|
||
|
// is not a kernel breakpoint instruction, then give the kernel
|
||
|
// debugger a chance to handle the exception.
|
||
|
|
||
|
|
||
|
if ((KiDebugRoutine != NULL) &&
|
||
|
(ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
||
|
(KdIsThisAKdTrap(ExceptionRecord,
|
||
|
&ContextFrame,
|
||
|
UserMode) != FALSE) &&
|
||
|
((PsGetCurrentProcess()->DebugPort == NULL) ||
|
||
|
((PsGetCurrentProcess()->DebugPort != NULL) &&
|
||
|
(ExceptionRecord->ExceptionInformation[0] !=
|
||
|
KERNEL_BREAKPOINT_INSTRUCTION)))) {
|
||
|
|
||
|
if (((KiDebugRoutine) (TrapFrame,
|
||
|
ExceptionFrame,
|
||
|
ExceptionRecord,
|
||
|
&ContextFrame,
|
||
|
UserMode,
|
||
|
FALSE)) != FALSE) {
|
||
|
|
||
|
goto Handled1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// This is the first chance to handle the exception.
|
||
|
|
||
|
|
||
|
if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
|
||
|
TrapFrame->Fsr = SANITIZE_FSR(TrapFrame->Fsr, UserMode);
|
||
|
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 {
|
||
|
|
||
|
|
||
|
// Coerce the 64-bit integer register context to 32-bits
|
||
|
// and store in the 32-bit context area of the context
|
||
|
// record.
|
||
|
|
||
|
// N.B. This only works becasue the 32- and 64-bit integer
|
||
|
// register context does not overlap in the context
|
||
|
// record.
|
||
|
|
||
|
|
||
|
Destination = &ContextFrame.IntZero;
|
||
|
Source = &ContextFrame.XIntZero;
|
||
|
for (Index = 0; Index < 32; Index += 1) {
|
||
|
*Destination++ = (ULONG)*Source++;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Compute length of exception record and new aligned stack
|
||
|
// address.
|
||
|
|
||
|
|
||
|
Length = (sizeof(EXCEPTION_RECORD) + 7) & (~7);
|
||
|
UserStack1 = (ULONG)(ContextFrame.XIntSp & (~7)) - Length;
|
||
|
|
||
|
|
||
|
// Probe user stack area for writeability and then transfer the
|
||
|
// exception record to the user stack area.
|
||
|
|
||
|
|
||
|
ProbeForWrite((PCHAR)UserStack1, Length, sizeof(QUAD));
|
||
|
RtlMoveMemory((PVOID)UserStack1, ExceptionRecord, Length);
|
||
|
|
||
|
|
||
|
// Compute length of context record and new aligned user stack
|
||
|
// pointer.
|
||
|
|
||
|
|
||
|
Length = sizeof(CONTEXT);
|
||
|
UserStack2 = UserStack1 - Length;
|
||
|
|
||
|
|
||
|
// Probe user stack area for writeability and then transfer the
|
||
|
// context record to the user stack.
|
||
|
|
||
|
|
||
|
ProbeForWrite((PCHAR)UserStack2, Length, sizeof(QUAD));
|
||
|
RtlMoveMemory((PVOID)UserStack2, &ContextFrame, sizeof(CONTEXT));
|
||
|
|
||
|
|
||
|
// Set address of exception record, context record, and the
|
||
|
// and the new stack pointer in the current trap frame.
|
||
|
|
||
|
|
||
|
TrapFrame->XIntSp = (LONG)UserStack2;
|
||
|
TrapFrame->XIntS8 = (LONG)UserStack2;
|
||
|
TrapFrame->XIntS0 = (LONG)UserStack1;
|
||
|
TrapFrame->XIntS1 = (LONG)UserStack2;
|
||
|
|
||
|
|
||
|
// Sanitize the floating status register so a recursive
|
||
|
// exception will not occur.
|
||
|
|
||
|
|
||
|
TrapFrame->Fsr = SANITIZE_FSR(ContextFrame.Fsr, UserMode);
|
||
|
|
||
|
|
||
|
// 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->Fir = KeUserExceptionDispatcher;
|
||
|
return;
|
||
|
|
||
|
|
||
|
// If an exception occurs, then copy the new exception information
|
||
|
// to an exception record and handle the exception.
|
||
|
|
||
|
|
||
|
} 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;
|
||
|
RtlMoveMemory((PVOID)ExceptionRecord,
|
||
|
&ExceptionRecord1, sizeof(EXCEPTION_RECORD));
|
||
|
goto repeat;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// This is the second chance to handle the exception.
|
||
|
|
||
|
|
||
|
UserApcPending = KeGetCurrentThread()->ApcState.UserApcPending;
|
||
|
if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
|
||
|
TrapFrame->Fsr = SANITIZE_FSR(TrapFrame->Fsr, UserMode);
|
||
|
goto Handled2;
|
||
|
|
||
|
} else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
|
||
|
|
||
|
|
||
|
// If a user APC was not previously pending and one is now
|
||
|
// pending, then the thread has been terminated and the PC
|
||
|
// must be forced to a legal address so an infinite loop does
|
||
|
// not occur for the case where a jump to an unmapped address
|
||
|
// occured.
|
||
|
|
||
|
|
||
|
if ((UserApcPending == FALSE) &&
|
||
|
(KeGetCurrentThread()->ApcState.UserApcPending != FALSE)) {
|
||
|
TrapFrame->Fir = (ULONG)USPCR;
|
||
|
}
|
||
|
|
||
|
TrapFrame->Fsr = SANITIZE_FSR(TrapFrame->Fsr, UserMode);
|
||
|
goto Handled2;
|
||
|
|
||
|
} else {
|
||
|
ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
|
||
|
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, 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.
|
||
|
|
||
|
|
||
|
RtlMoveMemory((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
|
||
|
usermode context. This is accomplished 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.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
PKTRAP_FRAME TrapFrame;
|
||
|
|
||
|
ASSERT(KeGetPreviousMode() == UserMode);
|
||
|
|
||
|
TrapFrame = KeGetCurrentThread()->TrapFrame;
|
||
|
TrapFrame->Fir = KeRaiseUserExceptionDispatcher;
|
||
|
return ExceptionCode;
|
||
|
}
|