413 lines
9.5 KiB
C
413 lines
9.5 KiB
C
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
emulate.c
|
||
|
||
Abstract:
|
||
|
||
This module implements an instruction level emulator for the execution
|
||
of x86 code. It is a complete 386/486 emulator, but only implements
|
||
real mode execution. Thus 32-bit addressing and operands are supported,
|
||
but paging and protected mode operations are not supported. The code is
|
||
written with the primary goals of being complete and small. Thus speed
|
||
of emulation is not important.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 2-Sep-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "nthal.h"
|
||
#include "emulate.h"
|
||
|
||
VOID
|
||
XmInitializeEmulator (
|
||
IN USHORT StackSegment,
|
||
IN USHORT StackOffset,
|
||
IN PXM_READ_IO_SPACE ReadIoSpace,
|
||
IN PXM_WRITE_IO_SPACE WriteIoSpace,
|
||
IN PXM_TRANSLATE_ADDRESS TranslateAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the state of the x86 emulator.
|
||
|
||
Arguments:
|
||
|
||
StackSegment - Supplies the stack segment value.
|
||
|
||
StackOffset - Supplies the stack offset value.
|
||
|
||
ReadIoSpace - Supplies a pointer to a the function that reads from
|
||
I/O space given a datatype and port number.
|
||
|
||
WriteIoSpace - Supplies a pointer to a function that writes to I/O
|
||
space given a datatype, port number, and value.
|
||
|
||
TranslateAddress - Supplies a pointer to the function that translates
|
||
segment/offset address pairs into a pointer to memory or I/O space.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
LONG Index;
|
||
PRXM_CONTEXT P = &XmContext;
|
||
PULONG Vector;
|
||
|
||
//
|
||
// Clear the emulator context.
|
||
//
|
||
|
||
memset((PCHAR)P, 0, sizeof(XM_CONTEXT));
|
||
|
||
//
|
||
// Initialize the segment registers.
|
||
//
|
||
|
||
Index = GS;
|
||
do {
|
||
P->SegmentLimit[Index] = 0xffff;
|
||
Index -= 1;
|
||
} while (Index >= ES);
|
||
|
||
//
|
||
// Initialize the stack segment register and offset.
|
||
//
|
||
|
||
P->SegmentRegister[SS] = StackSegment;
|
||
P->Gpr[ESP].Exx = StackOffset;
|
||
|
||
//
|
||
// Set the address of the read I/O space, write I/O space, and translate
|
||
// functions.
|
||
//
|
||
|
||
P->ReadIoSpace = ReadIoSpace;
|
||
P->WriteIoSpace = WriteIoSpace;
|
||
P->TranslateAddress = TranslateAddress;
|
||
|
||
//
|
||
// Get address of interrupt vector table and initialize all vector to
|
||
// point to an iret instruction at location 0x500.
|
||
//
|
||
//
|
||
// N.B. It is assumed that the vector table is contiguous in emulated
|
||
// memory.
|
||
//
|
||
|
||
Vector = (PULONG)(P->TranslateAddress)(0, 0);
|
||
Vector[0x500 / 4] = 0x000000cf;
|
||
Index = 0;
|
||
do {
|
||
Vector[Index] = 0x00000500;
|
||
Index += 1;
|
||
} while (Index < 256);
|
||
|
||
|
||
XmEmulatorInitialized = TRUE;
|
||
return;
|
||
}
|
||
|
||
XM_STATUS
|
||
XmEmulateFarCall (
|
||
IN USHORT Segment,
|
||
IN USHORT Offset,
|
||
IN OUT PXM86_CONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates a far call by pushing a special exit
|
||
sequence on the stack and then starting instruction execution
|
||
at the address specified by the respective segment and offset.
|
||
|
||
Arguments:
|
||
|
||
Segment - Supplies the segment in which to start execution.
|
||
|
||
Offset - Supplies the offset within the code segment to start
|
||
execution.
|
||
|
||
Context - Supplies a pointer to an x86 context structure.
|
||
|
||
Return Value:
|
||
|
||
The emulation completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PRXM_CONTEXT P = &XmContext;
|
||
PUSHORT Stack;
|
||
|
||
//
|
||
// If the emulator has not been initialized, return an error.
|
||
//
|
||
|
||
if (XmEmulatorInitialized == FALSE) {
|
||
return XM_EMULATOR_NOT_INITIALIZED;
|
||
}
|
||
|
||
//
|
||
// Get address of current stack pointer, push exit markers, and
|
||
// update stack pointer.
|
||
//
|
||
// N.B. It is assumed that the stack pointer is within range and
|
||
// contiguous in emulated memory.
|
||
//
|
||
|
||
Stack = (PUSHORT)(P->TranslateAddress)(P->SegmentRegister[SS], P->Gpr[SP].Xx);
|
||
*--Stack = 0xffff;
|
||
*--Stack = 0xffff;
|
||
P->Gpr[SP].Xx -= 4;
|
||
|
||
//
|
||
// Emulate the specified instruction stream and return the final status.
|
||
//
|
||
|
||
return XmEmulateStream(&XmContext, Segment, Offset, Context);
|
||
}
|
||
|
||
XM_STATUS
|
||
XmEmulateInterrupt (
|
||
IN UCHAR Interrupt,
|
||
IN OUT PXM86_CONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates an interrrupt by pushing a special exit
|
||
sequence on the stack and then starting instruction execution
|
||
at the address specified by the respective interrupt vector.
|
||
|
||
Arguments:
|
||
|
||
Interrupt - Supplies the number of the interrupt that is emulated.
|
||
|
||
Context - Supplies a pointer to an x86 context structure.
|
||
|
||
Return Value:
|
||
|
||
The emulation completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PRXM_CONTEXT P = &XmContext;
|
||
USHORT Segment;
|
||
USHORT Offset;
|
||
PUSHORT Stack;
|
||
PULONG Vector;
|
||
|
||
//
|
||
// If the emulator has not been initialized, return an error.
|
||
//
|
||
|
||
if (XmEmulatorInitialized == FALSE) {
|
||
return XM_EMULATOR_NOT_INITIALIZED;
|
||
}
|
||
|
||
//
|
||
// Get address of current stack pointer, push exit markers, and
|
||
// update stack pointer.
|
||
//
|
||
// N.B. It is assumed that the stack pointer is within range and
|
||
// contiguous in emulated memory.
|
||
//
|
||
|
||
Stack = (PUSHORT)(P->TranslateAddress)(P->SegmentRegister[SS], P->Gpr[SP].Xx);
|
||
*--Stack = 0;
|
||
*--Stack = 0xffff;
|
||
*--Stack = 0xffff;
|
||
P->Gpr[SP].Xx -= 6;
|
||
|
||
//
|
||
// Get address of interrupt vector table and set code segment and IP
|
||
// values.
|
||
//
|
||
//
|
||
// N.B. It is assumed that the vector table is contiguous in emulated
|
||
// memory.
|
||
//
|
||
|
||
Vector = (PULONG)(P->TranslateAddress)(0, 0);
|
||
Segment = (USHORT)(Vector[Interrupt] >> 16);
|
||
Offset = (USHORT)(Vector[Interrupt] & 0xffff);
|
||
|
||
//
|
||
// Emulate the specified instruction stream and return the final status.
|
||
//
|
||
|
||
return XmEmulateStream(&XmContext, Segment, Offset, Context);
|
||
}
|
||
|
||
XM_STATUS
|
||
XmEmulateStream (
|
||
PRXM_CONTEXT P,
|
||
IN USHORT Segment,
|
||
IN USHORT Offset,
|
||
IN OUT PXM86_CONTEXT Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function establishes the specfied context and emulates the
|
||
specified instruction stream until exit conditions are reached..
|
||
|
||
Arguments:
|
||
|
||
Segment - Supplies the segment in which to start execution.
|
||
|
||
Offset - Supplies the offset within the code segment to start
|
||
execution.
|
||
|
||
Context - Supplies a pointer to an x86 context structure.
|
||
|
||
Return Value:
|
||
|
||
The emulation completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
XM_STATUS Status;
|
||
|
||
//
|
||
// Set the x86 emulator registers from the specified context.
|
||
//
|
||
|
||
P->Gpr[EAX].Exx = Context->Eax;
|
||
P->Gpr[ECX].Exx = Context->Ecx;
|
||
P->Gpr[EDX].Exx = Context->Edx;
|
||
P->Gpr[EBX].Exx = Context->Ebx;
|
||
P->Gpr[EBP].Exx = Context->Ebp;
|
||
P->Gpr[ESI].Exx = Context->Esi;
|
||
P->Gpr[EDI].Exx = Context->Edi;
|
||
P->SegmentRegister[DS] = Context->SegDs;
|
||
P->SegmentRegister[ES] = Context->SegEs;
|
||
|
||
//
|
||
// Set the code segment, offset within segment, and emulate code.
|
||
//
|
||
|
||
P->SegmentRegister[CS] = Segment;
|
||
P->Eip = Offset;
|
||
if ((Status = setjmp(&P->JumpBuffer[0])) == 0) {
|
||
|
||
//
|
||
// Emulate x86 instruction stream.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// Initialize instruction decode variables.
|
||
//
|
||
|
||
P->ComputeOffsetAddress = FALSE;
|
||
P->DataSegment = DS;
|
||
P->LockPrefixActive = FALSE;
|
||
P->OpaddrPrefixActive = FALSE;
|
||
P->OpsizePrefixActive = FALSE;
|
||
P->RepeatPrefixActive = FALSE;
|
||
P->SegmentPrefixActive = FALSE;
|
||
P->OpcodeControlTable = &XmOpcodeControlTable1[0];
|
||
|
||
#if defined(XM_DEBUG)
|
||
|
||
P->OpcodeNameTable = &XmOpcodeNameTable1[0];
|
||
|
||
#endif
|
||
|
||
//
|
||
// Get the next byte from the instruction stream and decode
|
||
// operands. If the byte is a prefix or an escape, then the
|
||
// next byte will be decoded. Decoding continues until an
|
||
// opcode byte is reached with a terminal decode condition.
|
||
//
|
||
// N.B. There is no checking for legitimate sequences of prefix
|
||
// and/or two byte opcode escapes. Redundant or invalid
|
||
// prefixes or two byte escape opcodes have no effect and
|
||
// are benign.
|
||
//
|
||
|
||
do {
|
||
P->CurrentOpcode = XmGetCodeByte(P);
|
||
|
||
#if defined(XM_DEBUG)
|
||
|
||
if ((XmDebugFlags & TRACE_INSTRUCTIONS) != 0) {
|
||
DEBUG_PRINT(("\n%04lx %s %02lx ",
|
||
P->Eip - 1,
|
||
P->OpcodeNameTable[P->CurrentOpcode],
|
||
(ULONG)P->CurrentOpcode));
|
||
}
|
||
|
||
#endif
|
||
|
||
P->OpcodeControl = P->OpcodeControlTable[P->CurrentOpcode];
|
||
P->FunctionIndex = P->OpcodeControl.FunctionIndex;
|
||
} while (XmOperandDecodeTable[P->OpcodeControl.FormatType](P) == FALSE);
|
||
|
||
//
|
||
// Emulate the instruction.
|
||
//
|
||
|
||
XmTraceFlags(P);
|
||
XmOpcodeFunctionTable[P->FunctionIndex](P);
|
||
XmTraceFlags(P);
|
||
XmTraceRegisters(P);
|
||
|
||
#if defined(XM_DEBUG)
|
||
|
||
if ((XmDebugFlags & TRACE_SINGLE_STEP) != 0) {
|
||
DEBUG_PRINT(("\n"));
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#endif
|
||
|
||
} while (TRUE);
|
||
}
|
||
|
||
//
|
||
// Set the x86 return context to the current emulator registers.
|
||
//
|
||
|
||
Context->Eax = P->Gpr[EAX].Exx;
|
||
Context->Ecx = P->Gpr[ECX].Exx;
|
||
Context->Edx = P->Gpr[EDX].Exx;
|
||
Context->Ebx = P->Gpr[EBX].Exx;
|
||
Context->Ebp = P->Gpr[EBP].Exx;
|
||
Context->Esi = P->Gpr[ESI].Exx;
|
||
Context->Edi = P->Gpr[EDI].Exx;
|
||
return Status;
|
||
}
|