summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@qumranet.com>2007-09-20 12:27:28 +0200
committerAvi Kivity <avi@qumranet.com>2007-09-20 12:27:28 +0200
commit5fdd2a196e7975d446fedf6973cbb20708f1359c (patch)
tree6bccfeb537002bf6bdacb5801db89a16e71d5a43
parente8ebaa91f96407a90c1cb81708a87a25f40ba8ab (diff)
KVM: Fix host oops due to guest changing eferkvm-42
If the guest changes efer from long mode with sce disabled to legacy mode, then load_transition_efer() zeros vmx->host_state.guest_efer_loaded, but the SCE-disabled efer remains in effect. So when we return to the host, we disable SCE and syscalls no longer work. Fix by (a) not touching vmx->host_state.guest_efer_loaded if we're not setting it, and instead (b) clearing it explicitly when we switch back. Also switch back when the guest writes to efer so we start from a clean slate. Signed-off-by: Avi Kivity <avi@qumranet.com>
-rw-r--r--drivers/kvm/vmx.c22
1 files changed, 15 insertions, 7 deletions
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c
index 7f168ad4b7a3..c342981d5ad7 100644
--- a/drivers/kvm/vmx.c
+++ b/drivers/kvm/vmx.c
@@ -354,17 +354,24 @@ static void load_transition_efer(struct vcpu_vmx *vmx)
if (guest_efer & EFER_LMA)
ignore_bits &= ~(u64)EFER_SCE;
#endif
- vmx->host_state.guest_efer_loaded
- = (guest_efer & ~ignore_bits) != (host_efer & ~ignore_bits);
-
- if (!vmx->host_state.guest_efer_loaded)
+ if ((guest_efer & ~ignore_bits) == (host_efer & ~ignore_bits))
return;
+
+ vmx->host_state.guest_efer_loaded = 1;
guest_efer &= ~ignore_bits;
guest_efer |= host_efer & ignore_bits;
wrmsrl(MSR_EFER, guest_efer);
vmx->vcpu.stat.efer_reload++;
}
+static void reload_host_efer(struct vcpu_vmx *vmx)
+{
+ if (vmx->host_state.guest_efer_loaded) {
+ vmx->host_state.guest_efer_loaded = 0;
+ load_msrs(vmx->host_msrs + vmx->msr_offset_efer, 1);
+ }
+}
+
static void vmx_save_host_state(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -439,8 +446,7 @@ static void vmx_load_host_state(struct vcpu_vmx *vmx)
reload_tss();
save_msrs(vmx->guest_msrs, vmx->save_nmsrs);
load_msrs(vmx->host_msrs, vmx->save_nmsrs);
- if (vmx->host_state.guest_efer_loaded)
- load_msrs(vmx->host_msrs + vmx->msr_offset_efer, 1);
+ reload_host_efer(vmx);
}
/*
@@ -728,8 +734,10 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
#ifdef CONFIG_X86_64
case MSR_EFER:
ret = kvm_set_msr_common(vcpu, msr_index, data);
- if (vmx->host_state.loaded)
+ if (vmx->host_state.loaded) {
+ reload_host_efer(vmx);
load_transition_efer(vmx);
+ }
break;
case MSR_FS_BASE:
vmcs_writel(GUEST_FS_BASE, data);