Skip to content

Commit

Permalink
KVM: x86: only copy XSAVE state for the supported features
Browse files Browse the repository at this point in the history
This makes the interface more deterministic for userspace, which can expect
(after configuring only the features it supports) to get exactly the same
state from the kernel, independent of the host CPU and kernel version.

Suggested-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
  • Loading branch information
Paolo Bonzini authored and Gleb Natapov committed Oct 3, 2013
1 parent d7876f1 commit 4344ee9
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 6 deletions.
1 change: 1 addition & 0 deletions arch/x86/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ struct kvm_vcpu_arch {
struct fpu guest_fpu;
u64 xcr0;
u64 guest_supported_xcr0;
u32 guest_xstate_size;

struct kvm_pio_request pio;
void *pio_data;
Expand Down
28 changes: 26 additions & 2 deletions arch/x86/kvm/cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@
#include "mmu.h"
#include "trace.h"

static u32 xstate_required_size(u64 xstate_bv)
{
int feature_bit = 0;
u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;

xstate_bv &= ~XSTATE_FPSSE;
while (xstate_bv) {
if (xstate_bv & 0x1) {
u32 eax, ebx, ecx, edx;
cpuid_count(0xD, feature_bit, &eax, &ebx, &ecx, &edx);
ret = max(ret, eax + ebx);
}

xstate_bv >>= 1;
feature_bit++;
}

return ret;
}

void kvm_update_cpuid(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
Expand All @@ -47,12 +67,16 @@ void kvm_update_cpuid(struct kvm_vcpu *vcpu)
}

best = kvm_find_cpuid_entry(vcpu, 0xD, 0);
if (!best)
if (!best) {
vcpu->arch.guest_supported_xcr0 = 0;
else
vcpu->arch.guest_xstate_size = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
} else {
vcpu->arch.guest_supported_xcr0 =
(best->eax | ((u64)best->edx << 32)) &
host_xcr0 & KVM_SUPPORTED_XCR0;
vcpu->arch.guest_xstate_size =
xstate_required_size(vcpu->arch.guest_supported_xcr0);
}

kvm_pmu_cpuid_update(vcpu);
}
Expand Down
11 changes: 7 additions & 4 deletions arch/x86/kvm/x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -2984,11 +2984,13 @@ static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu,
struct kvm_xsave *guest_xsave)
{
if (cpu_has_xsave)
if (cpu_has_xsave) {
memcpy(guest_xsave->region,
&vcpu->arch.guest_fpu.state->xsave,
xstate_size);
else {
vcpu->arch.guest_xstate_size);
*(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)] &=
vcpu->arch.guest_supported_xcr0 | XSTATE_FPSSE;
} else {
memcpy(guest_xsave->region,
&vcpu->arch.guest_fpu.state->fxsave,
sizeof(struct i387_fxsave_struct));
Expand All @@ -3014,7 +3016,7 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
if (xstate_bv & ~host_xcr0)
return -EINVAL;
memcpy(&vcpu->arch.guest_fpu.state->xsave,
guest_xsave->region, xstate_size);
guest_xsave->region, vcpu->arch.guest_xstate_size);
} else {
if (xstate_bv & ~XSTATE_FPSSE)
return -EINVAL;
Expand Down Expand Up @@ -6951,6 +6953,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
vcpu->arch.pv_time_enabled = false;

vcpu->arch.guest_supported_xcr0 = 0;
vcpu->arch.guest_xstate_size = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;

kvm_async_pf_hash_reset(vcpu);
kvm_pmu_init(vcpu);
Expand Down

0 comments on commit 4344ee9

Please sign in to comment.