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>
This commit is contained in:
David Vrabel authored and Jan Beulich committed 2016-02-26 12:29:21 +01:00
1 parent 825c6dddf5
commit 879b44b041
4 files changed
+61 -24

No files matched your search

+10
View File
@@ -343,6 +343,8 @@ int switch_native(struct domain *d)
hvm_set_mode(v, 8);
}
d->arch.x87_fip_width = cpu_has_fpu_sel ? 0 : 8;
return 0;
}
@@ -377,6 +379,8 @@ int switch_compat(struct domain *d)
domain_set_alloc_bitsize(d);
d->arch.x87_fip_width = 4;
return 0;
undo_and_fail:
@@ -653,6 +657,12 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags,
/* 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:
+12 -7
View File
@@ -144,9 +144,9 @@ static inline void fpu_xsave(struct vcpu *v)
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.
@@ -164,7 +164,11 @@ static inline void fpu_fxsave(struct vcpu *v)
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;
@@ -172,17 +176,18 @@ static inline void fpu_fxsave(struct vcpu *v)
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;
}
/*******************************/
+24 -17
View File
@@ -249,7 +249,7 @@ 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;
#define XSAVE(pfx) \
alternative_io_3(".byte " pfx "0x0f,0xae,0x27\n", /* xsave */ \
".byte " pfx "0x0f,0xae,0x37\n", /* xsaveopt */ \
@@ -261,7 +261,15 @@ void xsave(struct vcpu *v, uint64_t mask)
"=m" (*ptr), \
"a" (lmask), "d" (hmask), "D" (ptr))
if ( word_size <= 0 || !is_pv_32bit_vcpu(v) )
if ( fip_width == 8 || !(mask & XSTATE_FP) )
{
XSAVE("0x48,");
}
else if ( fip_width == 4 )
{
XSAVE("");
}
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;
@@ -274,9 +282,8 @@ void xsave(struct vcpu *v, uint64_t mask)
* we hence 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;
@@ -285,7 +292,7 @@ void xsave(struct vcpu *v, uint64_t mask)
XSAVE("0x48,");
if ( !(mask & ptr->xsave_hdr.xstate_bv & XSTATE_FP) ||
if ( !(ptr->xsave_hdr.xstate_bv & XSTATE_FP) ||
/*
* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
* is pending.
@@ -293,7 +300,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 || cpu_has_xsaves) && word_size > 0 )
if ( cpu_has_xsaveopt || cpu_has_xsaves )
{
ptr->fpu_sse.fip.sel = fcs;
ptr->fpu_sse.fdp.sel = fds;
@@ -301,25 +308,25 @@ 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
{
XSAVE("");
word_size = 4;
else
fip_width = 8;
}
#undef XSAVE
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)
+15
View File
@@ -339,6 +339,21 @@ struct arch_domain
u8 x86_vendor; /* CPU vendor */
u8 x86_model; /* CPU model */
/*
* 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;
cpuid_input_t *cpuids;
struct PITState vpit;
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.