diff options
Diffstat (limited to 'private/ntos/nthals/x86new/emulate.c')
-rw-r--r-- | private/ntos/nthals/x86new/emulate.c | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/private/ntos/nthals/x86new/emulate.c b/private/ntos/nthals/x86new/emulate.c new file mode 100644 index 000000000..2c840f19a --- /dev/null +++ b/private/ntos/nthals/x86new/emulate.c @@ -0,0 +1,410 @@ +/*++ + +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; + + // + // 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; +} |