[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] x86_emulate: (Almost) complete FPU emulation.
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1214915261 -3600 # Node ID 6685c412698f43592c0f9fcc941ecb0b3a5e2012 # Parent d4cf12d128eda92fc461571f2454e00ef7090e45 x86_emulate: (Almost) complete FPU emulation. Provide emulation for all FPU instructions except fsave/frstore & fnstenv/fldenv. While the main purpose of the patch is to avoid current and future "gotchas" on FPU intructions used by various OS boot-loaders, it is complete enough to run DOS realmode FPU applications and benchmarks, but don't expect to set any speed records. Signed-off-by: Trolle Selander <trolle.selander@xxxxxxxxxxxxx> Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx> --- xen/arch/x86/x86_emulate/x86_emulate.c | 494 ++++++++++++++++++++++++++++++--- 1 files changed, 458 insertions(+), 36 deletions(-) diff -r d4cf12d128ed -r 6685c412698f xen/arch/x86/x86_emulate/x86_emulate.c --- a/xen/arch/x86/x86_emulate/x86_emulate.c Tue Jul 01 11:41:43 2008 +0100 +++ b/xen/arch/x86/x86_emulate/x86_emulate.c Tue Jul 01 13:27:41 2008 +0100 @@ -142,12 +142,14 @@ static uint8_t opcode_table[256] = { ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, /* 0xD0 - 0xD7 */ - ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM, - ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM, + ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM, + ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, /* 0xD8 - 0xDF */ - 0, ImplicitOps|ModRM|Mov, 0, ImplicitOps|ModRM|Mov, - 0, ImplicitOps|ModRM|Mov, ImplicitOps|ModRM|Mov, ImplicitOps|ModRM|Mov, + ImplicitOps|ModRM|Mov, ImplicitOps|ModRM|Mov, + ImplicitOps|ModRM|Mov, ImplicitOps|ModRM|Mov, + ImplicitOps|ModRM|Mov, ImplicitOps|ModRM|Mov, + ImplicitOps|ModRM|Mov, ImplicitOps|ModRM|Mov, /* 0xE0 - 0xE7 */ ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps, @@ -216,7 +218,7 @@ static uint8_t twobyte_table[256] = { ByteOp|DstMem|SrcNone|ModRM|Mov, ByteOp|DstMem|SrcNone|ModRM|Mov, /* 0xA0 - 0xA7 */ ImplicitOps, ImplicitOps, ImplicitOps, DstBitBase|SrcReg|ModRM, - DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, 0, 0, + DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, 0, 0, /* 0xA8 - 0xAF */ ImplicitOps, ImplicitOps, 0, DstBitBase|SrcReg|ModRM, DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, 0, DstReg|SrcMem|ModRM, @@ -246,8 +248,20 @@ static uint8_t twobyte_table[256] = { /* Type, address-of, and value of an instruction's operand. */ struct operand { enum { OP_REG, OP_MEM, OP_IMM, OP_NONE } type; - unsigned int bytes; - unsigned long val, orig_val; + unsigned int bytes; + + /* Up to 128-byte operand value, addressable as ulong or uint32_t[]. */ + union { + unsigned long val; + uint32_t bigval[4]; + }; + + /* Up to 128-byte operand value, addressable as ulong or uint32_t[]. */ + union { + unsigned long orig_val; + uint32_t orig_bigval[4]; + }; + union { /* OP_REG: Pointer to register field. */ unsigned long *reg; @@ -594,6 +608,18 @@ do{ struct fpu_insn_ctxt fic; put_fpu(&fic); \ } while (0) +#define emulate_fpu_insn_memsrc(_op, _arg) \ +do{ struct fpu_insn_ctxt fic; \ + get_fpu(X86EMUL_FPU_fpu, &fic); \ + asm volatile ( \ + "movb $2f-1f,%0 \n" \ + "1: " _op " %1 \n" \ + "2: \n" \ + : "=m" (fic.insn_bytes) \ + : "m" (_arg) : "memory" ); \ + put_fpu(&fic); \ +} while (0) + #define emulate_fpu_insn_stub(_bytes...) \ do{ uint8_t stub[] = { _bytes, 0xc3 }; \ struct fpu_insn_ctxt fic = { .insn_bytes = sizeof(stub)-1 }; \ @@ -1007,14 +1033,15 @@ protmode_load_seg( if ( (desc.b & (5u<<9)) == (4u<<9) ) goto raise_exn; /* Non-conforming segment: check DPL against RPL and CPL. */ - if ( ((desc.b & (6u<<9)) != (6u<<9)) && ((dpl < cpl) || (dpl < rpl)) ) + if ( ((desc.b & (6u<<9)) != (6u<<9)) && + ((dpl < cpl) || (dpl < rpl)) ) goto raise_exn; break; } /* Ensure Accessed flag is set. */ new_desc_b = desc.b | 0x100; - rc = ((desc.b & 0x100) ? X86EMUL_OKAY : + rc = ((desc.b & 0x100) ? X86EMUL_OKAY : ops->cmpxchg( x86_seg_none, desctab.base + (sel & 0xfff8) + 4, &desc.b, &new_desc_b, 4, ctxt)); @@ -1076,16 +1103,16 @@ decode_register( 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->eax + 1) : (unsigned char *)®s->esp); break; case 5: p = (highbyte_regs ? - ((unsigned char *)®s->ecx + 1) : + ((unsigned char *)®s->ecx + 1) : (unsigned char *)®s->ebp); break; case 6: p = (highbyte_regs ? - ((unsigned char *)®s->edx + 1) : + ((unsigned char *)®s->edx + 1) : (unsigned char *)®s->esi); break; case 7: p = (highbyte_regs ? - ((unsigned char *)®s->ebx + 1) : + ((unsigned char *)®s->ebx + 1) : (unsigned char *)®s->edi); break; #if defined(__x86_64__) case 8: p = ®s->r8; break; @@ -2708,7 +2735,7 @@ x86_emulate( int offset = (b == 0xca) ? insn_fetch_type(uint16_t) : 0; op_bytes = mode_64bit() ? 8 : op_bytes; if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes), - &dst.val, op_bytes, ctxt, ops)) || + &dst.val, op_bytes, ctxt, ops)) || (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes + offset), &src.val, op_bytes, ctxt, ops)) || (rc = load_seg(x86_seg_cs, (uint16_t)src.val, ctxt, ops)) ) @@ -2800,6 +2827,58 @@ x86_emulate( *(uint8_t *)&_regs.eax = al; break; } + + case 0xd8: /* FPU 0xd8 */ + switch ( modrm ) + { + case 0xc0 ... 0xc7: /* fadd %stN,%stN */ + case 0xc8 ... 0xcf: /* fmul %stN,%stN */ + case 0xd0 ... 0xd7: /* fcom %stN,%stN */ + case 0xd8 ... 0xdf: /* fcomp %stN,%stN */ + case 0xe0 ... 0xe7: /* fsub %stN,%stN */ + case 0xe8 ... 0xef: /* fsubr %stN,%stN */ + case 0xf0 ... 0xf7: /* fdiv %stN,%stN */ + case 0xf8 ... 0xff: /* fdivr %stN,%stN */ + emulate_fpu_insn_stub(0xd8, modrm); + break; + default: + fail_if(modrm >= 0xc0); + ea.bytes = 4; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + switch ( modrm_reg & 7 ) + { + case 0: /* fadd */ + emulate_fpu_insn_memsrc("fadds", src.val); + break; + case 1: /* fmul */ + emulate_fpu_insn_memsrc("fmuls", src.val); + break; + case 2: /* fcom */ + emulate_fpu_insn_memsrc("fcoms", src.val); + break; + case 3: /* fcomp */ + emulate_fpu_insn_memsrc("fcomps", src.val); + break; + case 4: /* fsub */ + emulate_fpu_insn_memsrc("fsubs", src.val); + break; + case 5: /* fsubr */ + emulate_fpu_insn_memsrc("fsubrs", src.val); + break; + case 6: /* fdiv */ + emulate_fpu_insn_memsrc("fdivs", src.val); + break; + case 7: /* fdivr */ + emulate_fpu_insn_memsrc("fdivrs", src.val); + break; + default: + goto cannot_emulate; + } + } + break; case 0xd9: /* FPU 0xd9 */ switch ( modrm ) @@ -2837,28 +2916,269 @@ x86_emulate( emulate_fpu_insn_stub(0xd9, modrm); break; default: - fail_if((modrm_reg & 7) != 7); fail_if(modrm >= 0xc0); - /* fnstcw m2byte */ - ea.bytes = 2; - dst = ea; - emulate_fpu_insn_memdst("fnstcw", dst.val); + switch ( modrm_reg & 7 ) + { + case 0: /* fld m32fp */ + ea.bytes = 4; + src = ea; + if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memsrc("flds", src.val); + break; + case 2: /* fstp m32fp */ + ea.bytes = 4; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fsts", dst.val); + break; + case 3: /* fstp m32fp */ + ea.bytes = 4; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fstps", dst.val); + break; + /* case 4: fldenv - TODO */ + case 5: /* fldcw m2byte */ + ea.bytes = 2; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memsrc("fldcw", src.val); + break; + /* case 6: fstenv - TODO */ + case 7: /* fnstcw m2byte */ + ea.bytes = 2; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fnstcw", dst.val); + break; + default: + goto cannot_emulate; + } + } + break; + + case 0xda: /* FPU 0xda */ + switch ( modrm ) + { + case 0xc0 ... 0xc7: /* fcmovb %stN */ + case 0xc8 ... 0xcf: /* fcmove %stN */ + case 0xd0 ... 0xd7: /* fcmovbe %stN */ + case 0xd8 ... 0xdf: /* fcmovu %stN */ + case 0xe9: /* fucompp */ + emulate_fpu_insn_stub(0xda, modrm); + break; + default: + fail_if(modrm >= 0xc0); + ea.bytes = 8; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + switch ( modrm_reg & 7 ) + { + case 0: /* fiadd m64i */ + emulate_fpu_insn_memsrc("fiaddl", src.val); + break; + case 1: /* fimul m64i */ + emulate_fpu_insn_memsrc("fimul", src.val); + break; + case 2: /* ficom m64i */ + emulate_fpu_insn_memsrc("ficoml", src.val); + break; + case 3: /* ficomp m64i */ + emulate_fpu_insn_memsrc("ficompl", src.val); + break; + case 4: /* fisub m64i */ + emulate_fpu_insn_memsrc("fisubl", src.val); + break; + case 5: /* fisubr m64i */ + emulate_fpu_insn_memsrc("fisubrl", src.val); + break; + case 6: /* fidiv m64i */ + emulate_fpu_insn_memsrc("fidivl", src.val); + break; + case 7: /* fidivr m64i */ + emulate_fpu_insn_memsrc("fidivrl", src.val); + break; + default: + goto cannot_emulate; + } } break; case 0xdb: /* FPU 0xdb */ - fail_if(modrm != 0xe3); - /* fninit */ - emulate_fpu_insn("fninit"); + switch ( modrm ) + { + case 0xc0 ... 0xc7: /* fcmovnb %stN */ + case 0xc8 ... 0xcf: /* fcmovne %stN */ + case 0xd0 ... 0xd7: /* fcmovnbe %stN */ + case 0xd8 ... 0xdf: /* fcmovnu %stN */ + emulate_fpu_insn_stub(0xdb, modrm); + break; + case 0xe2: /* fnclex */ + emulate_fpu_insn("fnclex"); + break; + case 0xe3: /* fninit */ + emulate_fpu_insn("fninit"); + break; + case 0xe4: /* fsetpm - 287 only, ignored by 387 */ + break; + case 0xe8 ... 0xef: /* fucomi %stN */ + case 0xf0 ... 0xf7: /* fcomi %stN */ + emulate_fpu_insn_stub(0xdb, modrm); + break; + default: + fail_if(modrm >= 0xc0); + switch ( modrm_reg & 7 ) + { + case 0: /* fild m32i */ + ea.bytes = 4; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memsrc("fildl", src.val); + break; + case 1: /* fisttp m32i */ + ea.bytes = 4; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fisttpl", dst.val); + break; + case 2: /* fist m32i */ + ea.bytes = 4; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fistl", dst.val); + break; + case 3: /* fistp m32i */ + ea.bytes = 4; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fistpl", dst.val); + break; + case 5: /* fld m80fp */ + ea.bytes = 10; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, + &src.val, src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memdst("fldt", src.val); + break; + case 7: /* fstp m80fp */ + ea.bytes = 10; + dst.type = OP_MEM; + dst = ea; + emulate_fpu_insn_memdst("fstpt", dst.val); + break; + default: + goto cannot_emulate; + } + } + break; + + case 0xdc: /* FPU 0xdc */ + switch ( modrm ) + { + case 0xc0 ... 0xc7: /* fadd %stN */ + case 0xc8 ... 0xcf: /* fmul %stN */ + case 0xe0 ... 0xe7: /* fsubr %stN */ + case 0xe8 ... 0xef: /* fsub %stN */ + case 0xf0 ... 0xf7: /* fdivr %stN */ + case 0xf8 ... 0xff: /* fdiv %stN */ + emulate_fpu_insn_stub(0xdc, modrm); + break; + default: + fail_if(modrm >= 0xc0); + ea.bytes = 8; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + switch ( modrm_reg & 7 ) + { + case 0: /* fadd m64fp */ + emulate_fpu_insn_memsrc("faddl", src.val); + break; + case 1: /* fmul m64fp */ + emulate_fpu_insn_memsrc("fmull", src.val); + break; + case 2: /* fcom m64fp */ + emulate_fpu_insn_memsrc("fcoml", src.val); + break; + case 3: /* fcomp m64fp */ + emulate_fpu_insn_memsrc("fcompl", src.val); + break; + case 4: /* fsub m64fp */ + emulate_fpu_insn_memsrc("fsubl", src.val); + break; + case 5: /* fsubr m64fp */ + emulate_fpu_insn_memsrc("fsubrl", src.val); + break; + case 6: /* fdiv m64fp */ + emulate_fpu_insn_memsrc("fdivl", src.val); + break; + case 7: /* fdivr m64fp */ + emulate_fpu_insn_memsrc("fdivrl", src.val); + break; + } + } break; case 0xdd: /* FPU 0xdd */ - fail_if((modrm_reg & 7) != 7); - fail_if(modrm >= 0xc0); - /* fnstsw m2byte */ - ea.bytes = 2; - dst = ea; - emulate_fpu_insn_memdst("fnstsw", dst.val); + switch ( modrm ) + { + case 0xc0 ... 0xc7: /* ffree %stN */ + case 0xd0 ... 0xd7: /* fst %stN */ + case 0xd8 ... 0xdf: /* fstp %stN */ + case 0xe0 ... 0xe7: /* fucom %stN */ + case 0xe8 ... 0xef: /* fucomp %stN */ + emulate_fpu_insn_stub(0xdd, modrm); + break; + default: + fail_if(modrm >= 0xc0); + switch ( modrm_reg & 7 ) + { + case 0: /* fld m64fp */; + ea.bytes = 8; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memsrc("fldl", src.val); + break; + case 1: /* fisttp m64i */ + ea.bytes = 8; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fisttpll", dst.val); + break; + case 2: /* fst m64fp */ + ea.bytes = 8; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memsrc("fstl", dst.val); + break; + case 3: /* fstp m64fp */ + ea.bytes = 8; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fstpl", dst.val); + break; + case 7: /* fnstsw m2byte */ + ea.bytes = 2; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fnstsw", dst.val); + break; + default: + goto cannot_emulate; + } + } break; case 0xde: /* FPU 0xde */ @@ -2874,17 +3194,120 @@ x86_emulate( emulate_fpu_insn_stub(0xde, modrm); break; default: - goto cannot_emulate; + fail_if(modrm >= 0xc0); + ea.bytes = 2; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + switch ( modrm_reg & 7 ) + { + case 0: /* fiadd m16i */ + emulate_fpu_insn_memsrc("fiadd", src.val); + break; + case 1: /* fimul m16i */ + emulate_fpu_insn_memsrc("fimul", src.val); + break; + case 2: /* ficom m16i */ + emulate_fpu_insn_memsrc("ficom", src.val); + break; + case 3: /* ficomp m16i */ + emulate_fpu_insn_memsrc("ficomp", src.val); + break; + case 4: /* fisub m16i */ + emulate_fpu_insn_memsrc("fisub", src.val); + break; + case 5: /* fisubr m16i */ + emulate_fpu_insn_memsrc("fisubr", src.val); + break; + case 6: /* fidiv m16i */ + emulate_fpu_insn_memsrc("fidiv", src.val); + break; + case 7: /* fidivr m16i */ + emulate_fpu_insn_memsrc("fidivr", src.val); + break; + default: + goto cannot_emulate; + } } break; case 0xdf: /* FPU 0xdf */ - fail_if(modrm != 0xe0); - /* fnstsw %ax */ - dst.bytes = 2; - dst.type = OP_REG; - dst.reg = (unsigned long *)&_regs.eax; - emulate_fpu_insn_memdst("fnstsw", dst.val); + switch ( modrm ) + { + case 0xe0: + /* fnstsw %ax */ + dst.bytes = 2; + dst.type = OP_REG; + dst.reg = (unsigned long *)&_regs.eax; + emulate_fpu_insn_memdst("fnstsw", dst.val); + break; + case 0xf0 ... 0xf7: /* fcomip %stN */ + case 0xf8 ... 0xff: /* fucomip %stN */ + emulate_fpu_insn_stub(0xdf, modrm); + break; + default: + fail_if(modrm >= 0xc0); + switch ( modrm_reg & 7 ) + { + case 0: /* fild m16i */ + ea.bytes = 2; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memsrc("fild", src.val); + break; + case 1: /* fisttp m16i */ + ea.bytes = 2; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fisttp", dst.val); + break; + case 2: /* fist m16i */ + ea.bytes = 2; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fist", dst.val); + break; + case 3: /* fistp m16i */ + ea.bytes = 2; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fistp", dst.val); + break; + case 4: /* fbld m80dec */ + ea.bytes = 10; + dst = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, + &src.val, src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memdst("fbld", src.val); + break; + case 5: /* fild m64i */ + ea.bytes = 8; + src = ea; + if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, + src.bytes, ctxt)) != 0 ) + goto done; + emulate_fpu_insn_memsrc("fildll", src.val); + break; + case 6: /* fbstp packed bcd */ + ea.bytes = 10; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fbstp", dst.val); + break; + case 7: /* fistp m64i */ + ea.bytes = 8; + dst = ea; + dst.type = OP_MEM; + emulate_fpu_insn_memdst("fistpll", dst.val); + break; + default: + goto cannot_emulate; + } + } break; case 0xe0 ... 0xe2: /* loop{,z,nz} */ { @@ -2939,7 +3362,6 @@ x86_emulate( /* out */ fail_if(ops->write_io == NULL); rc = ops->write_io(port, op_bytes, _regs.eax, ctxt); - } else { _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |