x86emul: extend / amend supported FPU opcodes First of all there are a number of secondary encodings both Intel and AMD support, but which aren't formally documented. See e.g. www.sandpile.org/x86/opc_fpu.htm for inofficial documentation. Next there are a few more no-ops - instructions which served a purpose only on 8087 or 287. Further switch from fail_if() to raising of #UD in a couple of places (as the decoding of FPU opcodes should now be complete except where explicitly marked as todo). Also adjust a few comments. Signed-off-by: Jan Beulich --- v2: Correct placement of ffreep case. Use generate_exception(...) in favor of generate_exception_if(true, ...). Add sandpile.org reference. --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -3577,18 +3577,18 @@ x86_emulate( host_and_vcpu_must_have(fpu); 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 */ + case 0xc0 ... 0xc7: /* fadd %stN,%st */ + case 0xc8 ... 0xcf: /* fmul %stN,%st */ + case 0xd0 ... 0xd7: /* fcom %stN,%st */ + case 0xd8 ... 0xdf: /* fcomp %stN,%st */ + case 0xe0 ... 0xe7: /* fsub %stN,%st */ + case 0xe8 ... 0xef: /* fsubr %stN,%st */ + case 0xf0 ... 0xf7: /* fdiv %stN,%st */ + case 0xf8 ... 0xff: /* fdivr %stN,%st */ emulate_fpu_insn_stub(0xd8, modrm); break; default: - fail_if(modrm >= 0xc0); + ASSERT(ea.type == OP_MEM); ea.bytes = 4; src = ea; if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, @@ -3634,6 +3634,7 @@ x86_emulate( case 0xc0 ... 0xc7: /* fld %stN */ case 0xc8 ... 0xcf: /* fxch %stN */ case 0xd0: /* fnop */ + case 0xd8 ... 0xdf: /* fstp %stN (alternative encoding) */ case 0xe0: /* fchs */ case 0xe1: /* fabs */ case 0xe4: /* ftst */ @@ -3663,7 +3664,7 @@ x86_emulate( emulate_fpu_insn_stub(0xd9, modrm); break; default: - fail_if(modrm >= 0xc0); + generate_exception_if(ea.type != OP_MEM, EXC_UD); switch ( modrm_reg & 7 ) { case 0: /* fld m32fp */ @@ -3686,7 +3687,8 @@ x86_emulate( dst.type = OP_MEM; emulate_fpu_insn_memdst("fstps", dst.val); break; - /* case 4: fldenv - TODO */ + case 4: /* fldenv - TODO */ + goto cannot_emulate; case 5: /* fldcw m2byte */ ea.bytes = 2; src = ea; @@ -3695,7 +3697,8 @@ x86_emulate( goto done; emulate_fpu_insn_memsrc("fldcw", src.val); break; - /* case 6: fstenv - TODO */ + case 6: /* fnstenv - TODO */ + goto cannot_emulate; case 7: /* fnstcw m2byte */ ea.bytes = 2; dst = ea; @@ -3703,7 +3706,7 @@ x86_emulate( emulate_fpu_insn_memdst("fnstcw", dst.val); break; default: - goto cannot_emulate; + generate_exception(EXC_UD); } } break; @@ -3723,7 +3726,7 @@ x86_emulate( emulate_fpu_insn_stub(0xda, modrm); break; default: - fail_if(modrm >= 0xc0); + generate_exception_if(ea.type != OP_MEM, EXC_UD); ea.bytes = 4; src = ea; if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, @@ -3772,16 +3775,16 @@ x86_emulate( vcpu_must_have_cmov(); emulate_fpu_insn_stub_eflags(0xdb, modrm); break; + case 0xe0: /* fneni - 8087 only, ignored by 287 */ + case 0xe1: /* fndisi - 8087 only, ignored by 287 */ case 0xe2: /* fnclex */ - emulate_fpu_insn("fnclex"); - break; case 0xe3: /* fninit */ - emulate_fpu_insn("fninit"); - break; - case 0xe4: /* fsetpm - 287 only, ignored by 387 */ + case 0xe4: /* fnsetpm - 287 only, ignored by 387 */ + /* case 0xe5: frstpm - 287 only, #UD on 387 */ + emulate_fpu_insn_stub(0xdb, modrm); break; default: - fail_if(modrm >= 0xc0); + generate_exception_if(ea.type != OP_MEM, EXC_UD); switch ( modrm_reg & 7 ) { case 0: /* fild m32i */ @@ -3826,7 +3829,7 @@ x86_emulate( emulate_fpu_insn_memdst("fstpt", dst.val); break; default: - goto cannot_emulate; + generate_exception(EXC_UD); } } break; @@ -3835,16 +3838,18 @@ x86_emulate( host_and_vcpu_must_have(fpu); 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 */ + case 0xc0 ... 0xc7: /* fadd %st,%stN */ + case 0xc8 ... 0xcf: /* fmul %st,%stN */ + case 0xd0 ... 0xd7: /* fcom %stN,%st (alternative encoding) */ + case 0xd8 ... 0xdf: /* fcomp %stN,%st (alternative encoding) */ + case 0xe0 ... 0xe7: /* fsubr %st,%stN */ + case 0xe8 ... 0xef: /* fsub %st,%stN */ + case 0xf0 ... 0xf7: /* fdivr %st,%stN */ + case 0xf8 ... 0xff: /* fdiv %st,%stN */ emulate_fpu_insn_stub(0xdc, modrm); break; default: - fail_if(modrm >= 0xc0); + ASSERT(ea.type == OP_MEM); ea.bytes = 8; src = ea; if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, @@ -3885,6 +3890,7 @@ x86_emulate( switch ( modrm ) { case 0xc0 ... 0xc7: /* ffree %stN */ + case 0xc8 ... 0xcf: /* fxch %stN (alternative encoding) */ case 0xd0 ... 0xd7: /* fst %stN */ case 0xd8 ... 0xdf: /* fstp %stN */ case 0xe0 ... 0xe7: /* fucom %stN */ @@ -3892,7 +3898,7 @@ x86_emulate( emulate_fpu_insn_stub(0xdd, modrm); break; default: - fail_if(modrm >= 0xc0); + generate_exception_if(ea.type != OP_MEM, EXC_UD); switch ( modrm_reg & 7 ) { case 0: /* fld m64fp */; @@ -3922,6 +3928,9 @@ x86_emulate( dst.type = OP_MEM; emulate_fpu_insn_memdst("fstpl", dst.val); break; + case 4: /* frstor - TODO */ + case 6: /* fnsave - TODO */ + goto cannot_emulate; case 7: /* fnstsw m2byte */ ea.bytes = 2; dst = ea; @@ -3929,7 +3938,7 @@ x86_emulate( emulate_fpu_insn_memdst("fnstsw", dst.val); break; default: - goto cannot_emulate; + generate_exception(EXC_UD); } } break; @@ -3940,6 +3949,7 @@ x86_emulate( { case 0xc0 ... 0xc7: /* faddp %stN */ case 0xc8 ... 0xcf: /* fmulp %stN */ + case 0xd0 ... 0xd7: /* fcomp %stN (alternative encoding) */ case 0xd9: /* fcompp */ case 0xe0 ... 0xe7: /* fsubrp %stN */ case 0xe8 ... 0xef: /* fsubp %stN */ @@ -3948,7 +3958,7 @@ x86_emulate( emulate_fpu_insn_stub(0xde, modrm); break; default: - fail_if(modrm >= 0xc0); + generate_exception_if(ea.type != OP_MEM, EXC_UD); ea.bytes = 2; src = ea; if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val, @@ -4000,8 +4010,14 @@ x86_emulate( vcpu_must_have_cmov(); emulate_fpu_insn_stub_eflags(0xdf, modrm); break; + case 0xc0 ... 0xc7: /* ffreep %stN */ + case 0xc8 ... 0xcf: /* fxch %stN (alternative encoding) */ + case 0xd0 ... 0xd7: /* fstp %stN (alternative encoding) */ + case 0xd8 ... 0xdf: /* fstp %stN (alternative encoding) */ + emulate_fpu_insn_stub(0xdf, modrm); + break; default: - fail_if(modrm >= 0xc0); + generate_exception_if(ea.type != OP_MEM, EXC_UD); switch ( modrm_reg & 7 ) { case 0: /* fild m16i */