mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
bitkeeper revision 1.1236.18.1 (422f27c9EveZXnXhkLBg8iYwaAffoQ)
Begin integrating new instruction decoder and emulator. Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
d9109bda54
commit
4c5eeec983
9 files changed
+1302
-78
No files matched your search
@@ -788,6 +788,8 @@
|
||||
40fcefb3yMSrZvApO9ToIi-iQwnchA tools/sv/images/xen.png
|
||||
41013a83z27rKvWIxAfUBMVZ1eDCDg tools/sv/inc/script.js
|
||||
40fcefb3zGC9XNBkSwTEobCoq8YClA tools/sv/inc/style.css
|
||||
422f27c8MDeRoOWZNdcRC5VDTcj3TQ tools/tests/Makefile
|
||||
422f27c81CCtXt4Lthf7JF3Ajr0fUA tools/tests/test_x86_emulator.c
|
||||
420b963dK3yGNtqxRM8npGZtrCQd1g tools/vnet/00INSTALL
|
||||
41a21888_WlknVWjSxb32Fo13_ujsw tools/vnet/00README
|
||||
420a9b706I-bN_uPdiy0m3rmDifNNg tools/vnet/INSTALL
|
||||
@@ -1017,6 +1019,7 @@
|
||||
42000d3cMb8o1WuFBXC07c8i3lPZBw xen/arch/x86/x86_64/traps.c
|
||||
40e96d3ahBTZqbTViInnq0lM03vs7A xen/arch/x86/x86_64/usercopy.c
|
||||
40e96d3akN3Hu_J5Bk-WXD8OGscrYQ xen/arch/x86/x86_64/xen.lds
|
||||
422f27c8J9DQfCpegccMid59XhSmGA xen/arch/x86/x86_emulate.c
|
||||
3ddb79bdff-gj-jFGKjOejeHLqL8Lg xen/common/Makefile
|
||||
3e397e66AyyD5fYraAySWuwi9uqSXg xen/common/ac_timer.c
|
||||
3ddb79bdLX_P6iB7ILiblRLWvebapg xen/common/dom0_ops.c
|
||||
@@ -1180,6 +1183,7 @@
|
||||
404f1bb86rAXB3aLS1vYdcqpJiEcyg xen/include/asm-x86/x86_64/regs.h
|
||||
40e1966azOJZfNI6Ilthe6Q-T3Hewg xen/include/asm-x86/x86_64/string.h
|
||||
404f1bc4tWkB9Qr8RkKtZGW5eMQzhw xen/include/asm-x86/x86_64/uaccess.h
|
||||
422f27c8RHFkePhD34VIEpMMqofZcA xen/include/asm-x86/x86_emulate.h
|
||||
400304fcmRQmDdFYEzDh0wcBba9alg xen/include/public/COPYING
|
||||
421098b7OKb9YH_EUA_UpCxBjaqtgA xen/include/public/arch-ia64.h
|
||||
404f1bc68SXxmv0zQpXBWGrCzSyp8w xen/include/public/arch-x86_32.h
|
||||
|
||||
+3
-29
@@ -121,32 +121,6 @@ tools/blktap/vdi_snap
|
||||
tools/blktap/vdi_snap_list
|
||||
tools/blktap/vdi_tree
|
||||
tools/blktap/vdi_validate
|
||||
tools/blktap/xen/arch-ia64.h
|
||||
tools/blktap/xen/arch-x86_32.h
|
||||
tools/blktap/xen/arch-x86_64.h
|
||||
tools/blktap/xen/dom0_ops.h
|
||||
tools/blktap/xen/event_channel.h
|
||||
tools/blktap/xen/grant_table.h
|
||||
tools/blktap/xen/io/blkif.h
|
||||
tools/blktap/xen/io/domain_controller.h
|
||||
tools/blktap/xen/io/ioreq.h
|
||||
tools/blktap/xen/io/netif.h
|
||||
tools/blktap/xen/io/ring.h
|
||||
tools/blktap/xen/io/usbif.h
|
||||
tools/blktap/xen/linux/privcmd.h
|
||||
tools/blktap/xen/linux/suspend.h
|
||||
tools/blktap/xen/physdev.h
|
||||
tools/blktap/xen/sched_ctl.h
|
||||
tools/blktap/xen/trace.h
|
||||
tools/blktap/xen/xen.h
|
||||
tools/cmdline/Makefile
|
||||
tools/cmdline/ringbuster
|
||||
tools/cmdline/ringbuster.c
|
||||
tools/cmdline/xdestroy
|
||||
tools/cmdline/xdestroy.c
|
||||
tools/cmdline/xevtchn
|
||||
tools/cmdline/xevtchn.c
|
||||
tools/cmdline/xls
|
||||
tools/cmdline/xls.c
|
||||
tools/cmdline/xphysinfo
|
||||
tools/cmdline/xphysinfo.c
|
||||
tools/blktap/xen/*
|
||||
tools/cmdline/*
|
||||
tools/tests/test_x86_emulator
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
XEN_ROOT=../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
TARGET := test_x86_emulator
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -O2 -Wall -Werror -D__TEST_HARNESS__
|
||||
|
||||
$(TARGET): x86_emulate.o test_x86_emulator.o
|
||||
$(CC) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -rf $(TARGET) *.o *~ core
|
||||
|
||||
install:
|
||||
|
||||
x86_emulate.o: $(XEN_ROOT)/xen/arch/x86/x86_emulate.c
|
||||
$(CC) $(CFLAGS) -I$(XEN_ROOT)/xen/include -c -o $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -I$(XEN_ROOT)/xen/include -c -o $@ $<
|
||||
@@ -0,0 +1,168 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
#include <public/xen.h>
|
||||
#include <asm-x86/x86_emulate.h>
|
||||
|
||||
static int read_any(
|
||||
unsigned long addr,
|
||||
unsigned long *val,
|
||||
unsigned int bytes)
|
||||
{
|
||||
switch ( bytes )
|
||||
{
|
||||
case 1: *val = *(u8 *)addr; break;
|
||||
case 2: *val = *(u16 *)addr; break;
|
||||
case 4: *val = *(u32 *)addr; break;
|
||||
case 8: *val = *(unsigned long *)addr; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_any(
|
||||
unsigned long addr,
|
||||
unsigned long val,
|
||||
unsigned int bytes)
|
||||
{
|
||||
switch ( bytes )
|
||||
{
|
||||
case 1: *(u8 *)addr = (u8)val; break;
|
||||
case 2: *(u16 *)addr = (u16)val; break;
|
||||
case 4: *(u32 *)addr = (u32)val; break;
|
||||
case 8: *(unsigned long *)addr = val; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmpxchg_any(
|
||||
unsigned long addr,
|
||||
unsigned long old,
|
||||
unsigned long new,
|
||||
unsigned long *seen,
|
||||
unsigned int bytes)
|
||||
{
|
||||
*seen = old;
|
||||
switch ( bytes )
|
||||
{
|
||||
case 1: *(u8 *)addr = (u8)new; break;
|
||||
case 2: *(u16 *)addr = (u16)new; break;
|
||||
case 4: *(u32 *)addr = (u32)new; break;
|
||||
case 8: *(unsigned long *)addr = new; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct x86_mem_emulator emulops = {
|
||||
read_any, write_any, read_any, write_any, cmpxchg_any
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct xen_regs regs;
|
||||
char instr[] = { 0x01, 0x08 }; /* add %ecx,(%eax) */
|
||||
unsigned int res = 0x7FFFFFFF;
|
||||
unsigned long cr2;
|
||||
int rc;
|
||||
|
||||
printf("%-40s", "Testing addl %%ecx,(%%eax)...");
|
||||
instr[0] = 0x01; instr[1] = 0x08;
|
||||
regs.eflags = 0x200;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
regs.ecx = 0x12345678;
|
||||
cr2 = (unsigned long)&res;
|
||||
res = 0x7FFFFFFF;
|
||||
rc = x86_emulate_memop(®s, cr2, &emulops, 4);
|
||||
if ( (rc != 0) ||
|
||||
(res != 0x92345677) ||
|
||||
(regs.eflags != 0xa94) ||
|
||||
(regs.eip != (unsigned long)&instr[2]) )
|
||||
goto fail;
|
||||
printf("okay\n");
|
||||
|
||||
printf("%-40s", "Testing xorl (%%eax),%%ecx...");
|
||||
instr[0] = 0x33; instr[1] = 0x08;
|
||||
regs.eflags = 0x200;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
#ifdef __x86_64__
|
||||
regs.ecx = 0xFFFFFFFF12345678UL;
|
||||
#else
|
||||
regs.ecx = 0x12345678UL;
|
||||
#endif
|
||||
cr2 = (unsigned long)&res;
|
||||
rc = x86_emulate_memop(®s, cr2, &emulops, 4);
|
||||
if ( (rc != 0) ||
|
||||
(res != 0x92345677) ||
|
||||
(regs.ecx != 0x8000000FUL) ||
|
||||
(regs.eip != (unsigned long)&instr[2]) )
|
||||
goto fail;
|
||||
printf("okay\n");
|
||||
|
||||
printf("%-40s", "Testing lock cmpxchgb %%cl,(%%eax)...");
|
||||
instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb0; instr[3] = 0x08;
|
||||
regs.eflags = 0x200;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
regs.eax = 0x92345677UL;
|
||||
regs.ecx = 0xAA;
|
||||
cr2 = (unsigned long)&res;
|
||||
rc = x86_emulate_memop(®s, cr2, &emulops, 4);
|
||||
if ( (rc != 0) ||
|
||||
(res != 0x923456AA) ||
|
||||
(regs.eflags != 0x244) ||
|
||||
(regs.eax != 0x92345677UL) ||
|
||||
(regs.eip != (unsigned long)&instr[4]) )
|
||||
goto fail;
|
||||
printf("okay\n");
|
||||
|
||||
printf("%-40s", "Testing lock cmpxchgl %%ecx,(%%eax)...");
|
||||
instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb1; instr[3] = 0x08;
|
||||
regs.eflags = 0x200;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
regs.eax = 0x923456AAUL;
|
||||
regs.ecx = 0xDDEEFF00L;
|
||||
cr2 = (unsigned long)&res;
|
||||
rc = x86_emulate_memop(®s, cr2, &emulops, 4);
|
||||
if ( (rc != 0) ||
|
||||
(res != 0xDDEEFF00) ||
|
||||
(regs.eflags != 0x244) ||
|
||||
(regs.eax != 0x923456AAUL) ||
|
||||
(regs.eip != (unsigned long)&instr[4]) )
|
||||
goto fail;
|
||||
printf("okay\n");
|
||||
|
||||
printf("%-40s", "Testing rep movsw...");
|
||||
instr[0] = 0xf3; instr[1] = 0x66; instr[2] = 0xa5;
|
||||
res = 0x22334455;
|
||||
regs.eflags = 0x200;
|
||||
regs.ecx = 23;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
regs.esi = (unsigned long)&res + 0;
|
||||
regs.edi = (unsigned long)&res + 2;
|
||||
regs.error_code = 0; /* read fault */
|
||||
cr2 = regs.esi;
|
||||
rc = x86_emulate_memop(®s, cr2, &emulops, 4);
|
||||
if ( (rc != 0) ||
|
||||
(res != 0x44554455) ||
|
||||
(regs.eflags != 0x200) ||
|
||||
(regs.ecx != 22) ||
|
||||
(regs.esi != ((unsigned long)&res + 2)) ||
|
||||
(regs.edi != ((unsigned long)&res + 4)) ||
|
||||
(regs.eip != (unsigned long)&instr[0]) )
|
||||
goto fail;
|
||||
printf("okay\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printf("failed!\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -52,6 +52,7 @@
|
||||
#include <asm/i387.h>
|
||||
#include <asm/debugger.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/x86_emulate.h>
|
||||
|
||||
/*
|
||||
* opt_nmi: one of 'ignore', 'dom0', or 'fatal'.
|
||||
@@ -369,8 +370,6 @@ long do_fpu_taskswitch(int set)
|
||||
|
||||
static int emulate_privileged_op(struct xen_regs *regs)
|
||||
{
|
||||
extern void *decode_reg(struct xen_regs *regs, u8 b);
|
||||
|
||||
struct exec_domain *ed = current;
|
||||
unsigned long *reg, eip = regs->eip;
|
||||
u8 opcode;
|
||||
@@ -405,7 +404,7 @@ static int emulate_privileged_op(struct xen_regs *regs)
|
||||
eip += 1;
|
||||
if ( (opcode & 0xc0) != 0xc0 )
|
||||
goto fail;
|
||||
reg = decode_reg(regs, opcode & 7);
|
||||
reg = decode_register(opcode & 7, regs, 0);
|
||||
switch ( (opcode >> 3) & 7 )
|
||||
{
|
||||
case 0: /* Read CR0 */
|
||||
@@ -433,7 +432,7 @@ static int emulate_privileged_op(struct xen_regs *regs)
|
||||
eip += 1;
|
||||
if ( (opcode & 0xc0) != 0xc0 )
|
||||
goto fail;
|
||||
reg = decode_reg(regs, opcode & 7);
|
||||
reg = decode_register(opcode & 7, regs, 0);
|
||||
switch ( (opcode >> 3) & 7 )
|
||||
{
|
||||
case 0: /* Write CR0 */
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <xen/mm.h>
|
||||
#include <xen/perfc.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/x86_emulate.h>
|
||||
|
||||
/* Make the scary benign errors go away. */
|
||||
#undef DPRINTK
|
||||
@@ -258,24 +259,6 @@ int fixup_seg(u16 seg, unsigned long offset)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Decode Reg field of a ModRM byte: return a pointer into a register block. */
|
||||
void *decode_reg(struct xen_regs *regs, u8 b)
|
||||
{
|
||||
switch ( b & 7 )
|
||||
{
|
||||
case 0: return ®s->eax;
|
||||
case 1: return ®s->ecx;
|
||||
case 2: return ®s->edx;
|
||||
case 3: return ®s->ebx;
|
||||
case 4: return ®s->esp;
|
||||
case 5: return ®s->ebp;
|
||||
case 6: return ®s->esi;
|
||||
case 7: return ®s->edi;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from the general-protection fault handler to attempt to decode
|
||||
* and emulate an instruction that depends on 4GB segments.
|
||||
@@ -402,8 +385,8 @@ int gpf_emulate_4gb(struct xen_regs *regs)
|
||||
}
|
||||
|
||||
/* Decode Reg and R/M fields. */
|
||||
regreg = decode_reg(regs, reg);
|
||||
memreg = decode_reg(regs, rm);
|
||||
regreg = decode_register(reg, regs, 0);
|
||||
memreg = decode_register(rm, regs, 0);
|
||||
|
||||
/* Decode Mod field. */
|
||||
switch ( modrm >> 6 )
|
||||
|
||||
@@ -248,31 +248,6 @@ void __init percpu_traps_init(void)
|
||||
wrmsr(MSR_SYSCALL_MASK, EF_VM|EF_RF|EF_NT|EF_DF|EF_IE|EF_TF, 0U);
|
||||
}
|
||||
|
||||
void *decode_reg(struct xen_regs *regs, u8 b)
|
||||
{
|
||||
switch ( b )
|
||||
{
|
||||
case 0: return ®s->rax;
|
||||
case 1: return ®s->rcx;
|
||||
case 2: return ®s->rdx;
|
||||
case 3: return ®s->rbx;
|
||||
case 4: return ®s->rsp;
|
||||
case 5: return ®s->rbp;
|
||||
case 6: return ®s->rsi;
|
||||
case 7: return ®s->rdi;
|
||||
case 8: return ®s->r8;
|
||||
case 9: return ®s->r9;
|
||||
case 10: return ®s->r10;
|
||||
case 11: return ®s->r11;
|
||||
case 12: return ®s->r12;
|
||||
case 13: return ®s->r13;
|
||||
case 14: return ®s->r14;
|
||||
case 15: return ®s->r15;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long do_set_callbacks(unsigned long event_address,
|
||||
unsigned long failsafe_address,
|
||||
unsigned long syscall_address)
|
||||
|
||||
@@ -0,0 +1,968 @@
|
||||
/******************************************************************************
|
||||
* x86_emulate.c
|
||||
*
|
||||
* Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
|
||||
*
|
||||
* Copyright (c) 2005 Keir Fraser
|
||||
*/
|
||||
|
||||
#ifdef __TEST_HARNESS__
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
#include <public/xen.h>
|
||||
#else
|
||||
#include <xen/config.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/lib.h>
|
||||
#include <asm/regs.h>
|
||||
#endif
|
||||
#include <asm-x86/x86_emulate.h>
|
||||
|
||||
/*
|
||||
* Opcode effective-address decode tables.
|
||||
* Note that we only emulate instructions that have at least one memory
|
||||
* operand (excluding implicit stack references). We assume that stack
|
||||
* references and instruction fetches will never occur in special memory
|
||||
* areas that require emulation. So, for example, 'mov <imm>,<reg>' need
|
||||
* not be handled.
|
||||
*/
|
||||
|
||||
/* Operand sizes: 8-bit operands or specified/overridden size. */
|
||||
#define ByteOp (1<<0) /* 8-bit operands. */
|
||||
/* Destination operand type. */
|
||||
#define ImplicitOps (1<<1) /* Implicit in opcode. No generic decode. */
|
||||
#define DstReg (2<<1) /* Register operand. */
|
||||
#define DstMem (3<<1) /* Memory operand. */
|
||||
#define DstMask (3<<1)
|
||||
/* Source operand type. */
|
||||
#define SrcNone (0<<3) /* No source operand. */
|
||||
#define SrcImplicit (0<<3) /* Source operand is implicit in the opcode. */
|
||||
#define SrcReg (1<<3) /* Register operand. */
|
||||
#define SrcMem (2<<3) /* Memory operand. */
|
||||
#define SrcImm (3<<3) /* Immediate operand. */
|
||||
#define SrcImmByte (4<<3) /* 8-bit sign-extended immediate operand. */
|
||||
#define SrcMask (7<<3)
|
||||
/* Generic ModRM decode. */
|
||||
#define ModRM (1<<6)
|
||||
/* Destination is only written; never read. */
|
||||
#define Mov (1<<7)
|
||||
|
||||
static u8 opcode_table[256] = {
|
||||
/* 0x00 - 0x07 */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x08 - 0x0F */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x10 - 0x17 */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x18 - 0x1F */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x20 - 0x27 */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x28 - 0x2F */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x30 - 0x37 */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x38 - 0x3F */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0x40 - 0x4F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x50 - 0x5F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x60 - 0x6F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x70 - 0x7F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x80 - 0x87 */
|
||||
ByteOp|DstMem|SrcImm|ModRM, DstMem|SrcImm|ModRM,
|
||||
ByteOp|DstMem|SrcImm|ModRM, DstMem|SrcImmByte|ModRM,
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
/* 0x88 - 0x8F */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM,
|
||||
ByteOp|DstReg|SrcMem|ModRM, DstReg|SrcMem|ModRM,
|
||||
0, 0, 0, DstMem|SrcNone|ModRM|Mov,
|
||||
/* 0x90 - 0x9F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xA0 - 0xA7 */
|
||||
ByteOp|DstReg|SrcMem|Mov, DstReg|SrcMem|Mov,
|
||||
ByteOp|DstMem|SrcReg|Mov, DstMem|SrcReg|Mov,
|
||||
ByteOp|ImplicitOps|Mov, ImplicitOps|Mov,
|
||||
ByteOp|ImplicitOps, ImplicitOps,
|
||||
/* 0xA8 - 0xAF */
|
||||
0, 0, ByteOp|ImplicitOps|Mov, ImplicitOps|Mov,
|
||||
ByteOp|ImplicitOps|Mov, ImplicitOps|Mov,
|
||||
ByteOp|ImplicitOps, ImplicitOps,
|
||||
/* 0xB0 - 0xBF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xC0 - 0xC7 */
|
||||
ByteOp|DstMem|SrcImm|ModRM, DstMem|SrcImmByte|ModRM, 0, 0,
|
||||
0, 0, ByteOp|DstMem|SrcImm|ModRM, DstMem|SrcImm|ModRM,
|
||||
/* 0xC8 - 0xCF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xD0 - 0xD7 */
|
||||
ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM,
|
||||
ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0xD8 - 0xDF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xE0 - 0xEF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xF0 - 0xF7 */
|
||||
0, 0, 0, 0,
|
||||
0, 0, ByteOp|DstMem|SrcNone|ModRM, DstMem|SrcNone|ModRM,
|
||||
/* 0xF8 - 0xFF */
|
||||
0, 0, 0, 0,
|
||||
0, 0, ByteOp|DstMem|SrcNone|ModRM, DstMem|SrcNone|ModRM
|
||||
};
|
||||
|
||||
static u8 twobyte_table[256] = {
|
||||
/* 0x00 - 0x0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ImplicitOps|ModRM, 0, 0,
|
||||
/* 0x10 - 0x1F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, ImplicitOps|ModRM, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x20 - 0x2F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x30 - 0x3F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x40 - 0x47 */
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
/* 0x48 - 0x4F */
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem|ModRM|Mov,
|
||||
/* 0x50 - 0x5F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x60 - 0x6F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x70 - 0x7F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x80 - 0x8F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x90 - 0x9F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xA0 - 0xA7 */
|
||||
0, 0, 0, DstMem|SrcReg|ModRM, 0, 0, 0, 0,
|
||||
/* 0xA8 - 0xAF */
|
||||
0, 0, 0, DstMem|SrcReg|ModRM, 0, 0, 0, 0,
|
||||
/* 0xB0 - 0xB7 */
|
||||
ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, 0, DstMem|SrcReg|ModRM,
|
||||
0, 0, 0, 0,
|
||||
/* 0xB8 - 0xBF */
|
||||
0, 0, DstMem|SrcImmByte|ModRM, DstMem|SrcReg|ModRM, 0, 0, 0, 0,
|
||||
/* 0xC0 - 0xCF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xD0 - 0xDF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xE0 - 0xEF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xF0 - 0xFF */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/* Type, address-of, and value of an instruction's operand. */
|
||||
struct operand {
|
||||
enum { OP_REG, OP_MEM, OP_IMM } type;
|
||||
unsigned int bytes;
|
||||
unsigned long val, orig_val, *ptr;
|
||||
};
|
||||
|
||||
/* EFLAGS bit definitions. */
|
||||
#define EFLG_OF (1<<11)
|
||||
#define EFLG_DF (1<<10)
|
||||
#define EFLG_SF (1<<7)
|
||||
#define EFLG_ZF (1<<6)
|
||||
#define EFLG_AF (1<<4)
|
||||
#define EFLG_PF (1<<2)
|
||||
#define EFLG_CF (1<<0)
|
||||
|
||||
/*
|
||||
* Instruction emulation:
|
||||
* Most instructions are emulated directly via a fragment of inline assembly
|
||||
* code. This allows us to save/restore EFLAGS and thus very easily pick up
|
||||
* any modified flags.
|
||||
*/
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define _LO32 "k" /* force 32-bit operand */
|
||||
#define _STK "%%rsp" /* stack pointer */
|
||||
#elif defined(__i386__)
|
||||
#define _LO32 "" /* force 32-bit operand */
|
||||
#define _STK "%%esp" /* stack pointer */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These EFLAGS bits are restored from saved value during emulation, and
|
||||
* any changes are written back to the saved value after emulation.
|
||||
*/
|
||||
#define EFLAGS_MASK (EFLG_OF|EFLG_SF|EFLG_ZF|EFLG_AF|EFLG_PF|EFLG_CF)
|
||||
|
||||
/* Before executing instruction: restore necessary bits in EFLAGS. */
|
||||
/* EFLAGS = (_sav & _msk) | (EFLAGS & ~_msk); _sav &= ~msk; */
|
||||
#define _PRE_EFLAGS(_sav, _msk, _tmp) \
|
||||
"push %"_sav"; " \
|
||||
"movl %"_msk",%"_LO32 _tmp"; " \
|
||||
"andl %"_LO32 _tmp",("_STK"); " \
|
||||
"notl %"_LO32 _tmp"; " \
|
||||
"andl %"_LO32 _tmp",%"_sav"; " \
|
||||
"pushf; " \
|
||||
"andl %"_LO32 _tmp",("_STK"); " \
|
||||
"pop %"_tmp"; " \
|
||||
"orl %"_LO32 _tmp",("_STK"); " \
|
||||
"popf; "
|
||||
|
||||
/* After executing instruction: write-back necessary bits in EFLAGS. */
|
||||
/* _sav |= EFLAGS & _msk; */
|
||||
#define _POST_EFLAGS(_sav, _msk, _tmp) \
|
||||
"pushf; " \
|
||||
"pop %"_tmp"; " \
|
||||
"andl %"_msk",%"_LO32 _tmp"; " \
|
||||
"orl %"_LO32 _tmp",%"_sav"; "
|
||||
|
||||
/* Raw emulation: instruction has two explicit operands. */
|
||||
#define __emulate_2op_nobyte(_op,_src,_dst,_eflags,_wx,_wy,_lx,_ly,_qx,_qy)\
|
||||
do{ unsigned long _tmp; \
|
||||
switch ( (_dst).bytes ) \
|
||||
{ \
|
||||
case 2: \
|
||||
__asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","4","2") \
|
||||
_op"w %"_wx"3,%1; " \
|
||||
_POST_EFLAGS("0","4","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: _wy ((_src).val), "i" (EFLAGS_MASK) ); \
|
||||
break; \
|
||||
case 4: \
|
||||
__asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","4","2") \
|
||||
_op"l %"_lx"3,%1; " \
|
||||
_POST_EFLAGS("0","4","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: _ly ((_src).val), "i" (EFLAGS_MASK) ); \
|
||||
break; \
|
||||
case 8: \
|
||||
__emulate_2op_8byte(_op, _src, _dst, _eflags, _qx, _qy); \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
#define __emulate_2op(_op,_src,_dst,_eflags,_bx,_by,_wx,_wy,_lx,_ly,_qx,_qy)\
|
||||
do{ unsigned long _tmp; \
|
||||
switch ( (_dst).bytes ) \
|
||||
{ \
|
||||
case 1: \
|
||||
__asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","4","2") \
|
||||
_op"b %"_bx"3,%1; " \
|
||||
_POST_EFLAGS("0","4","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: _by ((_src).val), "i" (EFLAGS_MASK) ); \
|
||||
break; \
|
||||
default: \
|
||||
__emulate_2op_nobyte(_op,_src,_dst,_eflags,_wx,_wy,_lx,_ly,_qx,_qy);\
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
/* Source operand is byte-sized and may be restricted to just %cl. */
|
||||
#define emulate_2op_SrcB(_op, _src, _dst, _eflags) \
|
||||
__emulate_2op(_op, _src, _dst, _eflags, \
|
||||
"b", "c", "b", "c", "b", "c", "b", "c")
|
||||
/* Source operand is byte, word, long or quad sized. */
|
||||
#define emulate_2op_SrcV(_op, _src, _dst, _eflags) \
|
||||
__emulate_2op(_op, _src, _dst, _eflags, \
|
||||
"b", "q", "w", "r", _LO32, "r", "", "r")
|
||||
/* Source operand is word, long or quad sized. */
|
||||
#define emulate_2op_SrcV_nobyte(_op, _src, _dst, _eflags) \
|
||||
__emulate_2op_nobyte(_op, _src, _dst, _eflags, \
|
||||
"w", "r", _LO32, "r", "", "r")
|
||||
|
||||
/* Instruction has only one explicit operand (no source operand). */
|
||||
#define emulate_1op(_op,_dst,_eflags) \
|
||||
do{ unsigned long _tmp; \
|
||||
switch ( (_dst).bytes ) \
|
||||
{ \
|
||||
case 1: \
|
||||
__asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","3","2") \
|
||||
_op"b %1; " \
|
||||
_POST_EFLAGS("0","3","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: "i" (EFLAGS_MASK) ); \
|
||||
break; \
|
||||
case 2: \
|
||||
__asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","3","2") \
|
||||
_op"w %1; " \
|
||||
_POST_EFLAGS("0","3","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: "i" (EFLAGS_MASK) ); \
|
||||
break; \
|
||||
case 4: \
|
||||
__asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","3","2") \
|
||||
_op"l %1; " \
|
||||
_POST_EFLAGS("0","3","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: "i" (EFLAGS_MASK) ); \
|
||||
break; \
|
||||
case 8: \
|
||||
__emulate_1op_8byte(_op, _dst, _eflags); \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Emulate an instruction with quadword operands (x86/64 only). */
|
||||
#if defined(__x86_64__)
|
||||
#define __emulate_2op_8byte(_op, _src, _dst, _eflags, _qx, _qy) \
|
||||
do{ __asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","4","2") \
|
||||
_op"q %"_qx"3,%1; " \
|
||||
_POST_EFLAGS("0","4","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: _qy ((_src).val), "i" (EFLAGS_MASK) ); \
|
||||
} while (0)
|
||||
#define __emulate_1op_8byte(_op, _dst, _eflags) \
|
||||
do{ __asm__ __volatile__ ( \
|
||||
_PRE_EFLAGS("0","3","2") \
|
||||
_op"q %1; " \
|
||||
_POST_EFLAGS("0","3","2") \
|
||||
: "=m" (_eflags), "=m" ((_dst).val), "=&r" (_tmp) \
|
||||
: "i" (EFLAGS_MASK) ); \
|
||||
} while (0)
|
||||
#elif defined(__i386__)
|
||||
#define __emulate_2op_8byte(_op, _src, _dst, _eflags, _qx, _qy)
|
||||
#define __emulate_1op_8byte(_op, _dst, _eflags)
|
||||
#endif /* __i386__ */
|
||||
|
||||
/* Fetch next part of the instruction being emulated. */
|
||||
#define insn_fetch(_type, _size, _eip) \
|
||||
({ unsigned long _x; \
|
||||
if ( ops->read_std((unsigned long)(_eip), &_x, (_size)) ) \
|
||||
goto done; \
|
||||
(_eip) += (_size); \
|
||||
(_type)_x; \
|
||||
})
|
||||
|
||||
#define DPRINTF(_f, _a...) printf( _f , ## _a )
|
||||
|
||||
void *
|
||||
decode_register(
|
||||
u8 modrm_reg, struct xen_regs *regs, int highbyte_regs)
|
||||
{
|
||||
void *p;
|
||||
|
||||
switch ( modrm_reg )
|
||||
{
|
||||
case 0: p = ®s->eax; break;
|
||||
case 1: p = ®s->ecx; break;
|
||||
case 2: p = ®s->edx; break;
|
||||
case 3: p = ®s->ebx; break;
|
||||
case 4: p = (highbyte_regs ?
|
||||
((unsigned char *)®s->eax + 1) :
|
||||
(unsigned char *)®s->esp); break;
|
||||
case 5: p = (highbyte_regs ?
|
||||
((unsigned char *)®s->ecx + 1) :
|
||||
(unsigned char *)®s->ebp); break;
|
||||
case 6: p = (highbyte_regs ?
|
||||
((unsigned char *)®s->edx + 1) :
|
||||
(unsigned char *)®s->esi); break;
|
||||
case 7: p = (highbyte_regs ?
|
||||
((unsigned char *)®s->ebx + 1) :
|
||||
(unsigned char *)®s->edi); break;
|
||||
#if defined(__x86_64__)
|
||||
case 8: p = ®s->r8; break;
|
||||
case 9: p = ®s->r9; break;
|
||||
case 10: p = ®s->r10; break;
|
||||
case 11: p = ®s->r11; break;
|
||||
case 12: p = ®s->r12; break;
|
||||
case 13: p = ®s->r13; break;
|
||||
case 14: p = ®s->r14; break;
|
||||
case 15: p = ®s->r15; break;
|
||||
#endif
|
||||
default: p = NULL; break;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
x86_emulate_memop(
|
||||
struct xen_regs *regs,
|
||||
unsigned long cr2,
|
||||
struct x86_mem_emulator *ops,
|
||||
int mode)
|
||||
{
|
||||
u8 b, d, sib, twobyte = 0, rex_prefix = 0;
|
||||
u8 modrm, modrm_mod = 0, modrm_reg = 0, modrm_rm = 0;
|
||||
unsigned int op_bytes = (mode == 8) ? 4 : mode, ad_bytes = mode;
|
||||
unsigned int lock_prefix = 0, rep_prefix = 0;
|
||||
struct operand src, dst;
|
||||
|
||||
/* Shadow copy of register state. Committed on successful emulation. */
|
||||
struct xen_regs _regs = *regs;
|
||||
|
||||
/* Legacy prefixes. */
|
||||
next_prefix:
|
||||
switch ( b = insn_fetch(u8, 1, _regs.eip) )
|
||||
{
|
||||
case 0x66: /* operand-size override */
|
||||
op_bytes ^= 6; /* switch between 2/4 bytes */
|
||||
goto next_prefix;
|
||||
case 0x67: /* address-size override */
|
||||
ad_bytes ^= (mode == 8) ? 12 : 6; /* switch between 2/4 or 4/8 bytes */
|
||||
goto next_prefix;
|
||||
case 0x2e: /* CS override */
|
||||
case 0x3e: /* DS override */
|
||||
case 0x26: /* ES override */
|
||||
case 0x64: /* FS override */
|
||||
case 0x65: /* GS override */
|
||||
case 0x36: /* SS override */
|
||||
DPRINTF("Warning: ignoring a segment override. Probably okay. :-)\n");
|
||||
goto next_prefix;
|
||||
case 0xf0: /* LOCK */
|
||||
lock_prefix = 1;
|
||||
goto next_prefix;
|
||||
case 0xf3: /* REP/REPE/REPZ */
|
||||
rep_prefix = 1;
|
||||
goto next_prefix;
|
||||
case 0xf2: /* REPNE/REPNZ */
|
||||
goto next_prefix;
|
||||
}
|
||||
|
||||
if ( ad_bytes == 2 )
|
||||
{
|
||||
DPRINTF("Cannot parse 16-bit effective addresses.\n");
|
||||
goto cannot_emulate;
|
||||
}
|
||||
|
||||
/* REX prefix. */
|
||||
if ( (mode == 8) && ((b & 0xf0) == 0x40) )
|
||||
{
|
||||
rex_prefix = b;
|
||||
if ( b & 8 )
|
||||
op_bytes = 8; /* REX.W */
|
||||
modrm_reg = (b & 4) << 1; /* REX.R */
|
||||
/* REX.B and REX.X do not need to be decoded. */
|
||||
b = insn_fetch(u8, 1, _regs.eip);
|
||||
}
|
||||
|
||||
/* Opcode byte(s). */
|
||||
d = opcode_table[b];
|
||||
if ( d == 0 )
|
||||
{
|
||||
/* Two-byte opcode? */
|
||||
if ( b == 0x0f )
|
||||
{
|
||||
twobyte = 1;
|
||||
b = insn_fetch(u8, 1, _regs.eip);
|
||||
d = twobyte_table[b];
|
||||
}
|
||||
|
||||
/* Unrecognised? */
|
||||
if ( d == 0 )
|
||||
goto cannot_emulate;
|
||||
}
|
||||
|
||||
/* ModRM and SIB bytes. */
|
||||
if ( d & ModRM )
|
||||
{
|
||||
modrm = insn_fetch(u8, 1, _regs.eip);
|
||||
modrm_mod |= (modrm & 0xc0) >> 6;
|
||||
modrm_reg |= (modrm & 0x38) >> 3;
|
||||
modrm_rm |= (modrm & 0x07);
|
||||
switch ( modrm_mod )
|
||||
{
|
||||
case 0:
|
||||
if ( (modrm_rm == 4) &&
|
||||
(((sib = insn_fetch(u8, 1, _regs.eip)) & 7) == 5) )
|
||||
_regs.eip += 4; /* skip disp32 specified by SIB.base */
|
||||
else if ( modrm_rm == 5 )
|
||||
_regs.eip += 4; /* skip disp32 */
|
||||
break;
|
||||
case 1:
|
||||
if ( modrm_rm == 4 )
|
||||
sib = insn_fetch(u8, 1, _regs.eip);
|
||||
_regs.eip += 1; /* skip disp8 */
|
||||
break;
|
||||
case 2:
|
||||
if ( modrm_rm == 4 )
|
||||
sib = insn_fetch(u8, 1, _regs.eip);
|
||||
_regs.eip += 4; /* skip disp32 */
|
||||
break;
|
||||
case 3:
|
||||
DPRINTF("Cannot parse ModRM.mod == 3.\n");
|
||||
goto cannot_emulate;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode and fetch the destination operand: register or memory. */
|
||||
switch ( d & DstMask )
|
||||
{
|
||||
case ImplicitOps:
|
||||
/* Special instructions do their own operand decoding. */
|
||||
goto special_insn;
|
||||
case DstReg:
|
||||
dst.type = OP_REG;
|
||||
if ( d & ByteOp )
|
||||
{
|
||||
dst.ptr = decode_register(modrm_reg, &_regs, (rex_prefix == 0));
|
||||
dst.val = *(u8 *)dst.ptr;
|
||||
dst.bytes = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.ptr = decode_register(modrm_reg, &_regs, 0);
|
||||
switch ( (dst.bytes = op_bytes) )
|
||||
{
|
||||
case 2: dst.val = *(u16 *)dst.ptr; break;
|
||||
case 4: dst.val = *(u32 *)dst.ptr; break;
|
||||
case 8: dst.val = *(u64 *)dst.ptr; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DstMem:
|
||||
dst.type = OP_MEM;
|
||||
dst.ptr = (unsigned long *)cr2;
|
||||
dst.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
if ( !(d & Mov) && /* optimisation - avoid slow emulated read */
|
||||
ops->read_emulated((unsigned long)dst.ptr, &dst.val, dst.bytes) )
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
dst.orig_val = dst.val;
|
||||
|
||||
/* Decode and fetch the source operand: register, memory or immediate. */
|
||||
switch ( d & SrcMask )
|
||||
{
|
||||
case SrcNone:
|
||||
break;
|
||||
case SrcReg:
|
||||
src.type = OP_REG;
|
||||
if ( d & ByteOp )
|
||||
{
|
||||
src.ptr = decode_register(modrm_reg, &_regs, (rex_prefix == 0));
|
||||
src.val = src.orig_val = *(u8 *)src.ptr;
|
||||
src.bytes = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
src.ptr = decode_register(modrm_reg, &_regs, 0);
|
||||
switch ( (src.bytes = op_bytes) )
|
||||
{
|
||||
case 2: src.val = src.orig_val = *(u16 *)src.ptr; break;
|
||||
case 4: src.val = src.orig_val = *(u32 *)src.ptr; break;
|
||||
case 8: src.val = src.orig_val = *(u64 *)src.ptr; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SrcMem:
|
||||
src.type = OP_MEM;
|
||||
src.ptr = (unsigned long *)cr2;
|
||||
src.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
if ( ops->read_emulated((unsigned long)src.ptr, &src.val, src.bytes) )
|
||||
goto done;
|
||||
src.orig_val = src.val;
|
||||
break;
|
||||
case SrcImm:
|
||||
src.type = OP_IMM;
|
||||
src.ptr = (unsigned long *)_regs.eip;
|
||||
src.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
if ( src.bytes == 8 ) src.bytes = 4;
|
||||
/* NB. Immediates are sign-extended as necessary. */
|
||||
switch ( src.bytes )
|
||||
{
|
||||
case 1: src.val = insn_fetch(s8, 1, _regs.eip); break;
|
||||
case 2: src.val = insn_fetch(s16, 2, _regs.eip); break;
|
||||
case 4: src.val = insn_fetch(s32, 4, _regs.eip); break;
|
||||
}
|
||||
break;
|
||||
case SrcImmByte:
|
||||
src.type = OP_IMM;
|
||||
src.ptr = (unsigned long *)_regs.eip;
|
||||
src.bytes = 1;
|
||||
src.val = insn_fetch(s8, 1, _regs.eip);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( twobyte )
|
||||
goto twobyte_insn;
|
||||
|
||||
switch ( b )
|
||||
{
|
||||
case 0x00 ... 0x05: add: /* add */
|
||||
emulate_2op_SrcV("add", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x08 ... 0x0d: or: /* or */
|
||||
emulate_2op_SrcV("or", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x10 ... 0x15: adc: /* adc */
|
||||
emulate_2op_SrcV("adc", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x18 ... 0x1d: sbb: /* sbb */
|
||||
emulate_2op_SrcV("sbb", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x20 ... 0x25: and: /* and */
|
||||
emulate_2op_SrcV("and", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x28 ... 0x2d: sub: /* sub */
|
||||
emulate_2op_SrcV("sub", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x30 ... 0x35: xor: /* xor */
|
||||
emulate_2op_SrcV("xor", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x38 ... 0x3d: cmp: /* cmp */
|
||||
emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x80 ... 0x83: /* Grp1 */
|
||||
switch ( modrm_reg )
|
||||
{
|
||||
case 0: goto add;
|
||||
case 1: goto or;
|
||||
case 2: goto adc;
|
||||
case 3: goto sbb;
|
||||
case 4: goto and;
|
||||
case 5: goto sub;
|
||||
case 6: goto xor;
|
||||
case 7: goto cmp;
|
||||
}
|
||||
break;
|
||||
case 0x84 ... 0x85: test: /* test */
|
||||
emulate_2op_SrcV("test", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0x86 ... 0x87: /* xchg */
|
||||
src.val ^= dst.val;
|
||||
dst.val ^= src.val;
|
||||
src.val ^= dst.val;
|
||||
break;
|
||||
case 0xa0 ... 0xa1: /* mov */
|
||||
dst.ptr = (unsigned long *)&_regs.eax;
|
||||
dst.val = src.val;
|
||||
_regs.eip += ad_bytes; /* skip src displacement */
|
||||
break;
|
||||
case 0xa2 ... 0xa3: /* mov */
|
||||
dst.val = (unsigned long)_regs.eax;
|
||||
_regs.eip += ad_bytes; /* skip dst displacement */
|
||||
break;
|
||||
case 0x88 ... 0x8b: /* mov */
|
||||
case 0xc6 ... 0xc7: /* mov (sole member of Grp11) */
|
||||
dst.val = src.val;
|
||||
break;
|
||||
case 0x8f: /* pop (sole member of Grp1a) */
|
||||
/* 64-bit mode: POP defaults to 64-bit operands. */
|
||||
if ( (mode == 8) && (dst.bytes == 4) )
|
||||
dst.bytes = 8;
|
||||
if ( ops->read_std(_regs.esp, &dst.val, dst.bytes) )
|
||||
goto done;
|
||||
_regs.esp += dst.bytes;
|
||||
break;
|
||||
case 0xc0 ... 0xc1: grp2: /* Grp2 */
|
||||
switch ( modrm_reg )
|
||||
{
|
||||
case 0: /* rol */
|
||||
emulate_2op_SrcB("rol", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 1: /* ror */
|
||||
emulate_2op_SrcB("ror", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 2: /* rcl */
|
||||
emulate_2op_SrcB("rcl", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 3: /* rcr */
|
||||
emulate_2op_SrcB("rcr", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 4: /* sal/shl */
|
||||
case 6: /* sal/shl */
|
||||
emulate_2op_SrcB("sal", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 5: /* shr */
|
||||
emulate_2op_SrcB("shr", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 7: /* sar */
|
||||
emulate_2op_SrcB("sar", src, dst, _regs.eflags);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xd0 ... 0xd1: /* Grp2 */
|
||||
src.val = 1;
|
||||
goto grp2;
|
||||
case 0xd2 ... 0xd3: /* Grp2 */
|
||||
src.val = _regs.ecx;
|
||||
goto grp2;
|
||||
case 0xf6 ... 0xf7: /* Grp3 */
|
||||
switch ( modrm_reg )
|
||||
{
|
||||
case 0 ... 1: /* test */
|
||||
/* Special case in Grp3: test has an immediate source operand. */
|
||||
src.type = OP_IMM;
|
||||
src.ptr = (unsigned long *)_regs.eip;
|
||||
src.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
if ( src.bytes == 8 ) src.bytes = 4;
|
||||
switch ( src.bytes )
|
||||
{
|
||||
case 1: src.val = insn_fetch(s8, 1, _regs.eip); break;
|
||||
case 2: src.val = insn_fetch(s16, 2, _regs.eip); break;
|
||||
case 4: src.val = insn_fetch(s32, 4, _regs.eip); break;
|
||||
}
|
||||
goto test;
|
||||
case 2: /* not */
|
||||
dst.val = ~dst.val;
|
||||
break;
|
||||
case 3: /* neg */
|
||||
emulate_1op("neg", dst, _regs.eflags);
|
||||
break;
|
||||
default:
|
||||
goto cannot_emulate;
|
||||
}
|
||||
break;
|
||||
case 0xfe ... 0xff: /* Grp4/Grp5 */
|
||||
switch ( modrm_reg )
|
||||
{
|
||||
case 0: /* inc */
|
||||
emulate_1op("inc", dst, _regs.eflags);
|
||||
break;
|
||||
case 1: /* dec */
|
||||
emulate_1op("dec", dst, _regs.eflags);
|
||||
break;
|
||||
case 6: /* push */
|
||||
/* 64-bit mode: PUSH defaults to 64-bit operands. */
|
||||
if ( (mode == 8) && (dst.bytes == 4) )
|
||||
{
|
||||
dst.bytes = 8;
|
||||
if ( ops->read_std((unsigned long)dst.ptr, &dst.val, 8) )
|
||||
goto done;
|
||||
}
|
||||
_regs.esp -= dst.bytes;
|
||||
if ( ops->write_std(_regs.esp, dst.val, dst.bytes) )
|
||||
goto done;
|
||||
dst.val = dst.orig_val; /* skanky: disable writeback */
|
||||
break;
|
||||
default:
|
||||
goto cannot_emulate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
writeback:
|
||||
if ( (d & Mov) || (dst.orig_val != dst.val) )
|
||||
{
|
||||
switch ( dst.type )
|
||||
{
|
||||
case OP_REG:
|
||||
/* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */
|
||||
switch ( dst.bytes )
|
||||
{
|
||||
case 1: *(u8 *)dst.ptr = (u8)dst.val; break;
|
||||
case 2: *(u16 *)dst.ptr = (u16)dst.val; break;
|
||||
case 4: *dst.ptr = (u32)dst.val; break; /* 64b mode: zero-extend */
|
||||
case 8: *dst.ptr = dst.val; break;
|
||||
}
|
||||
break;
|
||||
case OP_MEM:
|
||||
if ( lock_prefix )
|
||||
{
|
||||
unsigned long seen;
|
||||
if ( ops->cmpxchg_emulated((unsigned long)dst.ptr,
|
||||
dst.orig_val, dst.val,
|
||||
&seen, dst.bytes) )
|
||||
goto done;
|
||||
if ( seen != dst.orig_val )
|
||||
goto done; /* Try again... */
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ops->write_emulated((unsigned long)dst.ptr,
|
||||
dst.val, dst.bytes) )
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Commit shadow register state. */
|
||||
*regs = _regs;
|
||||
|
||||
done:
|
||||
return 0;
|
||||
|
||||
special_insn:
|
||||
if ( twobyte )
|
||||
goto twobyte_special_insn;
|
||||
if ( rep_prefix )
|
||||
{
|
||||
if ( _regs.ecx == 0 )
|
||||
{
|
||||
regs->eip = _regs.eip;
|
||||
goto done;
|
||||
}
|
||||
_regs.ecx--;
|
||||
_regs.eip = regs->eip;
|
||||
}
|
||||
switch ( b )
|
||||
{
|
||||
case 0xa4 ... 0xa5: /* movs */
|
||||
dst.type = OP_MEM;
|
||||
dst.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
if ( _regs.error_code & 2 )
|
||||
{
|
||||
/* Write fault: destination is special memory. */
|
||||
dst.ptr = (unsigned long *)cr2;
|
||||
if ( ops->read_std(_regs.esi - _regs.edi + cr2,
|
||||
&dst.val, dst.bytes) )
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read fault: source is special memory. */
|
||||
dst.ptr = (unsigned long *)(_regs.edi - _regs.esi + cr2);
|
||||
if ( ops->read_emulated(cr2, &dst.val, dst.bytes) )
|
||||
goto done;
|
||||
}
|
||||
_regs.esi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
|
||||
_regs.edi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
|
||||
break;
|
||||
case 0xa6 ... 0xa7: /* cmps */
|
||||
DPRINTF("Urk! I don't handle CMPS.\n");
|
||||
goto cannot_emulate;
|
||||
case 0xaa ... 0xab: /* stos */
|
||||
dst.type = OP_MEM;
|
||||
dst.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
dst.ptr = (unsigned long *)cr2;
|
||||
dst.val = _regs.eax;
|
||||
_regs.edi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
|
||||
break;
|
||||
case 0xac ... 0xad: /* lods */
|
||||
dst.type = OP_REG;
|
||||
dst.bytes = (d & ByteOp) ? 1 : op_bytes;
|
||||
dst.ptr = (unsigned long *)&_regs.eax;
|
||||
if ( ops->read_emulated(cr2, &dst.val, dst.bytes) )
|
||||
goto done;
|
||||
_regs.esi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes;
|
||||
break;
|
||||
case 0xae ... 0xaf: /* scas */
|
||||
DPRINTF("Urk! I don't handle SCAS.\n");
|
||||
goto cannot_emulate;
|
||||
}
|
||||
goto writeback;
|
||||
|
||||
twobyte_insn:
|
||||
switch ( b )
|
||||
{
|
||||
case 0x40 ... 0x4f: /* cmov */
|
||||
dst.val = dst.orig_val = src.val;
|
||||
d &= ~Mov; /* default to no move */
|
||||
/* First, assume we're decoding an even cmov opcode (lsb == 0). */
|
||||
switch ( (b & 15) >> 1 )
|
||||
{
|
||||
case 0: /* cmovo */
|
||||
d |= (_regs.eflags & EFLG_OF) ? Mov : 0;
|
||||
break;
|
||||
case 1: /* cmovb/cmovc/cmovnae */
|
||||
d |= (_regs.eflags & EFLG_CF) ? Mov : 0;
|
||||
break;
|
||||
case 2: /* cmovz/cmove */
|
||||
d |= (_regs.eflags & EFLG_ZF) ? Mov : 0;
|
||||
break;
|
||||
case 3: /* cmovbe/cmovna */
|
||||
d |= (_regs.eflags & (EFLG_CF|EFLG_ZF)) ? Mov : 0;
|
||||
break;
|
||||
case 4: /* cmovs */
|
||||
d |= (_regs.eflags & EFLG_SF) ? Mov : 0;
|
||||
break;
|
||||
case 5: /* cmovp/cmovpe */
|
||||
d |= (_regs.eflags & EFLG_PF) ? Mov : 0;
|
||||
break;
|
||||
case 7: /* cmovle/cmovng */
|
||||
d |= (_regs.eflags & EFLG_ZF) ? Mov : 0;
|
||||
/* fall through */
|
||||
case 6: /* cmovl/cmovnge */
|
||||
d |= (!(_regs.eflags & EFLG_SF) != !(_regs.eflags & EFLG_OF)) ?
|
||||
Mov : 0;
|
||||
break;
|
||||
}
|
||||
/* Odd cmov opcodes (lsb == 1) have inverted sense. */
|
||||
d ^= (b & 1) ? Mov : 0;
|
||||
break;
|
||||
case 0xb0 ... 0xb1: /* cmpxchg */
|
||||
/* Save real source value, then compare EAX against destination. */
|
||||
src.orig_val = src.val;
|
||||
src.val = _regs.eax;
|
||||
emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
|
||||
/* Always write back. The question is: where to? */
|
||||
d |= Mov;
|
||||
if ( _regs.eflags & EFLG_ZF )
|
||||
{
|
||||
/* Success: write back to memory. */
|
||||
dst.val = src.orig_val;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Failure: write the value we saw to EAX. */
|
||||
dst.type = OP_REG;
|
||||
dst.ptr = (unsigned long *)&_regs.eax;
|
||||
}
|
||||
break;
|
||||
case 0xa3: bt: /* bt */
|
||||
src.val &= (1UL << (1 << dst.bytes)) - 1; /* only subword offset */
|
||||
emulate_2op_SrcV_nobyte("bt", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0xb3: btr: /* btr */
|
||||
src.val &= (1UL << (1 << dst.bytes)) - 1; /* only subword offset */
|
||||
emulate_2op_SrcV_nobyte("btr", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0xab: bts: /* bts */
|
||||
src.val &= (1UL << (1 << dst.bytes)) - 1; /* only subword offset */
|
||||
emulate_2op_SrcV_nobyte("bts", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0xbb: btc: /* btc */
|
||||
src.val &= (1UL << (1 << dst.bytes)) - 1; /* only subword offset */
|
||||
emulate_2op_SrcV_nobyte("btc", src, dst, _regs.eflags);
|
||||
break;
|
||||
case 0xba: /* Grp8 */
|
||||
switch ( modrm_reg >> 1 )
|
||||
{
|
||||
case 0: goto bt;
|
||||
case 1: goto bts;
|
||||
case 2: goto btr;
|
||||
case 3: goto btc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
goto writeback;
|
||||
|
||||
twobyte_special_insn:
|
||||
/* Only prefetch instructions get here, so nothing to do. */
|
||||
dst.orig_val = dst.val; /* disable writeback */
|
||||
goto writeback;
|
||||
|
||||
cannot_emulate:
|
||||
DPRINTF("Cannot emulate %02x\n", b);
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/******************************************************************************
|
||||
* x86_emulate.h
|
||||
*
|
||||
* Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
|
||||
*
|
||||
* Copyright (c) 2005 Keir Fraser
|
||||
*/
|
||||
|
||||
#ifndef __X86_EMULATE_H__
|
||||
#define __X86_EMULATE_H__
|
||||
|
||||
/*
|
||||
* x86_mem_emulator:
|
||||
*
|
||||
* These operations represent the instruction emulator's interface to memory.
|
||||
* There are two categories of operation: those that act on ordinary memory
|
||||
* regions (*_std), and those that act on memory regions known to require
|
||||
* special treatment or emulation (*_emulated).
|
||||
*
|
||||
* The emulator assumes that an instruction accesses only one 'emulated memory'
|
||||
* location, and that this is one of its data operands. Instruction fetches and
|
||||
* stack operations are assumed never to access emulated memory. The emulator
|
||||
* automatically deduces which operand of a string-move operation is accessing
|
||||
* emulated memory, and requires that the other operand accesses normal memory.
|
||||
*
|
||||
* NOTES:
|
||||
* 1. The emulator isn't very smart about emulated vs. standard memory.
|
||||
* 'Emulated memory' access addresses should be checked for sanity.
|
||||
* 'Normal memory' accesses may fault, and the caller must arrange to
|
||||
* detect and handle reentrancy into the emulator via recursive faults.
|
||||
* Accesses may be unaligned and may cross page boundaries.
|
||||
* 2. If the access fails (cannot emulate, or a standard access faults) then
|
||||
* it is up to the memop to propagate the fault to the guest VM via
|
||||
* some out-of-band mechanism, unknown to the emulator. The memop signals
|
||||
* failure by returning a non-zero value to the emulator, which will then
|
||||
* immediately bail.
|
||||
*/
|
||||
struct x86_mem_emulator
|
||||
{
|
||||
/*
|
||||
* read_std: Read bytes of standard (non-emulated/special) memory.
|
||||
* Used for instruction fetch, stack operations, and others.
|
||||
* @addr: [IN ] Linear address from which to read.
|
||||
* @val: [OUT] Value read from memory, zero-extended to 'u_long'.
|
||||
* @bytes: [IN ] Number of bytes to read from memory.
|
||||
*/
|
||||
int (*read_std)(
|
||||
unsigned long addr,
|
||||
unsigned long *val,
|
||||
unsigned int bytes);
|
||||
|
||||
/*
|
||||
* write_std: Write bytes of standard (non-emulated/special) memory.
|
||||
* Used for stack operations, and others.
|
||||
* @addr: [IN ] Linear address to which to write.
|
||||
* @val: [IN ] Value to write to memory (low-order bytes used as req'd).
|
||||
* @bytes: [IN ] Number of bytes to write to memory.
|
||||
*/
|
||||
int (*write_std)(
|
||||
unsigned long addr,
|
||||
unsigned long val,
|
||||
unsigned int bytes);
|
||||
|
||||
/*
|
||||
* read_emulated: Read bytes from emulated/special memory area.
|
||||
* @addr: [IN ] Linear address from which to read.
|
||||
* @val: [OUT] Value read from memory, zero-extended to 'u_long'.
|
||||
* @bytes: [IN ] Number of bytes to read from memory.
|
||||
*/
|
||||
int (*read_emulated)(
|
||||
unsigned long addr,
|
||||
unsigned long *val,
|
||||
unsigned int bytes);
|
||||
|
||||
/*
|
||||
* write_emulated: Read bytes from emulated/special memory area.
|
||||
* @addr: [IN ] Linear address to which to write.
|
||||
* @val: [IN ] Value to write to memory (low-order bytes used as req'd).
|
||||
* @bytes: [IN ] Number of bytes to write to memory.
|
||||
*/
|
||||
int (*write_emulated)(
|
||||
unsigned long addr,
|
||||
unsigned long val,
|
||||
unsigned int bytes);
|
||||
|
||||
/*
|
||||
* cmpxchg_emulated: Emulate an atomic (LOCKed) CMPXCHG operation on an
|
||||
* emulated/special memory area.
|
||||
* @addr: [IN ] Linear address to access.
|
||||
* @old: [IN ] Value expected to be current at @addr.
|
||||
* @new: [IN ] Value to write to @addr.
|
||||
* @seen: [OUT] Value actually seen at @addr, zero-extended to 'u_long'.
|
||||
* @bytes: [IN ] Number of bytes to access using CMPXCHG.
|
||||
*/
|
||||
int (*cmpxchg_emulated)(
|
||||
unsigned long addr,
|
||||
unsigned long old,
|
||||
unsigned long new,
|
||||
unsigned long *seen,
|
||||
unsigned int bytes);
|
||||
};
|
||||
|
||||
|
||||
struct xen_regs;
|
||||
|
||||
/*
|
||||
* x86_emulate_memop: Emulate an instruction that faulted attempting to
|
||||
* read/write a 'special' memory area.
|
||||
* @regs: Register state at time of fault.
|
||||
* @cr2: Linear faulting address.
|
||||
* @ops: Interface to access special memory.
|
||||
* @mode: Current execution mode, represented by the default size of memory
|
||||
* addresses, in bytes. Valid values are 2, 4 and 8 (x86/64 only).
|
||||
*/
|
||||
extern int
|
||||
x86_emulate_memop(
|
||||
struct xen_regs *regs,
|
||||
unsigned long cr2,
|
||||
struct x86_mem_emulator *ops,
|
||||
int mode);
|
||||
|
||||
/*
|
||||
* Given the 'reg' portion of a ModRM byte, and a register block, return a
|
||||
* pointer into the block that addresses the relevant register.
|
||||
* @highbyte_regs specifies whether to decode AH,CH,DH,BH.
|
||||
*/
|
||||
extern void *
|
||||
decode_register(
|
||||
u8 modrm_reg, struct xen_regs *regs, int highbyte_regs);
|
||||
|
||||
#endif /* __X86_EMULATE_H__ */
|
||||