|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v8 09/12] x86emul: support FXSAVE/FXRSTOR
On 05/05/2020 09:16, Jan Beulich wrote:
> Note that FPU selector handling as well as MXCSR mask saving for now
> does not honor differences between host and guest visible featuresets.
>
> While for Intel operation of the insns with CR4.OSFXSR=0 is
> implementation dependent, use the easiest solution there: Simply don't
> look at the bit in the first place. For AMD and alike the behavior is
> well defined, so it gets handled together with FFXSR.
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
> ---
> v8: Respect EFER.FFXSE and CR4.OSFXSR. Correct wrong X86EMUL_NO_*
> dependencies. Reduce #ifdef-ary.
> v7: New.
>
> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
> @@ -953,6 +958,29 @@ typedef union {
> uint32_t data32[16];
> } mmval_t;
>
> +struct x86_fxsr {
> + uint16_t fcw;
> + uint16_t fsw;
> + uint16_t ftw:8, :8;
uint8_t
> + uint16_t fop;
> + union {
> + struct {
> + uint32_t offs;
> + uint32_t sel:16, :16;
uint16_t
> + };
> + uint64_t addr;
> + } fip, fdp;
> + uint32_t mxcsr;
> + uint32_t mxcsr_mask;
> + struct {
> + uint8_t data[10];
> + uint8_t _[6];
I'd be tempted to suggest uint16_t :16, :16, :16; here, which gets rid
of the named field, or explicitly rsvd to match below.
> + } fpreg[8];
> + uint64_t __attribute__ ((aligned(16))) xmm[16][2];
> + uint64_t _[6];
This field however is used below. It would be clearer being named 'rsvd'.
> + uint64_t avl[6];
> +};
> +
> /*
> * While proper alignment gets specified above, this doesn't get honored by
> * the compiler for automatic variables. Use this helper to instantiate a
> @@ -1910,6 +1938,7 @@ amd_like(const struct x86_emulate_ctxt *
> #define vcpu_has_cmov() (ctxt->cpuid->basic.cmov)
> #define vcpu_has_clflush() (ctxt->cpuid->basic.clflush)
> #define vcpu_has_mmx() (ctxt->cpuid->basic.mmx)
> +#define vcpu_has_fxsr() (ctxt->cpuid->basic.fxsr)
> #define vcpu_has_sse() (ctxt->cpuid->basic.sse)
> #define vcpu_has_sse2() (ctxt->cpuid->basic.sse2)
> #define vcpu_has_sse3() (ctxt->cpuid->basic.sse3)
> @@ -8125,6 +8154,47 @@ x86_emulate(
> case X86EMUL_OPC(0x0f, 0xae): case X86EMUL_OPC_66(0x0f, 0xae): /* Grp15
> */
> switch ( modrm_reg & 7 )
> {
> +#if !defined(X86EMUL_NO_FPU) || !defined(X86EMUL_NO_MMX) || \
> + !defined(X86EMUL_NO_SIMD)
> + case 0: /* fxsave */
> + case 1: /* fxrstor */
> + generate_exception_if(vex.pfx, EXC_UD);
> + vcpu_must_have(fxsr);
> + generate_exception_if(ea.type != OP_MEM, EXC_UD);
> + generate_exception_if(!is_aligned(ea.mem.seg, ea.mem.off, 16,
> + ctxt, ops),
> + EXC_GP, 0);
> + fail_if(!ops->blk);
> + op_bytes =
> +#ifdef __x86_64__
> + !mode_64bit() ? offsetof(struct x86_fxsr, xmm[8]) :
> +#endif
> + sizeof(struct x86_fxsr);
> + if ( amd_like(ctxt) )
> + {
> + if ( !ops->read_cr ||
> + ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY )
> + cr4 = X86_CR4_OSFXSR;
Why do we want to assume OSFXSR in the case of a read_cr() failure,
rather than bailing on the entire instruction?
> + if ( !ops->read_msr ||
> + ops->read_msr(MSR_EFER, &msr_val, ctxt) != X86EMUL_OKAY
> )
> + msr_val = 0;
> + if ( !(cr4 & X86_CR4_OSFXSR) ||
> + (mode_64bit() && mode_ring0() && (msr_val &
> EFER_FFXSE)) )
> + op_bytes = offsetof(struct x86_fxsr, xmm[0]);
This is a very peculiar optimisation in AMD processors... (but your
logic here does agree with the description AFAICT)
> + }
> + /*
> + * This could also be X86EMUL_FPU_mmx, but it shouldn't be
> + * X86EMUL_FPU_xmm, as we don't want CR4.OSFXSR checked.
> + */
> + get_fpu(X86EMUL_FPU_fpu);
> + state->blk = modrm_reg & 1 ? blk_fxrstor : blk_fxsave;
> + if ( (rc = ops->blk(ea.mem.seg, ea.mem.off, NULL,
> + sizeof(struct x86_fxsr), &_regs.eflags,
> + state, ctxt)) != X86EMUL_OKAY )
> + goto done;
> + break;
> +#endif /* X86EMUL_NO_{FPU,MMX,SIMD} */
> +
> #ifndef X86EMUL_NO_SIMD
> case 2: /* ldmxcsr */
> generate_exception_if(vex.pfx, EXC_UD);
> @@ -11611,6 +11681,8 @@ int x86_emul_blk(
> struct x86_emulate_state *state,
> struct x86_emulate_ctxt *ctxt)
> {
> + int rc = X86EMUL_OKAY;
> +
> switch ( state->blk )
> {
> bool zf;
> @@ -11819,6 +11891,77 @@ int x86_emul_blk(
>
> #endif /* X86EMUL_NO_FPU */
>
> +#if !defined(X86EMUL_NO_FPU) || !defined(X86EMUL_NO_MMX) || \
> + !defined(X86EMUL_NO_SIMD)
> +
> + case blk_fxrstor:
> + {
> + struct x86_fxsr *fxsr = FXSAVE_AREA;
> +
> + ASSERT(!data);
> + ASSERT(bytes == sizeof(*fxsr));
> + ASSERT(state->op_bytes <= bytes);
> +
> + if ( state->op_bytes < sizeof(*fxsr) )
> + {
> + if ( state->rex_prefix & REX_W )
> + {
> + /*
> + * The only way to force fxsaveq on a wide range of gas
> + * versions. On older versions the rex64 prefix works only if
> + * we force an addressing mode that doesn't require extended
> + * registers.
> + */
> + asm volatile ( ".byte 0x48; fxsave (%1)"
> + : "=m" (*fxsr) : "R" (fxsr) );
> + }
> + else
> + asm volatile ( "fxsave %0" : "=m" (*fxsr) );
> + }
> +
> + memcpy(fxsr, ptr, min(state->op_bytes,
> + (unsigned int)offsetof(struct x86_fxsr, _)));
> + memset(fxsr->_, 0, sizeof(*fxsr) - offsetof(struct x86_fxsr, _));
I'm completely lost trying to follow what's going on here. Why are we
constructing something different to what the guest gave us?
> +
> + generate_exception_if(fxsr->mxcsr & ~mxcsr_mask, EXC_GP, 0);
> +
> + if ( state->rex_prefix & REX_W )
> + {
> + /* See above for why operand/constraints are this way. */
> + asm volatile ( ".byte 0x48; fxrstor (%0)"
> + :: "R" (fxsr), "m" (*fxsr) );
> + }
> + else
> + asm volatile ( "fxrstor %0" :: "m" (*fxsr) );
> + break;
> + }
> +
> + case blk_fxsave:
> + {
> + struct x86_fxsr *fxsr = FXSAVE_AREA;
> +
> + ASSERT(!data);
> + ASSERT(bytes == sizeof(*fxsr));
> + ASSERT(state->op_bytes <= bytes);
> +
> + if ( state->rex_prefix & REX_W )
> + {
> + /* See above for why operand/constraint are this way. */
> + asm volatile ( ".byte 0x48; fxsave (%0)"
> + :: "R" (state->op_bytes < sizeof(*fxsr) ? fxsr :
> ptr)
> + : "memory" );
> + }
> + else
> + asm volatile ( "fxsave (%0)"
> + :: "r" (state->op_bytes < sizeof(*fxsr) ? fxsr :
> ptr)
> + : "memory" );
> + if ( state->op_bytes < sizeof(*fxsr) )
> + memcpy(ptr, fxsr, state->op_bytes);
I think this logic would be clearer to follow with:
void *buf = state->op_bytes < sizeof(*fxsr) ? fxsr : ptr;
...
if ( buf != ptr )
memcpy(ptr, fxsr, state->op_bytes);
This more clearly highlights the "we either fxsave'd straight into the
destination pointer, or into a local buffer if we only want a subset"
property.
> --- a/xen/arch/x86/x86_emulate.c
> +++ b/xen/arch/x86/x86_emulate.c
> @@ -42,6 +42,8 @@
> } \
> })
>
> +#define FXSAVE_AREA current->arch.fpu_ctxt
How safe is this? Don't we already use this buffer to recover the old
state in the case of an exception?
~Andrew
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |