[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] Generic x86 emulator now properly supports 16-bit addressing
# HG changeset patch # User kaf24@xxxxxxxxxxxxxxxxxxxx # Node ID 5fa0c70663f1d3859656d3fe4b4e214de59cb7d1 # Parent 829517be689f95d3d935ab1af17d0c31ee4950fc Generic x86 emulator now properly supports 16-bit addressing by narrowing accesses to SI/DI/SP registers. Also supports real-mode addressing by shifting and adding the appropriate segment register for certain stack and string operations where the effective address is not already known. todo: Stack operations should have address size specified by B bit in stack segment descriptor, not by default address size or address-size override. Probably nothing depends on the proper behaviour though. Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx> diff -r 829517be689f -r 5fa0c70663f1 xen/arch/x86/x86_emulate.c --- a/xen/arch/x86/x86_emulate.c Fri Dec 23 15:42:46 2005 +++ b/xen/arch/x86/x86_emulate.c Fri Dec 23 17:28:33 2005 @@ -371,6 +371,21 @@ (_type)_x; \ }) +/* Access/update address held in a register, based on addressing mode. */ +#define register_address(sel, reg) \ + ((ad_bytes == sizeof(unsigned long)) ? (reg) : \ + ((mode == X86EMUL_MODE_REAL) ? /* implies ad_bytes == 2 */ \ + (((unsigned long)(sel) << 4) + ((reg) & 0xffff)) : \ + ((reg) & ((1UL << (ad_bytes << 3)) - 1)))) +#define register_address_increment(reg, inc) \ +do { \ + if ( ad_bytes == sizeof(unsigned long) ) \ + (reg) += (inc); \ + else \ + (reg) = ((reg) & ~((1UL << (ad_bytes << 3)) - 1)) | \ + (((reg) + (inc)) & ((1UL << (ad_bytes << 3)) - 1)); \ +} while (0) + void * decode_register( uint8_t modrm_reg, struct cpu_user_regs *regs, int highbyte_regs) @@ -420,32 +435,64 @@ { uint8_t b, d, sib, twobyte = 0, rex_prefix = 0; uint8_t 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, i; + uint16_t *seg = NULL; /* override segment */ + unsigned int op_bytes, ad_bytes, lock_prefix = 0, rep_prefix = 0, i; int rc = 0; struct operand src, dst; /* Shadow copy of register state. Committed on successful emulation. */ struct cpu_user_regs _regs = *regs; + switch ( mode ) + { + case X86EMUL_MODE_REAL: + case X86EMUL_MODE_PROT16: + op_bytes = ad_bytes = 2; + break; + case X86EMUL_MODE_PROT32: + op_bytes = ad_bytes = 4; + break; +#ifdef __x86_64__ + case X86EMUL_MODE_PROT64: + op_bytes = 4; + ad_bytes = 8; + break; +#endif + default: + return -1; + } + /* Legacy prefixes. */ for ( i = 0; i < 8; i++ ) { switch ( b = insn_fetch(uint8_t, 1, _regs.eip) ) { case 0x66: /* operand-size override */ - op_bytes ^= 6; /* switch between 2/4 bytes */ + op_bytes ^= 6; /* switch between 2/4 bytes */ break; case 0x67: /* address-size override */ - ad_bytes ^= (mode == 8) ? 12 : 6; /* switch between 2/4/8 bytes */ + if ( mode == X86EMUL_MODE_PROT64 ) + ad_bytes ^= 12; /* switch between 4/8 bytes */ + else + ad_bytes ^= 6; /* switch between 2/4 bytes */ break; case 0x2e: /* CS override */ + seg = &_regs.cs; + break; case 0x3e: /* DS override */ + seg = &_regs.ds; + break; case 0x26: /* ES override */ + seg = &_regs.es; + break; case 0x64: /* FS override */ + seg = &_regs.fs; + break; case 0x65: /* GS override */ + seg = &_regs.gs; + break; case 0x36: /* SS override */ - DPRINTF("Warning: ignoring a segment override.\n"); + seg = &_regs.ss; break; case 0xf0: /* LOCK */ lock_prefix = 1; @@ -461,8 +508,12 @@ } done_prefixes: + /* Note quite the same as 80386 real mode, but hopefully good enough. */ + if ( (mode == X86EMUL_MODE_REAL) && (ad_bytes != 2) ) + goto cannot_emulate; + /* REX prefix. */ - if ( (mode == 8) && ((b & 0xf0) == 0x40) ) + if ( (mode == X86EMUL_MODE_PROT64) && ((b & 0xf0) == 0x40) ) { rex_prefix = b; if ( b & 8 ) @@ -674,7 +725,7 @@ emulate_2op_SrcV("cmp", src, dst, _regs.eflags); break; case 0x63: /* movsxd */ - if ( mode != 8 ) /* x86/64 long mode only */ + if ( mode != X86EMUL_MODE_PROT64 ) goto cannot_emulate; dst.val = (int32_t)src.val; break; @@ -721,12 +772,13 @@ 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) ) + /* 64-bit mode: POP always pops a 64-bit operand. */ + if ( mode == X86EMUL_MODE_PROT64 ) dst.bytes = 8; - if ( (rc = ops->read_std(_regs.esp, &dst.val, dst.bytes)) != 0 ) + if ( (rc = ops->read_std(register_address(_regs.ss, _regs.esp), + &dst.val, dst.bytes)) != 0 ) goto done; - _regs.esp += dst.bytes; + register_address_increment(_regs.esp, dst.bytes); break; case 0xc0 ... 0xc1: grp2: /* Grp2 */ switch ( modrm_reg ) @@ -797,16 +849,17 @@ 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) ) + /* 64-bit mode: PUSH always pushes a 64-bit operand. */ + if ( mode == X86EMUL_MODE_PROT64 ) { dst.bytes = 8; if ( (rc = ops->read_std((unsigned long)dst.ptr, &dst.val, 8)) != 0 ) goto done; } - _regs.esp -= dst.bytes; - if ( (rc = ops->write_std(_regs.esp, dst.val, dst.bytes)) != 0 ) + register_address_increment(_regs.esp, -dst.bytes); + if ( (rc = ops->write_std(register_address(_regs.ss, _regs.esp), + dst.val, dst.bytes)) != 0 ) goto done; dst.val = dst.orig_val; /* skanky: disable writeback */ break; @@ -873,19 +926,22 @@ { /* Write fault: destination is special memory. */ dst.ptr = (unsigned long *)cr2; - if ( (rc = ops->read_std(_regs.esi - _regs.edi + cr2, + if ( (rc = ops->read_std(register_address(seg ? *seg : _regs.ds, + _regs.esi), &dst.val, dst.bytes)) != 0 ) goto done; } else { /* Read fault: source is special memory. */ - dst.ptr = (unsigned long *)(_regs.edi - _regs.esi + cr2); + dst.ptr = (unsigned long *)register_address(_regs.es, _regs.edi); if ( (rc = ops->read_emulated(cr2, &dst.val, dst.bytes)) != 0 ) goto done; } - _regs.esi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes; - _regs.edi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes; + register_address_increment( + _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); + register_address_increment( + _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); break; case 0xa6 ... 0xa7: /* cmps */ DPRINTF("Urk! I don't handle CMPS.\n"); @@ -895,7 +951,8 @@ 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; + register_address_increment( + _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); break; case 0xac ... 0xad: /* lods */ dst.type = OP_REG; @@ -903,7 +960,8 @@ dst.ptr = (unsigned long *)&_regs.eax; if ( (rc = ops->read_emulated(cr2, &dst.val, dst.bytes)) != 0 ) goto done; - _regs.esi += (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes; + register_address_increment( + _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); break; case 0xae ... 0xaf: /* scas */ DPRINTF("Urk! I don't handle SCAS.\n"); diff -r 829517be689f -r 5fa0c70663f1 xen/include/asm-x86/x86_emulate.h --- a/xen/include/asm-x86/x86_emulate.h Fri Dec 23 15:42:46 2005 +++ b/xen/include/asm-x86/x86_emulate.h Fri Dec 23 17:28:33 2005 @@ -141,6 +141,12 @@ struct cpu_user_regs; +/* Current execution mode, passed to the emulator. */ +#define X86EMUL_MODE_REAL 0 +#define X86EMUL_MODE_PROT16 2 +#define X86EMUL_MODE_PROT32 4 +#define X86EMUL_MODE_PROT64 8 + /* * x86_emulate_memop: Emulate an instruction that faulted attempting to * read/write a 'special' memory area. @@ -149,6 +155,8 @@ * @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). + * Alternatively use the appropriate X86EMUL_MODE value (which also + * includes a value for emulating real mode). */ extern int x86_emulate_memop( _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |