x86emul: honor guest CR4.OSFXSR, CR4.OSXSAVE, and CR0.PE/EFLAGS.VM These checks belong into the emulator instead of hvmemul_get_fpu(). Signed-off-by: Jan Beulich --- Eventually the XCR0 checks should also move into the insn emulator, but that'll require a new hook (or an extension to the read_cr() semantics). --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -129,6 +129,13 @@ static int cpuid( (edx & (1U << 26)) != 0; \ }) +#define cpu_has_xsave ({ \ + unsigned int eax = 1, ecx = 0; \ + cpuid(&eax, &eax, &ecx, &eax, NULL); \ + /* Intentionally checking OSXSAVE here. */ \ + (ecx & (1U << 27)) != 0; \ +}) + static inline uint64_t xgetbv(uint32_t xcr) { uint32_t lo, hi; @@ -169,6 +176,11 @@ static int read_cr( case 0: *val = 0x00000001; /* PE */ return X86EMUL_OKAY; + + case 4: + /* OSFXSR, OSXMMEXCPT, and maybe OSXSAVE */ + *val = 0x00000600 | (cpu_has_xsave ? 0x00040000 : 0); + return X86EMUL_OKAY; } return X86EMUL_UNHANDLEABLE; --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -1631,20 +1631,11 @@ static int hvmemul_get_fpu( { case X86EMUL_FPU_fpu: case X86EMUL_FPU_wait: - break; case X86EMUL_FPU_mmx: - if ( !cpu_has_mmx ) - return X86EMUL_UNHANDLEABLE; - break; case X86EMUL_FPU_xmm: - if ( !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) ) - return X86EMUL_UNHANDLEABLE; break; case X86EMUL_FPU_ymm: - if ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) || - (ctxt->regs->eflags & X86_EFLAGS_VM) || - !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSXSAVE) || - !(curr->arch.xcr0 & XSTATE_SSE) || + if ( !(curr->arch.xcr0 & XSTATE_SSE) || !(curr->arch.xcr0 & XSTATE_YMM) ) return X86EMUL_UNHANDLEABLE; break; --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -424,8 +424,11 @@ typedef union { #define CR0_MP (1<<1) #define CR0_EM (1<<2) #define CR0_TS (1<<3) -#define CR4_TSD (1<<2) -#define CR4_UMIP (1<<11) + +#define CR4_TSD (1<<2) +#define CR4_OSFXSR (1<<9) +#define CR4_UMIP (1<<11) +#define CR4_OSXSAVE (1<<18) /* EFLAGS bit definitions. */ #define EFLG_VIP (1<<20) @@ -770,9 +773,23 @@ static int _get_fpu( unsigned long cr0; fail_if(!ops->read_cr); + if ( type >= X86EMUL_FPU_xmm ) + { + unsigned long cr4; + + rc = ops->read_cr(4, &cr4, ctxt); + if ( rc != X86EMUL_OKAY ) + return rc; + generate_exception_if(!(cr4 & ((type == X86EMUL_FPU_xmm) + ? CR4_OSFXSR : CR4_OSXSAVE)), + EXC_UD, -1); + } + rc = ops->read_cr(0, &cr0, ctxt); if ( rc != X86EMUL_OKAY ) return rc; + if ( !(cr0 & CR0_PE) || (ctxt->regs->eflags & EFLG_VM) ) + generate_exception_if(type >= X86EMUL_FPU_ymm, EXC_UD, -1); if ( cr0 & CR0_EM ) { generate_exception_if(type == X86EMUL_FPU_fpu, EXC_NM, -1);