File 56d03711-x86-fpu-add-a-per-domain-field-to-set-the-width-of-FIP-FDP.patch of Package xen.7673
# Commit 879b44b041f26de35e4b527bf0f3c361eb52bd82
# Date 2016-02-26 12:29:21 +0100
# Author David Vrabel <david.vrabel@citrix.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86/fpu: add a per-domain field to set the width of FIP/FDP
The x86 architecture allows either: a) the 64-bit FIP/FDP registers to
be restored (clearing FCS and FDS); or b) the 32-bit FIP/FDP and
FCS/FDS registers to be restored (clearing the upper 32-bits).
Add a per-domain field to indicate which of these options a guest
needs. The options are: 8, 4 or 0. Where 0 indicates that the
hypervisor should automatically guess the FIP width by checking the
value of FIP/FDP when saving the state (this is the existing
behaviour).
The FIP width is initially automatic but is set explicitly in the
following cases:
- 32-bit PV guest: 4
- Newer CPUs that do not save FCS/FDS: 8
The x87_fip_width field is placed into an existing 1 byte hole in
struct arch_domain.
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Fix build.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -355,6 +355,8 @@ int switch_native(struct domain *d)
release_compat_l4(d->vcpu[vcpuid]);
}
+ d->arch.x87_fip_width = cpu_has_fpu_sel ? 0 : 8;
+
return 0;
}
@@ -388,6 +390,8 @@ int switch_compat(struct domain *d)
domain_set_alloc_bitsize(d);
+ d->arch.x87_fip_width = 4;
+
return 0;
undo_and_fail:
@@ -602,6 +606,12 @@ int arch_domain_create(struct domain *d,
/* PV/PVH guests get an emulated PIT too for video BIOSes to use. */
pit_init(d, cpu_khz);
+ /*
+ * If the FPU does not save FCS/FDS then we can always
+ * save/restore the 64-bit FIP/FDP and ignore the selectors.
+ */
+ d->arch.x87_fip_width = cpu_has_fpu_sel ? 0 : 8;
+
return 0;
fail:
--- a/xen/arch/x86/i387.c
+++ b/xen/arch/x86/i387.c
@@ -152,9 +152,9 @@ static inline void fpu_xsave(struct vcpu
static inline void fpu_fxsave(struct vcpu *v)
{
typeof(v->arch.xsave_area->fpu_sse) *fpu_ctxt = v->arch.fpu_ctxt;
- int word_size = cpu_has_fpu_sel ? 8 : 0;
+ unsigned int fip_width = v->domain->arch.x87_fip_width;
- if ( !is_pv_32bit_vcpu(v) )
+ if ( fip_width != 4 )
{
/*
* The only way to force fxsaveq on a wide range of gas versions.
@@ -172,7 +172,11 @@ static inline void fpu_fxsave(struct vcp
boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
return;
- if ( word_size > 0 &&
+ /*
+ * If the FIP/FDP[63:32] are both zero, it is safe to use the
+ * 32-bit restore to also restore the selectors.
+ */
+ if ( !fip_width &&
!((fpu_ctxt->fip.addr | fpu_ctxt->fdp.addr) >> 32) )
{
struct ix87_env fpu_env;
@@ -180,17 +184,18 @@ static inline void fpu_fxsave(struct vcp
asm volatile ( "fnstenv %0" : "=m" (fpu_env) );
fpu_ctxt->fip.sel = fpu_env.fcs;
fpu_ctxt->fdp.sel = fpu_env.fds;
- word_size = 4;
+ fip_width = 4;
}
+ else
+ fip_width = 8;
}
else
{
asm volatile ( "fxsave %0" : "=m" (*fpu_ctxt) );
- word_size = 4;
+ fip_width = 4;
}
- if ( word_size >= 0 )
- fpu_ctxt->x[FPU_WORD_SIZE_OFFSET] = word_size;
+ fpu_ctxt->x[FPU_WORD_SIZE_OFFSET] = fip_width;
}
/* Save x87 FPU state */
--- a/xen/arch/x86/xstate.c
+++ b/xen/arch/x86/xstate.c
@@ -70,9 +70,31 @@ void xsave(struct vcpu *v, uint64_t mask
struct xsave_struct *ptr = v->arch.xsave_area;
uint32_t hmask = mask >> 32;
uint32_t lmask = mask;
- int word_size = mask & XSTATE_FP ? (cpu_has_fpu_sel ? 8 : 0) : -1;
+ unsigned int fip_width = v->domain->arch.x87_fip_width;
- if ( word_size <= 0 || !is_pv_32bit_vcpu(v) )
+ if ( fip_width == 8 || !(mask & XSTATE_FP) )
+ {
+ if ( cpu_has_xsaveopt )
+ asm volatile ( ".byte 0x48,0x0f,0xae,0x37"
+ : "=m" (*ptr)
+ : "a" (lmask), "d" (hmask), "D" (ptr) );
+ else
+ asm volatile ( ".byte 0x48,0x0f,0xae,0x27"
+ : "=m" (*ptr)
+ : "a" (lmask), "d" (hmask), "D" (ptr) );
+ }
+ else if ( fip_width == 4 )
+ {
+ if ( cpu_has_xsaveopt )
+ asm volatile ( ".byte 0x0f,0xae,0x37"
+ : "=m" (*ptr)
+ : "a" (lmask), "d" (hmask), "D" (ptr) );
+ else
+ asm volatile ( ".byte 0x0f,0xae,0x27"
+ : "=m" (*ptr)
+ : "a" (lmask), "d" (hmask), "D" (ptr) );
+ }
+ else
{
typeof(ptr->fpu_sse.fip.sel) fcs = ptr->fpu_sse.fip.sel;
typeof(ptr->fpu_sse.fdp.sel) fds = ptr->fpu_sse.fdp.sel;
@@ -85,9 +107,8 @@ void xsave(struct vcpu *v, uint64_t mask
* need to put the save image back into the state that it was in
* right after the previous xsaveopt.
*/
- if ( word_size > 0 &&
- (ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] == 4 ||
- ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] == 2) )
+ if ( ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] == 4 ||
+ ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] == 2 )
{
ptr->fpu_sse.fip.sel = 0;
ptr->fpu_sse.fdp.sel = 0;
@@ -109,7 +130,7 @@ void xsave(struct vcpu *v, uint64_t mask
(!(ptr->fpu_sse.fsw & 0x0080) &&
boot_cpu_data.x86_vendor == X86_VENDOR_AMD) )
{
- if ( cpu_has_xsaveopt && word_size > 0 )
+ if ( cpu_has_xsaveopt )
{
ptr->fpu_sse.fip.sel = fcs;
ptr->fpu_sse.fdp.sel = fds;
@@ -117,31 +138,24 @@ void xsave(struct vcpu *v, uint64_t mask
return;
}
- if ( word_size > 0 &&
- !((ptr->fpu_sse.fip.addr | ptr->fpu_sse.fdp.addr) >> 32) )
+ /*
+ * If the FIP/FDP[63:32] are both zero, it is safe to use the
+ * 32-bit restore to also restore the selectors.
+ */
+ if ( !((ptr->fpu_sse.fip.addr | ptr->fpu_sse.fdp.addr) >> 32) )
{
struct ix87_env fpu_env;
asm volatile ( "fnstenv %0" : "=m" (fpu_env) );
ptr->fpu_sse.fip.sel = fpu_env.fcs;
ptr->fpu_sse.fdp.sel = fpu_env.fds;
- word_size = 4;
+ fip_width = 4;
}
- }
- else
- {
- if ( cpu_has_xsaveopt )
- asm volatile ( ".byte 0x0f,0xae,0x37"
- : "=m" (*ptr)
- : "a" (lmask), "d" (hmask), "D" (ptr) );
else
- asm volatile ( ".byte 0x0f,0xae,0x27"
- : "=m" (*ptr)
- : "a" (lmask), "d" (hmask), "D" (ptr) );
- word_size = 4;
+ fip_width = 8;
}
- if ( word_size >= 0 )
- ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] = word_size;
+ if ( mask & XSTATE_FP )
+ ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] = fip_width;
}
void xrstor(struct vcpu *v, uint64_t mask)
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -293,6 +293,21 @@ struct arch_domain
/* Domain cannot handle spurious page faults? */
bool_t suppress_spurious_page_faults;
+ /*
+ * The width of the FIP/FDP register in the FPU that needs to be
+ * saved/restored during a context switch. This is needed because
+ * the FPU can either: a) restore the 64-bit FIP/FDP and clear FCS
+ * and FDS; or b) restore the 32-bit FIP/FDP (clearing the upper
+ * 32-bits of FIP/FDP) and restore FCS/FDS.
+ *
+ * Which one is needed depends on the guest.
+ *
+ * This can be either: 8, 4 or 0. 0 means auto-detect the size
+ * based on the width of FIP/FDP values that are written by the
+ * guest.
+ */
+ uint8_t x87_fip_width;
+
/* Continuable domain_relinquish_resources(). */
enum {
RELMEM_not_started,