264 lines
6.8 KiB
C
264 lines
6.8 KiB
C
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
iafptrap.c
|
|
|
|
Abstract:
|
|
|
|
This is based on the i386 trapc.c module with very minor changes.
|
|
It would be nice if there wasn't so much duplicate code so that
|
|
fixes to that file would carry over to this one...
|
|
|
|
This module contains some trap handling code written in C.
|
|
Only by the kernel.
|
|
|
|
Author:
|
|
|
|
Ken Reneris 6-9-93
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ki.h"
|
|
#include "ia32def.h"
|
|
|
|
NTSTATUS
|
|
Ki386CheckDivideByZeroTrap (
|
|
IN PKTRAP_FRAME UserFrame
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, Ki386CheckDivideByZeroTrap)
|
|
#endif
|
|
|
|
|
|
#define REG(field) ((ULONG_PTR)(&((KTRAP_FRAME *)0)->field))
|
|
#define GETREG(frame,reg) ((PULONG) (((ULONG_PTR) frame)+reg))[0]
|
|
|
|
typedef struct {
|
|
UCHAR RmDisplaceOnly; // RM of displacment only, no base reg
|
|
UCHAR RmSib; // RM of SIB
|
|
UCHAR RmDisplace; // bit mask of RMs which have a displacement
|
|
UCHAR Disp; // sizeof displacement (in bytes)
|
|
} KMOD, *PKMOD;
|
|
|
|
static ULONG_PTR RM32[] = {
|
|
/* 000 */ REG(IntV0), // EAX
|
|
/* 001 */ REG(IntT2), // ECX
|
|
/* 010 */ REG(IntT3), // EDX
|
|
/* 011 */ REG(IntT4), // EBX
|
|
/* 100 */ REG(IntSp), // ESP
|
|
/* 101 */ REG(IntTeb), // EBP
|
|
/* 110 */ REG(IntT5), // ESI
|
|
/* 111 */ REG(IntT6) // EDI
|
|
};
|
|
|
|
static KMOD MOD32[] = {
|
|
/* 00 */ 5, 4, 0x20, 4,
|
|
/* 01 */ 0xff, 4, 0xff, 1,
|
|
/* 10 */ 0xff, 4, 0xff, 4,
|
|
/* 11 */ 0xff, 0xff, 0x00, 0
|
|
} ;
|
|
|
|
static struct {
|
|
UCHAR Opcode1, Opcode2; // instruction opcode
|
|
UCHAR ModRm, type; // if 2nd part of opcode is encoded in ModRm
|
|
} NoWaitNpxInstructions[] = {
|
|
/* FNINIT */ 0xDB, 0xE3, 0, 1,
|
|
/* FNCLEX */ 0xDB, 0xE2, 0, 1,
|
|
/* FNSTENV */ 0xD9, 0x06, 1, 1,
|
|
/* FNSAVE */ 0xDD, 0x06, 1, 1,
|
|
/* FNSTCW */ 0xD9, 0x07, 1, 2,
|
|
/* FNSTSW */ 0xDD, 0x07, 1, 3,
|
|
/* FNSTSW AX*/ 0xDF, 0xE0, 0, 4,
|
|
0x00, 0x00, 0, 1
|
|
};
|
|
|
|
|
|
NTSTATUS
|
|
Ki386CheckDivideByZeroTrap (
|
|
IN PKTRAP_FRAME UserFrame
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gains control when the x86 processor generates a
|
|
divide by zero trap. The x86 design generates such a trap on
|
|
divide by zero and on division overflows. In order to determine
|
|
which expection code to dispatch, the divisor of the "div" or "idiv"
|
|
instruction needs to be inspected.
|
|
|
|
Arguments:
|
|
|
|
UserFrame - Trap frame of the divide by zero trap
|
|
|
|
Return Value:
|
|
|
|
exception code dispatch
|
|
|
|
--*/
|
|
{
|
|
ULONG operandsize, operandmask, i;
|
|
ULONG_PTR accum;
|
|
PUCHAR istream;
|
|
UCHAR ibyte, rm;
|
|
PKMOD Mod;
|
|
BOOLEAN fPrefix;
|
|
NTSTATUS status;
|
|
BOOLEAN fHighRm8;
|
|
|
|
status = STATUS_INTEGER_DIVIDE_BY_ZERO;
|
|
fHighRm8 = FALSE;
|
|
|
|
try {
|
|
|
|
//
|
|
// read instruction prefixes
|
|
//
|
|
|
|
fPrefix = TRUE;
|
|
operandsize = 4;
|
|
operandmask = 0xffffffff;
|
|
istream = (PUCHAR) (ULONG_PTR) EIP(UserFrame);
|
|
while (fPrefix) {
|
|
ibyte = ProbeAndReadUchar(istream);
|
|
istream++;
|
|
switch (ibyte) {
|
|
case 0x2e: // cs override
|
|
case 0x36: // ss override
|
|
case 0x3e: // ds override
|
|
case 0x26: // es override
|
|
case 0x64: // fs override
|
|
case 0x65: // gs override
|
|
case 0xF3: // rep
|
|
case 0xF2: // rep
|
|
case 0xF0: // lock
|
|
break;
|
|
|
|
case 0x66:
|
|
// 16 bit operand override
|
|
operandsize = 2;
|
|
operandmask = 0xffff;
|
|
break;
|
|
|
|
case 0x67:
|
|
// 16 bit address size override
|
|
// this is some non-flat code
|
|
goto try_exit;
|
|
|
|
default:
|
|
fPrefix = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check instruction opcode
|
|
//
|
|
|
|
if (ibyte != 0xf7 && ibyte != 0xf6) {
|
|
// this is not a DIV or IDIV opcode
|
|
goto try_exit;
|
|
}
|
|
|
|
if (ibyte == 0xf6) {
|
|
// this is a byte div or idiv
|
|
operandsize = 1;
|
|
operandmask = 0xff;
|
|
}
|
|
|
|
//
|
|
// Get Mod R/M
|
|
//
|
|
|
|
ibyte = ProbeAndReadUchar (istream);
|
|
istream++;
|
|
Mod = MOD32 + (ibyte >> 6);
|
|
rm = ibyte & 7;
|
|
|
|
//
|
|
// put register values into accum
|
|
//
|
|
|
|
if (operandsize == 1 && (ibyte & 0xc0) == 0xc0) {
|
|
if ((rm & 4) != 0) {
|
|
fHighRm8 = TRUE;
|
|
}
|
|
}
|
|
|
|
accum = 0;
|
|
if (rm != Mod->RmDisplaceOnly) {
|
|
if (rm == Mod->RmSib) {
|
|
// get SIB
|
|
ibyte = ProbeAndReadUchar(istream);
|
|
istream++;
|
|
i = (ibyte >> 3) & 7;
|
|
if (i != 4) {
|
|
accum = GETREG(UserFrame, RM32[i]);
|
|
accum = accum << (ibyte >> 6); // apply scaler
|
|
}
|
|
i = ibyte & 7;
|
|
accum = accum + GETREG(UserFrame, RM32[i]);
|
|
} else {
|
|
//
|
|
// get register's value
|
|
//
|
|
if (fHighRm8 == TRUE) {
|
|
accum = GETREG(UserFrame, RM32[rm & 3]);
|
|
accum = accum >> 8;
|
|
} else {
|
|
accum = GETREG(UserFrame, RM32[rm]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// apply displacement to accum
|
|
//
|
|
|
|
if (Mod->RmDisplace & (1 << rm)) {
|
|
if (Mod->Disp == 4) {
|
|
i = ProbeAndReadUlong ((PULONG) istream);
|
|
} else {
|
|
ibyte = ProbeAndReadChar (istream);
|
|
i = (signed long) ((signed char) ibyte); // sign extend
|
|
}
|
|
accum += i;
|
|
}
|
|
|
|
//
|
|
// if this is an effective address, go get the data value
|
|
//
|
|
|
|
if (Mod->Disp) {
|
|
switch (operandsize) {
|
|
case 1: accum = ProbeAndReadUchar((PUCHAR) accum); break;
|
|
case 2: accum = ProbeAndReadUshort((PUSHORT) accum); break;
|
|
case 4: accum = ProbeAndReadUlong((PULONG) accum); break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// accum now contains the instruction operand, see if the
|
|
// operand was really a zero
|
|
//
|
|
|
|
if (accum & operandmask) {
|
|
// operand was non-zero, must be an overflow
|
|
status = STATUS_INTEGER_OVERFLOW;
|
|
}
|
|
|
|
try_exit: ;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// do nothing...
|
|
}
|
|
|
|
return status;
|
|
}
|