diff --git a/tools/tests/x86_emulator/test_x86_emulator.c b/tools/tests/x86_emulator/test_x86_emulator.c index 906fc6f398..5864a41b7a 100644 --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -127,6 +127,22 @@ static inline uint64_t xgetbv(uint32_t xcr) (ebx & (1U << 5)) != 0; \ }) +static int read_cr( + unsigned int reg, + unsigned long *val, + struct x86_emulate_ctxt *ctxt) +{ + /* Fake just enough state for the emulator's _get_fpu() to be happy. */ + switch ( reg ) + { + case 0: + *val = 0x00000001; /* PE */ + return X86EMUL_OKAY; + } + + return X86EMUL_UNHANDLEABLE; +} + int get_fpu( void (*exception_callback)(void *, struct cpu_user_regs *), void *exception_callback_arg, @@ -158,6 +174,7 @@ static struct x86_emulate_ops emulops = { .write = write, .cmpxchg = cmpxchg, .cpuid = cpuid, + .read_cr = read_cr, .get_fpu = get_fpu, }; diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index 868aa1df51..fab8ae324f 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -1070,6 +1070,7 @@ static int hvmemul_get_fpu( switch ( type ) { case X86EMUL_FPU_fpu: + case X86EMUL_FPU_wait: break; case X86EMUL_FPU_mmx: if ( !cpu_has_mmx ) @@ -1077,7 +1078,6 @@ static int hvmemul_get_fpu( break; case X86EMUL_FPU_xmm: if ( !cpu_has_xmm || - (curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_EM) || !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) ) return X86EMUL_UNHANDLEABLE; break; diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c index 4c90101f9c..81e268a848 100644 --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -373,6 +373,9 @@ typedef union { /* Control register flags. */ #define CR0_PE (1<<0) +#define CR0_MP (1<<1) +#define CR0_EM (1<<2) +#define CR0_TS (1<<3) #define CR4_TSD (1<<2) /* EFLAGS bit definitions. */ @@ -400,6 +403,7 @@ typedef union { #define EXC_OF 4 #define EXC_BR 5 #define EXC_UD 6 +#define EXC_NM 7 #define EXC_TS 10 #define EXC_NP 11 #define EXC_SS 12 @@ -668,10 +672,45 @@ static void fpu_handle_exception(void *_fic, struct cpu_user_regs *regs) regs->eip += fic->insn_bytes; } +static int _get_fpu( + enum x86_emulate_fpu_type type, + struct fpu_insn_ctxt *fic, + struct x86_emulate_ctxt *ctxt, + const struct x86_emulate_ops *ops) +{ + int rc; + + fic->exn_raised = 0; + + fail_if(!ops->get_fpu); + rc = ops->get_fpu(fpu_handle_exception, fic, type, ctxt); + + if ( rc == X86EMUL_OKAY ) + { + unsigned long cr0; + + fail_if(!ops->read_cr); + rc = ops->read_cr(0, &cr0, ctxt); + if ( rc != X86EMUL_OKAY ) + return rc; + if ( cr0 & CR0_EM ) + { + generate_exception_if(type == X86EMUL_FPU_fpu, EXC_NM, -1); + generate_exception_if(type == X86EMUL_FPU_mmx, EXC_UD, -1); + generate_exception_if(type == X86EMUL_FPU_xmm, EXC_UD, -1); + } + generate_exception_if((cr0 & CR0_TS) && + (type != X86EMUL_FPU_wait || (cr0 & CR0_MP)), + EXC_NM, -1); + } + + done: + return rc; +} + #define get_fpu(_type, _fic) \ -do{ (_fic)->exn_raised = 0; \ - fail_if(ops->get_fpu == NULL); \ - rc = ops->get_fpu(fpu_handle_exception, _fic, _type, ctxt); \ +do { \ + rc = _get_fpu(_type, _fic, ctxt, ops); \ if ( rc ) goto done; \ } while (0) #define put_fpu(_fic) \ @@ -2349,8 +2388,14 @@ x86_emulate( } case 0x9b: /* wait/fwait */ - emulate_fpu_insn("fwait"); + { + struct fpu_insn_ctxt fic = { .insn_bytes = 1 }; + + get_fpu(X86EMUL_FPU_wait, &fic); + asm volatile ( "fwait" ::: "memory" ); + put_fpu(&fic); break; + } case 0x9c: /* pushf */ src.val = _regs.eflags; diff --git a/xen/arch/x86/x86_emulate/x86_emulate.h b/xen/arch/x86/x86_emulate/x86_emulate.h index 85bc4bcf76..73074236b6 100644 --- a/xen/arch/x86/x86_emulate/x86_emulate.h +++ b/xen/arch/x86/x86_emulate/x86_emulate.h @@ -99,6 +99,7 @@ struct segment_register { /* FPU sub-types which may be requested via ->get_fpu(). */ enum x86_emulate_fpu_type { X86EMUL_FPU_fpu, /* Standard FPU coprocessor instruction set */ + X86EMUL_FPU_wait, /* WAIT/FWAIT instruction */ X86EMUL_FPU_mmx, /* MMX instruction set (%mm0-%mm7) */ X86EMUL_FPU_xmm, /* SSE instruction set (%xmm0-%xmm7/15) */ X86EMUL_FPU_ymm /* AVX/XOP instruction set (%ymm0-%ymm7/15) */