Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3158 lines
66 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ia32emul.c
Abstract:
This module implements an x86 instruction decoder and emulator.
Author:
Samer Arafeh (samera) 30-Oct-2000
Environment:
Kernel mode only.
Revision History:
--*/
#include "ki.h"
#include "ia32def.h"
#include "wow64t.h"
#if DBG
BOOLEAN KiIa32InstructionEmulationDbg = 0;
#endif
#define KiIa32GetX86Eflags(efl) efl.Value = __getReg(CV_IA64_AR24)
#define KiIa32SetX86Eflags(efl) __setReg(CV_IA64_AR24, efl.Value)
#define IA32_GETEFLAGS_CF(efl) (efl & 0x01UI64)
//
// Ia32 instruction handlers
//
NTSTATUS
KiIa32InstructionAdc (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionAdd (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionArithmeticBitwiseHelper (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionBitTestHelper (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionOneParamHelper (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionXadd (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionXchg (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionCmpXchg (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionCmpXchg8b (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
NTSTATUS
KiIa32InstructionMoveSeg (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
);
//
// Opcode Ids
//
typedef enum _IA32_OPCODE
{
Ia32_Adc,
Ia32_Add,
Ia32_And,
Ia32_Bt,
Ia32_Btc,
Ia32_Btr,
Ia32_Bts,
Ia32_Cmpxchg,
Ia32_Cmpxchg8b,
Ia32_Dec,
Ia32_Inc,
Ia32_Neg,
Ia32_Not,
Ia32_Or,
Ia32_Sbb,
Ia32_Sub,
Ia32_Xadd,
Ia32_Xchg,
Ia32_Xor,
Ia32_MovToSeg,
//
// This needs always to be the last element
//
Ia32_LastOpcode
} IA32_OPCODE;
//
// Array of Ia32 instruction handlers
// NOTE : The following table must be in sync with the above enum.
//
typedef NTSTATUS (*IA32_INSTRUCTION_HANDLER) (PKTRAP_FRAME, PIA32_INSTRUCTION);
IA32_INSTRUCTION_HANDLER KiIa32InstructionHandler [] =
{
KiIa32InstructionAdc,
KiIa32InstructionAdd,
KiIa32InstructionArithmeticBitwiseHelper,
KiIa32InstructionBitTestHelper,
KiIa32InstructionBitTestHelper,
KiIa32InstructionBitTestHelper,
KiIa32InstructionBitTestHelper,
KiIa32InstructionCmpXchg,
KiIa32InstructionCmpXchg8b,
KiIa32InstructionOneParamHelper,
KiIa32InstructionOneParamHelper,
KiIa32InstructionOneParamHelper,
KiIa32InstructionOneParamHelper,
KiIa32InstructionArithmeticBitwiseHelper,
KiIa32InstructionAdc,
KiIa32InstructionAdd,
KiIa32InstructionXadd,
KiIa32InstructionXchg,
KiIa32InstructionArithmeticBitwiseHelper,
KiIa32InstructionMoveSeg,
NULL
};
#if DBG
PCHAR KiIa32InstructionHandlerNames [] =
{
"KiIa32InstructionAdc",
"KiIa32InstructionAdd",
"KiIa32InstructionAnd",
"KiIa32InstructionBt",
"KiIa32InstructionBtc",
"KiIa32InstructionBtr",
"KiIa32InstructionBts",
"KiIa32InstructionCmpXchg",
"KiIa32InstructionCmpXchg8b",
"KiIa32InstructionDec",
"KiIa32InstructionInc",
"KiIa32InstructionNeg",
"KiIa32InstructionNot",
"KiIa32InstructionOr",
"KiIa32InstructionSbb",
"KiIa32InstructionSub",
"KiIa32InstructionXadd",
"KiIa32InstructionXchg",
"KiIa32InstructionXor",
"KiIa32InstructionMoveSeg",
NULL,
};
#endif
IA32_OPCODE_DESCRIPTION OpcodesDescription[] =
{
//
// Adc
//
// Adc r/m8, imm8
{
0x80, 0x00, 0x02, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Adc
},
// Adc r/m, imm
{
0x81, 0x00, 0x02, 0x11, IA32_PARAM_RM_IMM, Ia32_Adc
},
// Adc r/m, imm8 (sign)
{
0x83, 0x00, 0x02, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Adc
},
// Adc r/m8, r8
{
0x10, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Adc
},
// Adc r/m, r
{
0x11, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Adc
},
// Adc r, r/m8
{
0x12, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Adc
},
// Adc r, r/m
{
0x13, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Adc
},
//
// Add
//
// Add r/m8, imm8
{
0x80, 0x00, 0x00, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Add
},
// Add r/m, imm
{
0x81, 0x00, 0x00, 0x11, IA32_PARAM_RM_IMM, Ia32_Add
},
// Add r/m, imm8 (sign)
{
0x83, 0x00, 0x00, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Add
},
// Add r/m8, r8
{
0x00, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Add
},
// Add r/m, r
{
0x01, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Add
},
// Add r, r/m8
{
0x02, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Add
},
// Add r, r/m
{
0x03, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Add
},
//
// And
//
// And r/m8, imm8
{
0x80, 0x00, 0x04, 0x11, IA32_PARAM_RM8_IMM8, Ia32_And
},
// And r/m, imm
{
0x81, 0x00, 0x04, 0x11, IA32_PARAM_RM_IMM, Ia32_And
},
// And r/m, imm8
{
0x83, 0x00, 0x04, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_And
},
// And r/m8, r8
{
0x20, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_And
},
// And rm, r
{
0x21, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_And
},
// And r8, r/m8
{
0x22, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_And
},
// And r, r/m
{
0x23, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_And
},
//
// Or
//
// Or r/m8, imm8
{
0x80, 0x00, 0x01, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Or
},
// Or r/m, imm
{
0x81, 0x00, 0x01, 0x11, IA32_PARAM_RM_IMM, Ia32_Or
},
// Or r/m, imm8
{
0x83, 0x00, 0x01, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Or
},
// Or r/m8, r8
{
0x08, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Or
},
// Or rm, r
{
0x09, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Or
},
// Or r8, r/m8
{
0x0a, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Or
},
// Or r, r/m
{
0x0b, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Or
},
//
// Xor
//
// Xor r/m8, imm8
{
0x80, 0x00, 0x06, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Xor
},
// Xor r/m, imm
{
0x81, 0x00, 0x06, 0x11, IA32_PARAM_RM_IMM, Ia32_Xor
},
// Xor r/m, imm8
{
0x83, 0x00, 0x06, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Xor
},
// Xor r/m8, r8
{
0x30, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Xor
},
// Xor rm, r
{
0x31, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Xor
},
// Xor r8, r/m8
{
0x32, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Xor
},
// Xor r, r/m
{
0x33, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Xor
},
//
// Inc
//
// Inc r/m8
{
0xfe, 0x00, 0x00, 0x11, IA32_PARAM_RM8, Ia32_Inc
},
// Inc r/m
{
0xff, 0x00, 0x00, 0x11, IA32_PARAM_RM, Ia32_Inc
},
//
// Dec
//
// Dec r/m8
{
0xfe, 0x00, 0x01, 0x11, IA32_PARAM_RM8, Ia32_Dec
},
// Dec r/m
{
0xff, 0x00, 0x01, 0x11, IA32_PARAM_RM, Ia32_Dec
},
//
// Xchg
//
// Xchg r/m8, r
{
0x86, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Xchg
},
// Xchg r/m, r
{
0x87, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Xchg
},
//
// Cmpxchg
//
// Cmpxchg r/m8, r
{
0x0f, 0xb0, 0x00, 0x02, IA32_PARAM_RM8_R, Ia32_Cmpxchg
},
// Cmpxchg r/m, r
{
0x0f, 0xb1, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Cmpxchg
},
//
// Cmpxchg8b
//
// Cmpxchg8b m64
{
0x0f, 0xc7, 0x01, 0x12, IA32_PARAM_RM, Ia32_Cmpxchg8b
},
//
// Xadd
//
// Xadd r/m8, r
{
0x0f, 0xc0, 0x00, 0x02, IA32_PARAM_RM8_R, Ia32_Xadd
},
// Xadd r/m, r
{
0x0f, 0xc1, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Xadd
},
//
// Neg
//
// Neg r/m8
{
0xf6, 0x00, 0x03, 0x11, IA32_PARAM_RM8, Ia32_Neg
},
// Neg r/m
{
0xf7, 0x00, 0x03, 0x11, IA32_PARAM_RM, Ia32_Neg
},
//
// Not
//
// Not r/m8
{
0xf6, 0x00, 0x02, 0x11, IA32_PARAM_RM8, Ia32_Not
},
// Not r/m
{
0xf7, 0x00, 0x02, 0x11, IA32_PARAM_RM, Ia32_Not
},
//
// Bt (Bit Test)
//
// Bt r/m, r
{
0x0f, 0xa3, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Bt
},
// Bt r/m, imm8
{
0x0f, 0xba, 0x04, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Bt
},
//
// Btc
//
// Btc r/m, r
{
0x0f, 0xbb, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Btc
},
// Btc r/m, imm8
{
0x0f, 0xba, 0x07, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Btc
},
//
// Btr
//
// Btr r/m, r
{
0x0f, 0xb3, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Btr
},
// Btr r/m, imm8
{
0x0f, 0xba, 0x06, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Btr
},
//
// Bts
//
// Bts r/m, r
{
0x0f, 0xab, 0x00, 0x02, IA32_PARAM_RM_R, Ia32_Bts
},
// Bts r/m, imm8
{
0x0f, 0xba, 0x05, 0x12, IA32_PARAM_RM_IMM8SIGN, Ia32_Bts
},
//
// Sub
//
// Sub r/m8, imm8
{
0x80, 0x00, 0x05, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Sub
},
// Sub r/m, imm
{
0x81, 0x00, 0x05, 0x11, IA32_PARAM_RM_IMM, Ia32_Sub
},
// Sub r/m, imm8 (sign)
{
0x83, 0x00, 0x05, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Sub
},
// Sub r/m8, r8
{
0x28, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Sub
},
// Sub r/m, r
{
0x29, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Sub
},
// Sub r, r/m8
{
0x2a, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Sub
},
// Sub r, r/m
{
0x2b, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Sub
},
//
// Sbb
//
// Sbb r/m8, imm8
{
0x80, 0x00, 0x03, 0x11, IA32_PARAM_RM8_IMM8, Ia32_Sbb
},
// Sbb r/m, imm
{
0x81, 0x00, 0x03, 0x11, IA32_PARAM_RM_IMM, Ia32_Sbb
},
// Sbb r/m, imm8 (sign)
{
0x83, 0x00, 0x03, 0x11, IA32_PARAM_RM_IMM8SIGN, Ia32_Sbb
},
// Sbb r/m8, r8
{
0x18, 0x00, 0x00, 0x01, IA32_PARAM_RM8_R, Ia32_Sbb
},
// Sbb r/m, r
{
0x19, 0x00, 0x00, 0x01, IA32_PARAM_RM_R, Ia32_Sbb
},
// Sbb r, r/m8
{
0x1a, 0x00, 0x00, 0x01, IA32_PARAM_R_RM8, Ia32_Sbb
},
// Sbb r, r/m
{
0x1b, 0x00, 0x00, 0x01, IA32_PARAM_R_RM, Ia32_Sbb
},
//
// Mov
//
// Mov seg-reg, r/m8
{
0x8e, 0x00, 0x00, 0x01, IA32_PARAM_SEGREG_RM8, Ia32_MovToSeg
},
// Mov seg-reg, r/m
{
0x8e, 0x00, 0x00, 0x01, IA32_PARAM_SEGREG_RM, Ia32_MovToSeg
},
};
//
// Fast mutex that will serialize access to the instruction
// emulator when the lock prefix is set.
//
FAST_MUTEX KiIa32MisalignedLockFastMutex;
#define KiIa32AcquireMisalignedLockFastMutex() ExAcquireFastMutex(&KiIa32MisalignedLockFastMutex)
#define KiIa32ReleaseMisalignedLockFastMutex() ExReleaseFastMutex(&KiIa32MisalignedLockFastMutex)
//
// This table contains the offset into the KTRAP_FRAME
// for the appropriate register. This table is based on the
// needs of the x86 instruction R/M bits
//
const ULONG RegOffsetTable[8] =
{
FIELD_OFFSET(KTRAP_FRAME, IntV0), // EAX
FIELD_OFFSET(KTRAP_FRAME, IntT2), // ECX
FIELD_OFFSET(KTRAP_FRAME, IntT3), // EDX
FIELD_OFFSET(KTRAP_FRAME, IntT4), // EBX
FIELD_OFFSET(KTRAP_FRAME, IntSp), // ESP
FIELD_OFFSET(KTRAP_FRAME, IntTeb), // EBP
FIELD_OFFSET(KTRAP_FRAME, IntT5), // ESI
FIELD_OFFSET(KTRAP_FRAME, IntT6) // EDI
};
ULONG_PTR GetX86RegOffset (
IN PKTRAP_FRAME TrapFrame,
IN ULONG RegisterBase
)
/*++
Routine Description:
Retreives the offset into the aliased ia64 register for the ia32 register
inside the trap frame.
Arguments:
TrapFrame - Pointer to TrapFrame on the stack.
RegisterBase - Register number to retrieve the offset for.
Return Value:
Address of ia64 alias register for the ia32 register.
--*/
{
return (ULONG_PTR)((PCHAR)TrapFrame + RegOffsetTable[RegisterBase]);
}
ULONG GetX86Reg (
IN PKTRAP_FRAME TrapFrame,
IN ULONG RegisterBase
)
/*++
Routine Description:
Retreives the ia32 register value.
Arguments:
TrapFrame - Pointer to TrapFrame on the stack.
RegisterBase - Register number to retrieve the value for.
Return Value:
Ia32 register context.
--*/
{
return (ULONG)(*(PULONG_PTR)GetX86RegOffset(TrapFrame, RegisterBase));
}
NTSTATUS
KiIa32InitializeLockFastMutex (
VOID
)
/*++
Routine Description:
Initializes the misaligned lock fast mutex. Used to serialize
access if the r/m address is misaligned.
Arguments:
None.
Return Value:
NTSTATUS.
--*/
{
ExInitializeFastMutex (&KiIa32MisalignedLockFastMutex);
return STATUS_SUCCESS;
}
LONG
KiIa32ComputeSIBAddress(
IN PKTRAP_FRAME Frame,
IN LONG Displacement,
IN UCHAR Sib,
IN UCHAR ModRm
)
/*++
Routine Description:
Compute an effective address based on the SIB bytes in an instruction
using the register values in the trap frame
Arguments:
Frame - Pointer to iA32 TrapFrame in the stack.
Displacement - The value of the displacement byte. If no displacement, this
value should be passed in as zero.
Sib - The sib byte that is causing all the trouble.
ModRm - ModRm instruction value
Return Value:
The effective address to use for the memory operation
--*/
{
LONG Base;
LONG Index;
LONG Scale;
//
// First get the base address that we will be using
//
if ((Sib & MI_SIB_BASEMASK) == 5)
{
//
// Handle the special case where we don't use EBP for the base
//
//
// EBP is an implicit reg-base if the Mod is not zero.
//
if ((ModRm >> MI_MODSHIFT) != 0) {
Base = GetX86Reg (Frame, IA32_REG_EBP);
} else {
Base = 0;
}
}
else
{
Base = GetX86Reg (Frame, (Sib & MI_SIB_BASEMASK) >> MI_SIB_BASESHIFT);
}
//
// Now get the Index
//
if ((Sib & MI_SIB_INDEXMASK) == MI_SIB_INDEXNONE)
{
//
// Handle the special case where we don't have an index
//
Index = 0;
}
else
{
Index = GetX86Reg (Frame, (Sib & MI_SIB_INDEXMASK) >> MI_SIB_INDEXSHIFT);
}
Scale = 1 << ((Sib & MI_SIB_SSMASK) >> MI_SIB_SSSHIFT);
return (Base + (Index * Scale) + Displacement);
}
BOOLEAN
KiIa32Compute32BitEffectiveAddress(
IN PKTRAP_FRAME Frame,
IN OUT PUCHAR *InstAddr,
OUT PUINT_PTR Addr,
OUT PBOOLEAN RegisterMode
)
/*++
Routine Description:
Compute an effective address based on bytes in memory and the register
values passed in via the ia64 stack frame. The addressing mode is assumed to
be 32-bit.
Arguments:
Frame - Pointer to iA32 TrapFrame in the stack
InstAddr - Pointer to the first byte after the opcode.
Addr - Effective address.
RegisterMode - Indicates whether the effective address is inside a register or memory.
Return Value:
Returns TRUE if able to compute the EA, else returns FALSE.
Note:
Does not verify permission on an Effective Address. It only computes the
value and lets someone else worry if the process should have access to
that memory location.
--*/
{
UNALIGNED ULONG * UlongAddress;
UCHAR ModRm;
UCHAR Sib = 0;
LONG UNALIGNED *DisplacementPtr;
BOOLEAN ReturnCode = TRUE;
//
// This needs to be a signed value. Start off assuming no displacement
//
LONG Displacement = 0;
try
{
ModRm = *(*InstAddr)++;
//
// handle the register case first
//
if ((ModRm >> MI_MODSHIFT) == 3)
{
//
// yup, we have a register - the easy case...
//
*Addr = GetX86RegOffset (Frame, ModRm & MI_RMMASK);
*RegisterMode = TRUE;
return ReturnCode;
}
*RegisterMode = FALSE;
//
// See if we have a SIB
//
if ((ModRm & MI_RMMASK) == 4)
{
Sib = *(*InstAddr)++;
}
//
// Now decode the destination bits
//
switch (ModRm >> MI_MODSHIFT)
{
case 0:
//
// We have an indirect through a register
//
switch (ModRm & MI_RMMASK)
{
case 4:
//
// Deal with the SIB
//
*Addr = KiIa32ComputeSIBAddress (Frame, Displacement, Sib, ModRm);
break;
case 5:
//
// We have a 32-bit indirect...
//
UlongAddress = (UNALIGNED ULONG *)*InstAddr;
*Addr = *UlongAddress;
*InstAddr = (PUCHAR) (UlongAddress + 1);
break;
default:
//
// The default case is get the address from the register
//
*Addr = GetX86Reg (Frame, (ModRm & MI_RMMASK));
break;
}
break;
case 1:
//
// we have an 8 bit displacement, so grab the next byte
//
Displacement = (signed char) (*(*InstAddr)++);
if ((ModRm & MI_RMMASK) == 4)
{
//
// Have a SIB, so do that
//
*Addr = KiIa32ComputeSIBAddress (Frame, Displacement, Sib, ModRm);
}
else
{
//
// No SIB, life is easy
//
*Addr = GetX86Reg (Frame, (ModRm & MI_RMMASK)) + Displacement;
}
break;
case 2:
//
// we have a 32-bit displacement, so grab the next 4 bytes
//
DisplacementPtr = (PLONG) (*InstAddr);
Displacement = *DisplacementPtr++;
*InstAddr = (PUCHAR)DisplacementPtr;
if ((ModRm & MI_RMMASK) == 4)
{
//
// Have a SIB, so do that
//
*Addr = KiIa32ComputeSIBAddress (Frame, Displacement, Sib, ModRm);
}
else
{
//
// No SIB, life is easy
//
*Addr = GetX86Reg (Frame, (ModRm & MI_RMMASK)) + Displacement;
}
break;
default:
//
// we should have handled case 3 (register access)
// before getting here...
//
ReturnCode = FALSE;
break;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
ReturnCode = FALSE;
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32Compute32BitEffectiveAddress - Exception %lx\n",
GetExceptionCode());
}
#endif
}
//
// Make sure the address stays within 4GB range
//
if (ReturnCode == TRUE) {
*Addr = (*Addr & 0x000000007fffffffI64);
}
return ReturnCode;
}
BOOLEAN
KiIa32Compute16BitEffectiveAddress (
IN PKTRAP_FRAME Frame,
IN OUT PUCHAR *InstAddr,
OUT PUINT_PTR Addr,
OUT PBOOLEAN RegisterMode
)
/*++
Routine Description:
Compute an effective address based on bytes in memory and
the register values passed in via the ia64 stack frame. The addressing
mode is assumed to be 16-bit.
Arguments:
Frame - Pointer to iA32 TrapFrame in the stack.
InstAddr - Pointer to the first byte after the opcode.
Addr - Effective address.
RegisterMode - Indicates whether the effective address is inside a register or memory.
Return Value:
Returns TRUE if able to compute the EA, else returns FALSE.
Note:
Does not verify permission on an Effective Address. It only computes the
value and lets someone else worry if the process should have access to
that memory location.
--*/
{
UCHAR ModRm;
UCHAR DisplacementType = IA32_DISP_NONE;
USHORT UNALIGNED *Disp16;
LONG EffectiveAddress = 0;
BOOLEAN ReturnCode = TRUE;
try
{
//
// Read in the Mod/Rm and increment the instruction address
//
ModRm = *(*InstAddr)++;
*RegisterMode = FALSE;
//
// First pass
//
switch (ModRm >> MI_MODSHIFT)
{
case 0:
if ((ModRm & MI_RMMASK) == 6)
{
Disp16 = (USHORT UNALIGNED *) InstAddr;
*Addr = *Disp16;
*InstAddr = (*InstAddr + 2);
return ReturnCode;
}
DisplacementType = IA32_DISP_NONE;
break;
case 1:
DisplacementType = IA32_DISP8;
break;
case 2:
DisplacementType = IA32_DISP16;
break;
case 3:
*Addr = GetX86RegOffset (Frame, ModRm & MI_RMMASK);
*RegisterMode = TRUE;
return ReturnCode;
}
//
// Second pass
//
switch (ModRm & MI_RMMASK)
{
case 0:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBX) & 0xffff) +
(GetX86Reg(Frame, IA32_REG_ESI) & 0xffff) ;
break;
case 1:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBX) & 0xffff) +
(GetX86Reg(Frame, IA32_REG_EDI) & 0xffff) ;
break;
case 2:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBP) & 0xffff) +
(GetX86Reg(Frame, IA32_REG_ESI) & 0xffff) ;
break;
case 3:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBP) & 0xffff) +
(GetX86Reg(Frame, IA32_REG_EDI) & 0xffff) ;
break;
case 4:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_ESI) & 0xffff);
break;
case 5:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EDI) & 0xffff);
break;
case 6:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBP) & 0xffff);
break;
case 7:
EffectiveAddress = (GetX86Reg(Frame, IA32_REG_EBX) & 0xffff);
break;
}
//
// Read the displacement, if any
//
if (DisplacementType != IA32_DISP_NONE)
{
switch (DisplacementType)
{
case IA32_DISP8:
{
EffectiveAddress += (LONG) (**InstAddr);
*InstAddr = *InstAddr + 1;
}
break;
case IA32_DISP16:
{
Disp16 = (USHORT UNALIGNED *) InstAddr;
EffectiveAddress += (LONG) *Disp16;
*InstAddr = *InstAddr + 2;
}
break;
default:
#if DBG
DbgPrint("KE: KiIa32Compute16BitEffectiveAddress - Invalid displacement type %lx\n",
DisplacementType);
#endif
ReturnCode = FALSE;
break;
}
}
*Addr = EffectiveAddress;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32Compute16BitEffectiveAddress - Exception %lx\n",
GetExceptionCode());
}
#endif
ReturnCode = FALSE;
}
//
// Make sure the address stays within 4GB range
//
if (ReturnCode == TRUE) {
*Addr = (*Addr & 0x000000007fffffffI64);
}
return ReturnCode;
}
NTSTATUS
KiIa32UpdateFlags (
IN PIA32_INSTRUCTION Instruction,
IN ULONGLONG Operand1,
IN ULONGLONG Result,
IN ULONG Ia32Eflags
)
/*++
Routine Description:
Updates the Ia32 specified eflags according to the result value.
Arguments:
Instruction - Pointer to the instruction being processed.
Operand1 - First operand (value) of the instruction being emulated.
Result - Result value.
Ia32Eflags - Specific flags to update based on the result value.
Return Value:
NTSTATUS
--*/
{
ULONGLONG Temp = 0;
IA32_EFLAGS Eflags = Instruction->Eflags;
//
// Sanitize the destination value.
//
Result = (Result & MAXULONG);
if ((Ia32Eflags & IA32_EFLAGS_CF) != 0)
{
if (Result > Instruction->OperandMask)
{
Eflags.u.cf = 1;
}
else
{
Eflags.u.cf = 0;
}
}
if ((Ia32Eflags & IA32_EFLAGS_OF) != 0)
{
if (((Operand1 & Result) & 0x80000000UI64) != 0)
{
Eflags.u.of = 1;
}
else
{
Eflags.u.of = 0;
}
}
if ((Ia32Eflags & IA32_EFLAGS_SF) != 0)
{
switch (Instruction->OperandSize)
{
case 0xff:
Temp = 0x80UI64;
break;
case 0xffff:
Temp = 0x8000UI64;
break;
case 0xffffffff:
Temp = 0x80000000UI64;
break;
}
if (Result & Temp)
{
Eflags.u.sf = 1;
}
else
{
Eflags.u.sf = 0;
}
}
if ((Ia32Eflags & IA32_EFLAGS_ZF) != 0)
{
if (Result == 0)
{
Eflags.u.zf = 1;
}
else
{
Eflags.u.zf = 0;
}
}
if ((Ia32Eflags & IA32_EFLAGS_AF) != 0)
{
Eflags.u.af = (((Operand1 ^ Result) >> 4) & 0x01UI64);
}
//
// This needs to be the last one as it modifies the 'Result'
//
if ((Ia32Eflags & IA32_EFLAGS_PF) != 0)
{
Result = Result & Instruction->OperandMask;
Temp = 0;
while (Result)
{
Result = (Result & (Result - 1));
Temp++;
}
if ((Temp & 0x01UI64) == 0)
{
Eflags.u.pf = 1;
}
else
{
Eflags.u.pf = 1;
}
}
//
// Reset reserved values.
//
Eflags.u.v1 = 1;
Eflags.u.v2 = 0;
Eflags.u.v3 = 0;
Eflags.u.v4 = 0;
//
// Sanitize the flags
//
Eflags.Value = SANITIZE_AR24_EFLAGS (Eflags.Value, UserMode);
Instruction->Eflags = Eflags;
return STATUS_SUCCESS;
}
NTSTATUS
KiIa32UpdateResult (
IN PIA32_INSTRUCTION Instruction,
IN PIA32_OPERAND DestinationOperand,
IN ULONGLONG Result
)
/*++
Routine Description:
Writes the result value taking into consideration operand size.
Arguments:
Instruction - Pointer to the instruction being processed.
DestinationOperand - Operand to receive the result.
Result - Result value to write
Return Value:
NTSTATUS
--*/
{
UNALIGNED USHORT *UshortPtr;
UNALIGNED ULONG *UlongPtr;
NTSTATUS NtStatus = STATUS_SUCCESS;
//
// Update results according to operand size
//
try
{
if (DestinationOperand->RegisterMode == FALSE)
{
if (DestinationOperand->v > MM_MAX_WOW64_ADDRESS)
{
return STATUS_ACCESS_VIOLATION;
}
}
switch (Instruction->OperandSize)
{
case OPERANDSIZE_ONEBYTE:
*(PUCHAR)DestinationOperand->v = (UCHAR)Result;
break;
case OPERANDSIZE_TWOBYTES:
UshortPtr = (UNALIGNED USHORT *) DestinationOperand->v;
*UshortPtr = (USHORT)Result;
break;
case OPERANDSIZE_FOURBYTES:
UlongPtr =(UNALIGNED ULONG *) DestinationOperand->v;
*UlongPtr = (ULONG)Result;
break;
default:
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32UpdateResult() - Invalid operand size - %lx - %p\n",
Instruction->OperandSize, Instruction);
}
#endif
NtStatus = STATUS_UNSUCCESSFUL;
break;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode ();
#if DBG
DbgPrint("KE: KiIa32UpdateResult - Exception %lx - %p\n",
NtStatus, Instruction);
#endif
}
return NtStatus;
}
NTSTATUS
KiIa32ReadOperand1 (
IN PIA32_INSTRUCTION Instruction,
OUT PULONGLONG Operand1
)
/*++
Routine Description:
Reads the first (destination) operand of an instruction.
Arguments:
Instruction - Pointer to the instruction being processed.
Operand1 - Buffer to receive the operand value.
Return Value:
NTSTATUS
--*/
{
UNALIGNED ULONG *UlongPtr;
UNALIGNED USHORT *UshortPtr;
NTSTATUS NtStatus = STATUS_SUCCESS;
try
{
switch (Instruction->Description->Type)
{
case IA32_PARAM_RM_IMM8SIGN:
case IA32_PARAM_RM_IMM:
case IA32_PARAM_RM_R:
case IA32_PARAM_R_RM8:
case IA32_PARAM_R_RM:
case IA32_PARAM_RM:
case IA32_PARAM_SEGREG_RM:
if (Instruction->OperandSize == OPERANDSIZE_TWOBYTES)
{
UshortPtr = (UNALIGNED USHORT *) Instruction->Operand1.v;
*Operand1 = (ULONGLONG) *UshortPtr;
}
else
{
UlongPtr = (UNALIGNED ULONG *) Instruction->Operand1.v;
*Operand1 = (ULONGLONG) *UlongPtr;
}
break;
case IA32_PARAM_RM8_IMM8:
case IA32_PARAM_RM8_R:
case IA32_PARAM_RM8:
case IA32_PARAM_SEGREG_RM8:
*Operand1 = (ULONGLONG) (*(PUCHAR)Instruction->Operand1.v);
break;
default:
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32ReadRm - Invalid opcode type %lx - %p\n",
Instruction->Description->Type, Instruction);
}
NtStatus = STATUS_UNSUCCESSFUL;
#endif
break;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode ();
#if DBG
DbgPrint("KE: KiIa32ReadOperand1 - Exception %lx - %p\n",
NtStatus, Instruction);
#endif
}
return NtStatus;
}
NTSTATUS
KiIa32ReadOperand2 (
IN PIA32_INSTRUCTION Instruction,
OUT PULONGLONG Operand2
)
/*++
Routine Description:
Reads the second (source) operand of an instruction.
Arguments:
Instruction - Pointer to the instruction being processed.
Operand1 - Buffer to receive the operand value.
Return Value:
NTSTATUS
--*/
{
UNALIGNED ULONG *UlongPtr;
UNALIGNED USHORT *UshortPtr;
NTSTATUS NtStatus = STATUS_SUCCESS;
try
{
switch (Instruction->Description->Type)
{
case IA32_PARAM_RM8_IMM8:
case IA32_PARAM_RM_IMM8SIGN:
*Operand2 = (UCHAR)Instruction->Operand2.v;
break;
case IA32_PARAM_RM_IMM:
*Operand2 = Instruction->Operand2.v & Instruction->OperandMask;
break;
case IA32_PARAM_RM8_R:
case IA32_PARAM_R_RM8:
*Operand2 = (ULONGLONG)(*(PUCHAR)Instruction->Operand2.v);
break;
case IA32_PARAM_RM_R:
case IA32_PARAM_R_RM:
if (Instruction->OperandSize == OPERANDSIZE_TWOBYTES)
{
UshortPtr = (UNALIGNED USHORT *) Instruction->Operand2.v;
*Operand2 = (ULONGLONG) *UshortPtr;
}
else
{
UlongPtr = (UNALIGNED ULONG *) Instruction->Operand2.v;
*Operand2 = (ULONGLONG) *UlongPtr;
}
break;
case IA32_PARAM_SEGREG_RM8:
case IA32_PARAM_SEGREG_RM:
break;
default:
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32ReadOperand2 - Invalid type %lx - %p\n",
Instruction->Description->Type, Instruction);
}
NtStatus = STATUS_UNSUCCESSFUL;
#endif
break;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode ();
#if DBG
DbgPrint("KE: KiIa32ReadOperand2 - Exception %lx - %p\n",
NtStatus, Instruction);
#endif
}
return NtStatus;
}
NTSTATUS
KiIa32InstructionAddWithIncrement (
IN PIA32_INSTRUCTION Instruction,
IN ULONG Increment
)
/*++
Routine Description:
Common routine implementing Ia32 add, adc, sub and sbb instructions.
Arguments:
Instruction - Pointer to the instruction being processed.
Increment - Specifies the carry value.
Return Value:
NTSTATUS
--*/
{
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
ULONGLONG Operand1;
UCHAR Imm8;
char SignImm8;
BOOLEAN Subtract;
NTSTATUS NtStatus;
switch (Instruction->Description->Opcode)
{
case Ia32_Add:
case Ia32_Adc:
Subtract = FALSE;
break;
case Ia32_Sub:
case Ia32_Sbb:
Subtract = TRUE;
break;
default:
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32InstructionAddWithIncrement - Invalid opcode %lx - %p\n",
Instruction->Description->Opcode, Instruction);
}
#endif
return STATUS_UNSUCCESSFUL;
break;
}
NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst);
if (NT_SUCCESS (NtStatus))
{
Operand1 = UlongDst;
NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus))
{
switch (Instruction->Description->Type)
{
case IA32_PARAM_RM_IMM8SIGN:
SignImm8 = (char) UlongSrc;
if (Subtract)
UlongDst = (UlongDst - (Increment + SignImm8));
else
UlongDst = UlongDst + Increment + SignImm8;
break;
case IA32_PARAM_RM8_IMM8:
Imm8 = (UCHAR) UlongSrc;
if (Subtract)
UlongDst = (UlongDst - (Increment + Imm8));
else
UlongDst = UlongDst + Increment + Imm8;
break;
case IA32_PARAM_RM_IMM:
default:
if (Subtract)
UlongDst = (UlongDst - (Increment + UlongSrc));
else
UlongDst = UlongDst + Increment + UlongSrc;
break;
}
//
// Update results according to operand size
//
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongDst
);
//
// Eflags update
//
if (NT_SUCCESS (NtStatus))
{
KiIa32UpdateFlags (
Instruction,
Operand1,
UlongDst,
(IA32_EFLAGS_CF | IA32_EFLAGS_SF | IA32_EFLAGS_OF |
IA32_EFLAGS_PF | IA32_EFLAGS_ZF | IA32_EFLAGS_AF)
);
}
}
}
return NtStatus;
}
NTSTATUS
KiIa32InstructionAdc (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Adc instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
return KiIa32InstructionAddWithIncrement (
Instruction,
(ULONG)Instruction->Eflags.u.cf);
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionAdd (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Add instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
return KiIa32InstructionAddWithIncrement (
Instruction,
0);
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionArithmeticBitwiseHelper (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
And, Or & Xor instructions handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
ULONGLONG Operand1;
NTSTATUS NtStatus;
NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst);
if (NT_SUCCESS (NtStatus))
{
Operand1 = UlongDst;
NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus))
{
switch (Instruction->Description->Opcode)
{
case Ia32_And:
UlongDst = UlongDst & UlongSrc;
break;
case Ia32_Or:
UlongDst = UlongDst | UlongSrc;
break;
case Ia32_Xor:
UlongDst = UlongDst ^ UlongSrc;
break;
default:
#if DBG
NtStatus = STATUS_UNSUCCESSFUL;
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32InstructionBitwiseHelper - Invalid operation %lx - %p\n",
Instruction->Description->Opcode, Instruction);
}
#endif
break;
}
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongDst
);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateFlags (
Instruction,
Operand1,
UlongDst,
(IA32_EFLAGS_SF | IA32_EFLAGS_PF | IA32_EFLAGS_ZF)
);
Instruction->Eflags.u.cf = 0;
Instruction->Eflags.u.of = 0;
}
}
}
}
return NtStatus;
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionBitTestHelper (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Bt, Bts, Btr & Btc instructions handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
ULONGLONG BitTestResult;
NTSTATUS NtStatus;
NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus))
{
if (Instruction->Operand2.RegisterMode == TRUE)
{
if (Instruction->Prefix.b.AddressOverride == 1)
{
Instruction->Operand1.v += ((UlongSrc >> 4) << 1);
UlongSrc &= 0x0f;
}
else
{
Instruction->Operand1.v += ((UlongSrc >> 5) << 2);
UlongSrc &= 0x1f;
}
}
NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst);
if (NT_SUCCESS (NtStatus))
{
BitTestResult = (UlongDst & (1 << UlongSrc));
if (BitTestResult)
{
Instruction->Eflags.u.cf = 1;
}
else
{
Instruction->Eflags.u.cf = 0;
}
switch (Instruction->Description->Opcode)
{
case Ia32_Btc:
UlongDst ^= (1 << UlongSrc);
break;
case Ia32_Btr:
UlongDst &= (~(1 << UlongSrc));
break;
case Ia32_Bts:
UlongDst |= (1 << UlongSrc);
break;
}
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongDst
);
}
}
return NtStatus;
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionOneParamHelper (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Inc, Dec, Neg & Not instructions handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
UCHAR Opcode;
ULONG FlagsAffected = 0;
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
NTSTATUS NtStatus;
NtStatus = KiIa32ReadOperand1 (
Instruction,
&UlongDst
);
if (NT_SUCCESS (NtStatus))
{
UlongSrc = UlongDst;
Opcode = Instruction->Description->Opcode;
switch (Opcode)
{
case Ia32_Inc:
UlongDst += 1;
break;
case Ia32_Dec:
UlongDst -= 1;
break;
case Ia32_Neg:
UlongDst = -(LONGLONG)UlongDst;
break;
case Ia32_Not:
UlongDst = ~UlongDst;
break;
}
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongDst
);
if (NT_SUCCESS (NtStatus))
{
switch (Opcode)
{
case Ia32_Inc:
case Ia32_Dec:
FlagsAffected = (IA32_EFLAGS_SF | IA32_EFLAGS_PF |
IA32_EFLAGS_ZF);
break;
case Ia32_Neg:
if (UlongDst == 0)
Instruction->Eflags.u.cf = 0;
else
Instruction->Eflags.u.cf = 1;
FlagsAffected = (IA32_EFLAGS_SF | IA32_EFLAGS_PF |
IA32_EFLAGS_ZF | IA32_EFLAGS_AF | IA32_EFLAGS_OF);
break;
}
if (FlagsAffected != 0)
{
NtStatus = KiIa32UpdateFlags (
Instruction,
UlongSrc,
UlongDst,
FlagsAffected
);
}
}
}
return NtStatus;
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionXadd (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Xadd instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
ULONGLONG Operand1;
ULONGLONG Temp;
NTSTATUS NtStatus;
NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst);
if (NT_SUCCESS (NtStatus))
{
Operand1 = UlongDst;
NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus))
{
Temp = UlongDst;
UlongDst += UlongSrc;
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongDst
);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand2,
Temp
);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateFlags (
Instruction,
Operand1,
UlongDst,
(IA32_EFLAGS_CF | IA32_EFLAGS_SF | IA32_EFLAGS_PF |
IA32_EFLAGS_ZF | IA32_EFLAGS_OF | IA32_EFLAGS_AF)
);
}
}
}
}
return NtStatus;
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionXchg (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Xchg instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
NTSTATUS NtStatus;
NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongSrc
);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand2,
UlongDst
);
}
}
}
return NtStatus;
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32InstructionCmpXchg (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Cmpxchg instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
ULONGLONG UlongDst;
ULONGLONG UlongSrc;
ULONGLONG Accumulator;
IA32_OPERAND AccumulatorOperand;
NTSTATUS NtStatus;
NtStatus = KiIa32ReadOperand1 (Instruction, &UlongDst);
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32ReadOperand2 (Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus))
{
Accumulator = GetX86Reg (TrapFrame, IA32_REG_EAX);
Accumulator &= Instruction->OperandMask;
if (Accumulator == UlongDst)
{
Instruction->Eflags.u.zf = 1;
NtStatus = KiIa32UpdateResult (
Instruction,
&Instruction->Operand1,
UlongSrc
);
}
else
{
Instruction->Eflags.u.zf = 0;
AccumulatorOperand.RegisterMode = TRUE;
AccumulatorOperand.v = GetX86RegOffset (TrapFrame, IA32_REG_EAX);
NtStatus = KiIa32UpdateResult (
Instruction,
&AccumulatorOperand,
UlongDst
);
}
if (NT_SUCCESS (NtStatus))
{
NtStatus = KiIa32UpdateFlags (
Instruction,
UlongDst,
UlongDst,
(IA32_EFLAGS_CF | IA32_EFLAGS_SF |
IA32_EFLAGS_PF | IA32_EFLAGS_OF |
IA32_EFLAGS_AF)
);
}
}
}
return NtStatus;
}
NTSTATUS
KiIa32InstructionCmpXchg8b (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Cmpxchg8b instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
UNALIGNED ULONGLONG *UlongDst;
UNALIGNED ULONGLONG *UlongSrc;
ULONGLONG EdxEax;
EdxEax = GetX86Reg (TrapFrame, IA32_REG_EDX);
EdxEax <<= 32;
EdxEax |= GetX86Reg (TrapFrame, IA32_REG_EAX);
UlongDst = (PULONGLONG)Instruction->Operand1.v;
if (*UlongDst == EdxEax)
{
Instruction->Eflags.u.zf = 1;
*UlongDst = ((((ULONGLONG) GetX86Reg (TrapFrame, IA32_REG_ECX)) << 32) |
((ULONGLONG) GetX86Reg (TrapFrame, IA32_REG_EBX)));
}
else
{
Instruction->Eflags.u.zf = 0;
UlongSrc = (PULONGLONG) GetX86RegOffset (TrapFrame, IA32_REG_EDX);
*UlongSrc = ((*UlongDst) >> 32);
UlongSrc = (PULONGLONG) GetX86RegOffset (TrapFrame, IA32_REG_EAX);
*UlongSrc = ((*UlongDst) & 0xffffffff);
}
return STATUS_SUCCESS;
}
NTSTATUS
KiIa32InstructionMoveSeg (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Mov Seg-Reg instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
return STATUS_NOT_IMPLEMENTED;
UNREFERENCED_PARAMETER (TrapFrame);
UNREFERENCED_PARAMETER (Instruction);
}
NTSTATUS
KiIa32LocateInstruction (
IN PKTRAP_FRAME TrapFrame,
OUT PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Searches the OpcodeDescription table for matching instruction. Fills any relevant
prefix values inside the Instruction structure.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to an Instruction structure to receive the opcode description.
Return Value:
NTSTATUS
--*/
{
BOOLEAN PrefixLoop;
BOOLEAN Match;
UCHAR ByteValue;
UCHAR ByteBuffer[4];
PUCHAR RegOpcodeByte;
PIA32_OPCODE_DESCRIPTION OpcodeDescription;
ULONG Count;
NTSTATUS NtStatus = STATUS_SUCCESS;
PrefixLoop = TRUE;
while (PrefixLoop)
{
try
{
ByteValue = ProbeAndReadUchar ((PUCHAR)Instruction->Eip);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
break;
}
switch (ByteValue)
{
case MI_LOCK_PREFIX:
Instruction->Prefix.b.Lock = 1;
break;
case MI_REPNE_PREFIX:
Instruction->Prefix.b.RepNe = 1;
break;
case MI_REP_PREFIX:
Instruction->Prefix.b.Rep = 1;
break;
case MI_SEGCS_PREFIX:
Instruction->Prefix.b.CsOverride = 1;
break;
case MI_SEGSS_PREFIX:
Instruction->Prefix.b.SsOverride = 1;
break;
case MI_SEGDS_PREFIX:
Instruction->Prefix.b.DsOverride = 1;
break;
case MI_SEGES_PREFIX:
Instruction->Prefix.b.EsOverride = 1;
break;
case MI_SEGFS_PREFIX:
Instruction->Prefix.b.FsOverride = 1;
break;
case MI_SEGGS_PREFIX:
Instruction->Prefix.b.GsOverride = 1;
break;
case MI_OPERANDSIZE_PREFIX:
Instruction->Prefix.b.SizeOverride = 1;
break;
case MI_ADDRESSOVERRIDE_PREFIX:
Instruction->Prefix.b.AddressOverride = 1;
break;
default:
PrefixLoop = FALSE;
break;
}
if (PrefixLoop == TRUE)
{
Instruction->Eip++;
}
}
try
{
RtlCopyMemory(ByteBuffer, Instruction->Eip, sizeof (ByteBuffer));
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
}
if (NT_SUCCESS (NtStatus))
{
//
// Locate the opcode
//
Match = FALSE;
OpcodeDescription = OpcodesDescription;
Count = (sizeof (OpcodesDescription) / sizeof (IA32_OPCODE_DESCRIPTION));
while (Count != 0)
{
Count--;
if (OpcodeDescription->Byte1 == ByteBuffer[0])
{
Match = TRUE;
if (OpcodeDescription->Count.m.Bytes == 2)
{
RegOpcodeByte = &ByteBuffer[2];
if (OpcodeDescription->Byte2 != ByteBuffer[1])
{
Match = FALSE;
}
}
else
{
RegOpcodeByte = &ByteBuffer[1];
}
if ((Match == TRUE) &&
(OpcodeDescription->Count.m.RegOpcode))
{
if (OpcodeDescription->Byte3 != ((*RegOpcodeByte & MI_REGMASK) >> MI_REGSHIFT))
{
Match = FALSE;
}
}
if (Match == TRUE)
{
break;
}
}
OpcodeDescription++;
}
if (Match != TRUE)
{
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32LocateInstruction - Unable to locate instruction %p\n",
Instruction);
}
#endif
NtStatus = STATUS_UNSUCCESSFUL;
}
if (NT_SUCCESS (NtStatus))
{
Instruction->Description = OpcodeDescription;
Instruction->Eip += OpcodeDescription->Count.m.Bytes;
}
}
return NtStatus;
UNREFERENCED_PARAMETER (TrapFrame);
}
NTSTATUS
KiIa32DecodeInstruction (
IN PKTRAP_FRAME TrapFrame,
OUT PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Decodes the instruction prefixes and operands.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to an Instruction structure to receive the opcode description.
Return Value:
NTSTATUS
--*/
{
UCHAR InstructionType;
UCHAR ModRm;
UNALIGNED USHORT *UnalignedUshort;
UNALIGNED ULONG *UnalignedUlong;
IA32_OPERAND Temp;
BOOLEAN ReturnCode;
NTSTATUS NtStatus;
//
// Check instruction pointer validity
//
if (TrapFrame->StIIP > MM_MAX_WOW64_ADDRESS) {
return STATUS_ACCESS_VIOLATION;
}
//
// Initialize the instruction pointer
//
Instruction->Eip = (PCHAR) TrapFrame->StIIP;
KiIa32GetX86Eflags (Instruction->Eflags);
//
// Locate a description for the instruction
//
NtStatus = KiIa32LocateInstruction (TrapFrame, Instruction);
if (NT_SUCCESS (NtStatus))
{
//
// Let's parse the arguments
//
InstructionType = Instruction->Description->Type;
switch (InstructionType)
{
case IA32_PARAM_RM8_IMM8:
case IA32_PARAM_RM8_R:
case IA32_PARAM_R_RM8:
case IA32_PARAM_RM8:
case IA32_PARAM_SEGREG_RM8:
Instruction->OperandSize = OPERANDSIZE_ONEBYTE;
Instruction->OperandMask = 0xff;
break;
case IA32_PARAM_RM_IMM:
case IA32_PARAM_RM_IMM8SIGN:
case IA32_PARAM_RM_R:
case IA32_PARAM_R_RM:
case IA32_PARAM_RM:
if (Instruction->Prefix.b.SizeOverride)
{
Instruction->OperandSize = OPERANDSIZE_TWOBYTES;
Instruction->OperandMask = 0xffff;
}
else
{
Instruction->OperandSize = OPERANDSIZE_FOURBYTES;
Instruction->OperandMask = 0xffffffff;
}
break;
break;
case IA32_PARAM_SEGREG_RM:
Instruction->OperandSize = OPERANDSIZE_TWOBYTES;
Instruction->OperandMask = 0xffff;
break;
default:
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32DecodeInstruction - Invalid Instruction type %lx, %p\n",
Instruction->Description->Type, Instruction);
}
#endif
return STATUS_UNSUCCESSFUL;
break;
}
try
{
ModRm = ProbeAndReadUchar ((PUCHAR)Instruction->Eip);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
//
// Eip should be pointing now at the bytes following the opcode
//
if (Instruction->Prefix.b.AddressOverride == 0)
{
ReturnCode = KiIa32Compute32BitEffectiveAddress (
TrapFrame,
(PUCHAR *)&Instruction->Eip,
&Instruction->Operand1.v,
&Instruction->Operand1.RegisterMode
);
}
else
{
ReturnCode = KiIa32Compute16BitEffectiveAddress (
TrapFrame,
(PUCHAR *)&Instruction->Eip,
&Instruction->Operand1.v,
&Instruction->Operand1.RegisterMode
);
}
if (ReturnCode != TRUE)
{
NtStatus = STATUS_UNSUCCESSFUL;
}
if (Instruction->Prefix.b.FsOverride)
{
try
{
Instruction->Operand1.v += (ULONGLONG)NtCurrentTeb32();
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode ();
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32DecodeInstruction - Exception while reading NtCurrentTeb32() - %p\n",
Instruction);
}
#endif
}
}
//
// Read in more args
//
if (NT_SUCCESS (NtStatus))
{
switch (InstructionType)
{
case IA32_PARAM_RM8_IMM8:
case IA32_PARAM_RM_IMM8SIGN:
try
{
Instruction->Operand2.v = (ULONG_PTR) ProbeAndReadUchar ((PUCHAR)Instruction->Eip);
Instruction->Eip += 1;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
}
break;
case IA32_PARAM_RM_IMM:
try
{
if (Instruction->OperandSize == OPERANDSIZE_TWOBYTES)
{
UnalignedUshort = (UNALIGNED USHORT *) Instruction->Eip;
Instruction->Operand2.v = (ULONG_PTR) *UnalignedUshort;
Instruction->Eip += sizeof (USHORT);
}
else
{
UnalignedUlong = (UNALIGNED ULONG *) Instruction->Eip;
Instruction->Operand2.v = (ULONG_PTR) *UnalignedUlong;
Instruction->Eip += sizeof (ULONG);
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
}
break;
case IA32_PARAM_RM8_R:
case IA32_PARAM_R_RM8:
case IA32_PARAM_RM_R:
case IA32_PARAM_R_RM:
Instruction->Operand2.v = GetX86RegOffset (
TrapFrame,
(ModRm & MI_REGMASK) >> MI_REGSHIFT);
Instruction->Operand2.RegisterMode = TRUE;
break;
case IA32_PARAM_RM8:
case IA32_PARAM_RM:
case IA32_PARAM_SEGREG_RM8:
case IA32_PARAM_SEGREG_RM:
break;
default:
NtStatus = STATUS_UNSUCCESSFUL;
#if DBG
DbgPrint("KE: KiIa32DecodeInstruction - Invalid instruction type %lx - %p\n",
InstructionType, Instruction);
#endif
break;
}
//
// Adjust operands order
//
if (NT_SUCCESS (NtStatus))
{
switch (InstructionType)
{
case IA32_PARAM_R_RM8:
case IA32_PARAM_R_RM:
Temp = Instruction->Operand2;
Instruction->Operand2 = Instruction->Operand1;
Instruction->Operand1 = Temp;
break;
}
}
}
}
return NtStatus;
}
NTSTATUS
KiIa32ExecuteInstruction (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Executes the instruction handler.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus;
#if DBG
if (KiIa32InstructionEmulationDbg)
{
DbgPrint("KE: KiIa32ExecuteInstruction - Calling %s %lx, %lx. Instruction = %p\n",
KiIa32InstructionHandlerNames[Instruction->Description->Opcode],
Instruction->Operand1.v,
Instruction->Operand2.v,
Instruction);
}
#endif
NtStatus = KiIa32InstructionHandler[Instruction->Description->Opcode] (
TrapFrame,
Instruction
);
//
// If all is good...
//
if (NT_SUCCESS (NtStatus))
{
TrapFrame->StIIP = (ULONGLONG) Instruction->Eip;
KiIa32SetX86Eflags (Instruction->Eflags);
}
return NtStatus;
}
NTSTATUS
KiIa32EmulateInstruction (
IN PKTRAP_FRAME TrapFrame,
IN PIA32_INSTRUCTION Instruction
)
/*++
Routine Description:
Emulates the instruction and emulates the lock prefix, if any.
Arguments:
TrapFrame - Pointer to TrapFrame.
Instruction - Pointer to the instruction being processed.
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus;
//
// Acquire the lock mutex
//
if (Instruction->Prefix.b.Lock)
{
if (ExAcquireRundownProtection (&PsGetCurrentThread()->RundownProtect) == FALSE)
{
return STATUS_UNSUCCESSFUL;
}
KiIa32AcquireMisalignedLockFastMutex ();
}
try
{
NtStatus = KiIa32ExecuteInstruction (TrapFrame, Instruction);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
NtStatus = GetExceptionCode();
}
//
// Release the lock mutex
//
if (Instruction->Prefix.b.Lock)
{
KiIa32ReleaseMisalignedLockFastMutex ();
ExReleaseRundownProtection (&PsGetCurrentThread()->RundownProtect);
}
return NtStatus;
}
NTSTATUS
KiIa32InterceptUnalignedLock (
IN PKTRAP_FRAME TrapFrame
)
/*++
Routine Description:
Handles misaligned lock interception raised by the iVE.
Arguments:
TrapFrame - Pointer to TrapFrame.
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus;
IA32_INSTRUCTION Instruction;
RtlZeroMemory (&Instruction, sizeof (Instruction));
//
// Decode the faulting instruction
//
NtStatus = KiIa32DecodeInstruction (TrapFrame, &Instruction);
if (NT_SUCCESS (NtStatus))
{
//
// xchg instruction asserts the lock by default
//
if (Instruction.Description->Opcode == Ia32_Xchg)
{
Instruction.Prefix.b.Lock = 1;
}
//
// Execute the x86 instruction by emulating its behaviour
//
NtStatus = KiIa32EmulateInstruction (TrapFrame, &Instruction);
}
if (NtStatus == STATUS_UNSUCCESSFUL)
{
NtStatus = STATUS_PRIVILEGED_INSTRUCTION;
}
return NtStatus;
}
NTSTATUS
KiIa32ValidateInstruction (
IN PKTRAP_FRAME TrapFrame
)
/*++
Routine Description:
This routine valiates the instruction that we trapped for. Currently,
the following instructions are checked:
- mov ss, r/m : the register/memory is validated to contain
a valid stack-selector value.
NOTE: This routine is only called for trap instructions (i.e. IIP is incremented
after the fault).
Arguments:
TrapFrame - Pointer to TrapFrame.
Return Value:
NTSTATUS
--*/
{
NTSTATUS NtStatus;
IA32_INSTRUCTION Instruction;
ULONGLONG UlongSrc;
ULONGLONG StIIP;
RtlZeroMemory (&Instruction, sizeof (Instruction));
//
// Adjust the instruction
//
StIIP = TrapFrame->StIIP;
TrapFrame->StIIP = TrapFrame->StIIPA;
//
// Decode the faulting instruction
//
NtStatus = KiIa32DecodeInstruction (TrapFrame, &Instruction);
if (NT_SUCCESS (NtStatus))
{
//
// Parse the opcode here
//
switch (Instruction.Description->Opcode)
{
case Ia32_MovToSeg:
{
//
// Validate the stack-selector being loaded
//
NtStatus = KiIa32ReadOperand1 (&Instruction, &UlongSrc);
if (NT_SUCCESS (NtStatus)) {
//
// If not a valid selector value
//
if ((UlongSrc != 0x23) &&
(UlongSrc != 0x1b) &&
(UlongSrc != 0x3b)) {
NtStatus = STATUS_ILLEGAL_INSTRUCTION;
}
}
}
break;
default:
NtStatus = STATUS_ILLEGAL_INSTRUCTION;
break;
}
} else {
NtStatus = STATUS_ILLEGAL_INSTRUCTION;
}
//
// Restore the saved IIP
//
if (NT_SUCCESS (NtStatus)) {
TrapFrame->StIIP = StIIP;
}
return NtStatus;
}