592 lines
19 KiB
C
592 lines
19 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Digital Equipment Corporation
|
|
|
|
Module Name:
|
|
|
|
trigger.c
|
|
|
|
Abstract:
|
|
|
|
This module implements functions that handle synchronous and asynchronous
|
|
arithmetic exceptions. The Alpha SRM specifies certain code generation
|
|
rules which if followed allow this code (in conjunction with internal
|
|
processor register state) to effect a precise, synchronous exception
|
|
given an imprecise, asynchronous exception. This capability is required
|
|
for software emulation of the IEEE single and double floating operations.
|
|
|
|
Author:
|
|
|
|
Thomas Van Baak (tvb) 5-Mar-1993
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ki.h"
|
|
#pragma hdrstop
|
|
#include "alphaops.h"
|
|
|
|
|
|
// Define forward referenced function prototypes.
|
|
|
|
|
|
BOOLEAN
|
|
KiLocateTriggerPc (
|
|
IN OUT PEXCEPTION_RECORD ExceptionRecord,
|
|
IN OUT PKTRAP_FRAME TrapFrame
|
|
);
|
|
|
|
|
|
// Define debugging macros.
|
|
|
|
|
|
#if DBG
|
|
|
|
extern ULONG RtlDebugFlags;
|
|
#define DBGPRINT ((RtlDebugFlags & 0x4) != 0) && DbgPrint
|
|
|
|
#else
|
|
|
|
#define DBGPRINT 0 && DbgPrint
|
|
|
|
#endif
|
|
|
|
|
|
// Define non-IEEE (a/k/a `high performance') arithmetic exception types.
|
|
// The PALcode exception record is extended by one word and the 4th word
|
|
// contains the reason the arithmetic exception is not an IEEE exception.
|
|
|
|
|
|
#define NON_IEEE(ExceptionRecord, Reason) \
|
|
(ExceptionRecord)->NumberParameters = 4; \
|
|
(ExceptionRecord)->ExceptionInformation[3] = (Reason);
|
|
|
|
#define TRIGGER_FLOATING_REGISTER_MASK_CLEAR 1
|
|
#define TRIGGER_INTEGER_REGISTER_MASK_SET 2
|
|
#define TRIGGER_NO_SOFTWARE_COMPLETION 3
|
|
#define TRIGGER_INVALID_INSTRUCTION_FOUND 4
|
|
#define TRIGGER_INSTRUCTION_FETCH_ERROR 5
|
|
#define TRIGGER_INSTRUCTION_NOT_FOUND 6
|
|
#define TRIGGER_SOURCE_IS_DESTINATION 7
|
|
#define TRIGGER_WRONG_INSTRUCTION 8
|
|
|
|
BOOLEAN
|
|
KiFloatingException (
|
|
IN OUT PEXCEPTION_RECORD ExceptionRecord,
|
|
IN OUT PKEXCEPTION_FRAME ExceptionFrame,
|
|
IN OUT PKTRAP_FRAME TrapFrame,
|
|
IN BOOLEAN ImpreciseTrap,
|
|
IN OUT PULONG SoftFpcrCopy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to emulate a floating operation and convert the
|
|
exception status to the proper value. If the exception is a fault, the
|
|
faulting floating point instruction is emulated. If the exception is an
|
|
imprecise trap, an attempt is made to locate and to emulate the original
|
|
trapping floating point instruction.
|
|
|
|
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.
|
|
|
|
ImpreciseTrap - Supplies a boolean value that specifies whether the
|
|
exception is an imprecise trap.
|
|
|
|
SoftFpcrCopy - Supplies a pointer to a longword variable that receives
|
|
a copy of the software FPCR.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the floating exception is successfully
|
|
emulated. Otherwise, a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
BOOLEAN Status;
|
|
PSW_FPCR SoftwareFpcr;
|
|
PTEB Teb;
|
|
|
|
try {
|
|
|
|
|
|
// Obtain a copy of the software FPCR longword from the TEB.
|
|
|
|
|
|
Teb = NtCurrentTeb();
|
|
*SoftFpcrCopy = Teb->FpSoftwareStatusRegister;
|
|
SoftwareFpcr = (PSW_FPCR)SoftFpcrCopy;
|
|
DBGPRINT("KiFloatingException: SoftFpcr = %.8lx\n", *SoftFpcrCopy);
|
|
|
|
#if DBG
|
|
|
|
// If the floating emulation inhibit flag is set, then bypass all
|
|
// software emulation by the kernel and return FALSE to raise the
|
|
// original PALcode exception.
|
|
|
|
// N.B. This is for user-mode development and testing and is not
|
|
// part of the API.
|
|
|
|
|
|
if (SoftwareFpcr->NoSoftwareEmulation != 0) {
|
|
DBGPRINT("KiFloatingException: NoSoftwareEmulation\n");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
|
|
// If the arithmetic exception is an imprecise trap, the address of
|
|
// the trapping instruction is somewhere before the exception address.
|
|
|
|
// Otherwise the exception is a fault and the address of the faulting
|
|
// instruction is the exception address.
|
|
|
|
|
|
if (ImpreciseTrap != FALSE) {
|
|
|
|
|
|
// If the arithmetic trap ignore mode is enabled, then do not
|
|
// spend time to locate or to emulate the trapping instruction,
|
|
// leave unpredictable results in the destination register, do
|
|
// not set correct IEEE sticky bits in the software FPCR, leave
|
|
// the hardware FPCR sticky status bits as they are, and return
|
|
// TRUE to continue execution. It is assumed that user code will
|
|
// check the hardware FPCR exception status bits to determine if
|
|
// the instruction succeeded or not (Insignia SoftPc feature).
|
|
|
|
|
|
if (SoftwareFpcr->ArithmeticTrapIgnore != 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Attempt to locate the trapping instruction. If the instruction
|
|
// stream is such that this is not possible or was not intended,
|
|
// then set an exception code that best reflects the exception
|
|
// summary register bits and return FALSE to raise the exception.
|
|
|
|
// Otherwise emulate the trigger instruction in order to compute
|
|
// the correct destination result value, the correct IEEE status
|
|
// bits, and raise any enabled IEEE exceptions.
|
|
|
|
|
|
if (KiLocateTriggerPc(ExceptionRecord, TrapFrame) == FALSE) {
|
|
KiSetFloatingStatus(ExceptionRecord);
|
|
return FALSE;
|
|
}
|
|
Status = KiEmulateFloating(ExceptionRecord,
|
|
ExceptionFrame,
|
|
TrapFrame,
|
|
SoftwareFpcr);
|
|
|
|
} else {
|
|
|
|
|
|
// Attempt to emulate the faulting instruction in order to perform
|
|
// floating operations not supported by EV4, to compute the correct
|
|
// destination result value, the correct IEEE status bits, and
|
|
// raise any enabled IEEE exceptions.
|
|
|
|
|
|
Status = KiEmulateFloating(ExceptionRecord,
|
|
ExceptionFrame,
|
|
TrapFrame,
|
|
SoftwareFpcr);
|
|
|
|
|
|
// If the emulation resulted in a floating point exception and
|
|
// the arithmetic trap ignore mode is enabled, then set the return
|
|
// value to TRUE to suppress the exception and continue execution.
|
|
|
|
|
|
if ((Status == FALSE) &&
|
|
(SoftwareFpcr->ArithmeticTrapIgnore != 0) &&
|
|
(ExceptionRecord->ExceptionCode != STATUS_ILLEGAL_INSTRUCTION)) {
|
|
Status = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// Store the updated software FPCR longword in the TEB.
|
|
|
|
|
|
Teb->FpSoftwareStatusRegister = *SoftFpcrCopy;
|
|
DBGPRINT("KiFloatingException: SoftFpcr = %.8lx\n", *SoftFpcrCopy);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
|
|
// An exception occurred accessing the TEB.
|
|
|
|
|
|
ExceptionRecord->ExceptionCode = GetExceptionCode();
|
|
return FALSE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
KiLocateTriggerPc (
|
|
IN OUT PEXCEPTION_RECORD ExceptionRecord,
|
|
IN OUT PKTRAP_FRAME TrapFrame
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to try to determine the precise location of the
|
|
instruction that caused an arithmetic exception. The instruction that
|
|
caused the trap to occur is known as the trigger instruction. On entry,
|
|
the actual address of the trigger instruction is unknown and the exception
|
|
address is the continuation address. The continuation address is the
|
|
address of the instruction that would have executed had the trap not
|
|
occurred. The instructions following the trigger instruction up to the
|
|
continuation address are known as the trap shadow of the trigger
|
|
instruction.
|
|
|
|
Alpha AXP produces imprecise, asynchronous arithmetic exceptions. The
|
|
exceptions are imprecise because the exception address when a trap is
|
|
taken may be more than one instruction beyond the address of the
|
|
instruction that actually caused the trap to occur.
|
|
|
|
The arithmetic exceptions are traps (rather than faults) because the
|
|
exception address is not the address of the trapping instruction
|
|
itself, but the address of the next instruction to execute, which is
|
|
always (at least) one instruction beyond the address of the trapping
|
|
instruction.
|
|
|
|
It is possible for multiple exceptions to occur and result in a single
|
|
trap. This function only determines the address of the first trapping
|
|
instruction.
|
|
|
|
Unpredictable values may have been stored in the destination register
|
|
of trapping instructions. Thus to insure that the trigger instruction
|
|
can be located, and that the trigger instruction and any instructions
|
|
in the trap shadow can be re-executed, certain restrictions are placed
|
|
on the type of instructions or the mix of operands in the trap shadow.
|
|
|
|
The code generation rules serve only to guarantee that the instruction
|
|
backup algorithm and subsequent re-execution can always be successful.
|
|
Hence the restrictions on such constructs as branches, jumps, and the
|
|
re-use of source or destination operands within the trap shadow.
|
|
|
|
Arguments:
|
|
|
|
ExceptionRecord - Supplies a pointer to an exception record.
|
|
|
|
TrapFrame - Supplies a pointer to a trap frame.
|
|
|
|
Return Value:
|
|
|
|
If the trigger PC was precisely determined, the exception address in
|
|
the exception record is set to the trigger PC, the continuation address
|
|
in the trap frame is updated, and a value of TRUE is returned. Otherwise
|
|
no values are stored and a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PEXC_SUM ExceptionSummary;
|
|
ULONG Fa;
|
|
ULONG Fb;
|
|
ULONG Fc;
|
|
ULONG FloatRegisterTrashMask;
|
|
ULONG FloatRegisterWriteMask;
|
|
ALPHA_INSTRUCTION Instruction;
|
|
ULONG IntegerRegisterWriteMask;
|
|
ULONG Opcode;
|
|
ULONG_PTR TrapShadowLowLimit;
|
|
ULONG_PTR TriggerPc;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
|
|
|
|
// Obtain a copy of the float and integer register write mask registers
|
|
// and the exception summary register from the exception record built by
|
|
// PALcode.
|
|
|
|
|
|
FloatRegisterWriteMask = (ULONG)ExceptionRecord->ExceptionInformation[0];
|
|
IntegerRegisterWriteMask = (ULONG)ExceptionRecord->ExceptionInformation[1];
|
|
ExceptionSummary = (PEXC_SUM)&(ExceptionRecord->ExceptionInformation[2]);
|
|
DBGPRINT("KiLocateTriggerPc: WriteMask %.8lx.%.8lx, ExceptionSummary %.8lx\n",
|
|
FloatRegisterWriteMask, IntegerRegisterWriteMask,
|
|
*(PULONG)ExceptionSummary);
|
|
|
|
|
|
// Capture previous mode from trap frame not current thread.
|
|
|
|
|
|
PreviousMode = (KPROCESSOR_MODE)(((PSR *)(&TrapFrame->Psr))->MODE);
|
|
|
|
if (FloatRegisterWriteMask == 0) {
|
|
|
|
|
|
// It should not be possible to have a floating point exception without
|
|
// at least one of the destination float register bits set. The trap
|
|
// shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: FloatRegisterWriteMask clear\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_FLOATING_REGISTER_MASK_CLEAR);
|
|
return FALSE;
|
|
}
|
|
if (IntegerRegisterWriteMask != 0) {
|
|
|
|
|
|
// It is not possible to precisely locate the trigger instruction
|
|
// when the integer overflow bit is set. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: IntegerRegisterMask set.\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INTEGER_REGISTER_MASK_SET);
|
|
return FALSE;
|
|
}
|
|
if (ExceptionSummary->SoftwareCompletion == 0) {
|
|
|
|
|
|
// The exception summary software completion bit is the AND of the
|
|
// /S bits of all trapping instructions in the trap shadow. Since
|
|
// the software completion bit is not set, it can be assumed the
|
|
// code that was executing does not want precise exceptions, or if
|
|
// it does, the code does not comply with the Alpha AXP guidelines
|
|
// for locating the trigger PC. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: SoftwareCompletion clear\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_NO_SOFTWARE_COMPLETION);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Search for the trigger instruction starting with the instruction before
|
|
// the continuation PC (the instruction pointed to by Fir either did not
|
|
// complete or did not even start). Limit the search to the arbitrary
|
|
// limit of N instructions back from the current PC to prevent unbounded
|
|
// searches. The search is complete when all trapping destination register
|
|
// bits in the float write mask register have been accounted for.
|
|
|
|
|
|
FloatRegisterTrashMask = 0;
|
|
TriggerPc = (ULONG_PTR)TrapFrame->Fir;
|
|
TrapShadowLowLimit = TriggerPc - (500 * sizeof(ULONG));
|
|
|
|
try {
|
|
do {
|
|
TriggerPc -= 4;
|
|
if (TriggerPc < TrapShadowLowLimit) {
|
|
|
|
|
|
// The trigger PC is too far away from the exception PC to
|
|
// be reasonable. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Trap shadow too long\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INSTRUCTION_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
if (PreviousMode != KernelMode) {
|
|
Instruction.Long = ProbeAndReadUlong((PULONG)TriggerPc);
|
|
} else {
|
|
Instruction.Long = *((PULONG)TriggerPc);
|
|
}
|
|
|
|
|
|
// Examine the opcode of this instruction to determine if the
|
|
// trap shadow is invalid.
|
|
|
|
|
|
Opcode = Instruction.Memory.Opcode;
|
|
if (Opcode == JMP_OP) {
|
|
|
|
|
|
// This is one of the jump instructions: jump, return, or
|
|
// either form of jsr. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Jump within Trap Shadow\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
|
|
return FALSE;
|
|
|
|
} else if ((Opcode >= BR_OP) && (Opcode <= BGT_OP)) {
|
|
|
|
|
|
// The instruction is one of 16 branch opcodes that consists
|
|
// of BR, the 6 floating point branch, BSR, and the 8 integer
|
|
// branch instructions. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Branch within Trap Shadow\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
|
|
return FALSE;
|
|
|
|
} else if ((Instruction.Memory.Opcode == MEMSPC_OP) &&
|
|
((Instruction.Memory.MemDisp == TRAPB_FUNC) ||
|
|
(Instruction.Memory.MemDisp == EXCB_FUNC))) {
|
|
|
|
|
|
// The instruction is a type of TRAPB instruction. The trap
|
|
// shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Trapb within Trap Shadow\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
|
|
return FALSE;
|
|
|
|
} else if (Opcode == CALLPAL_OP) {
|
|
|
|
|
|
// The instruction is a Call PAL. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Call PAL within Trap Shadow\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
|
|
return FALSE;
|
|
|
|
} else if ((Opcode == IEEEFP_OP) || (Opcode == FPOP_OP)) {
|
|
|
|
|
|
// The instruction is an IEEE floating point instruction.
|
|
// Decode the destination register of the floating point
|
|
// instruction in order to check against the register mask.
|
|
|
|
|
|
Fc = Instruction.FpOp.Fc;
|
|
if (Fc != FZERO_REG) {
|
|
FloatRegisterTrashMask |= (1 << Fc);
|
|
}
|
|
FloatRegisterWriteMask &= ~(1 << Fc);
|
|
}
|
|
|
|
} while (FloatRegisterWriteMask != 0);
|
|
|
|
|
|
// If the instruction thought to be the trigger instruction does not
|
|
// have the /S bit set, then the trap shadow is invalid (some other
|
|
// instruction must have caused software completion bit to be set).
|
|
|
|
|
|
if ((Instruction.FpOp.Function & FP_TRAP_ENABLE_S) == 0) {
|
|
DBGPRINT("KiLocateTriggerPc: Trigger instruction missing /S\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_WRONG_INSTRUCTION);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// If either of the operand registers of the trigger instruction is
|
|
// also the destination register of the trigger instruction or any
|
|
// instruction in the trap shadow, then the trap shadow in invalid.
|
|
// This is because the original value of the operand register(s) may
|
|
// have been destroyed making it impossible to re-execute the trigger
|
|
// instruction.
|
|
|
|
|
|
Fa = Instruction.FpOp.Fa;
|
|
Fb = Instruction.FpOp.Fb;
|
|
if ((FloatRegisterTrashMask & ((1 << Fa) | (1 << Fb))) != 0) {
|
|
DBGPRINT("KiLocateTriggerPc: Source is destination\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_SOURCE_IS_DESTINATION);
|
|
return FALSE;
|
|
}
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
|
|
// An exception occurred while fetching the value of the
|
|
// next previous instruction. The trap shadow is invalid.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Instruction fetch error\n");
|
|
NON_IEEE(ExceptionRecord, TRIGGER_INSTRUCTION_FETCH_ERROR);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// The trigger instruction was successfully located. Set the precise
|
|
// exception address in the exception record, set the new continuation
|
|
// address in the trap frame, and return a value of TRUE.
|
|
|
|
|
|
DBGPRINT("KiLocateTriggerPc: Exception PC = %p, Trigger PC = %p\n",
|
|
ExceptionRecord->ExceptionAddress, TriggerPc);
|
|
ExceptionRecord->ExceptionAddress = (PVOID)TriggerPc;
|
|
TrapFrame->Fir = (ULONGLONG)(LONG_PTR)(TriggerPc + 4);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
KiSetFloatingStatus (
|
|
IN OUT PEXCEPTION_RECORD ExceptionRecord
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to convert the exception summary register bits
|
|
into a status code value.
|
|
|
|
Arguments:
|
|
|
|
ExceptionRecord - Supplies a pointer to an exception record.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PEXC_SUM ExceptionSummary;
|
|
|
|
|
|
// Perform the following triage on the exception summary register to
|
|
// report the type of exception, even if though the PC reported is
|
|
// imprecise.
|
|
|
|
|
|
DBGPRINT("KiSetFloatingStatus: ExceptionSummary = %.8lx\n",
|
|
ExceptionRecord->ExceptionInformation[2]);
|
|
|
|
ExceptionSummary = (PEXC_SUM)(&ExceptionRecord->ExceptionInformation[2]);
|
|
if (ExceptionSummary->InvalidOperation != 0) {
|
|
ExceptionRecord->ExceptionCode = STATUS_FLOAT_INVALID_OPERATION;
|
|
|
|
} else if (ExceptionSummary->DivisionByZero != 0) {
|
|
ExceptionRecord->ExceptionCode = STATUS_FLOAT_DIVIDE_BY_ZERO;
|
|
|
|
} else if (ExceptionSummary->Overflow != 0) {
|
|
ExceptionRecord->ExceptionCode = STATUS_FLOAT_OVERFLOW;
|
|
|
|
} else if (ExceptionSummary->Underflow != 0) {
|
|
ExceptionRecord->ExceptionCode = STATUS_FLOAT_UNDERFLOW;
|
|
|
|
} else if (ExceptionSummary->InexactResult != 0) {
|
|
ExceptionRecord->ExceptionCode = STATUS_FLOAT_INEXACT_RESULT;
|
|
|
|
} else if (ExceptionSummary->IntegerOverflow != 0) {
|
|
ExceptionRecord->ExceptionCode = STATUS_INTEGER_OVERFLOW;
|
|
|
|
} else {
|
|
ExceptionRecord->ExceptionCode = STATUS_FLOAT_STACK_CHECK;
|
|
}
|
|
}
|