diff options
Diffstat (limited to 'drivers/kvm/vmx.c')
-rw-r--r-- | drivers/kvm/vmx.c | 61 |
1 files changed, 37 insertions, 24 deletions
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index 91768d5dbfb9..8eb49e055ec0 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -57,6 +57,7 @@ struct vcpu_vmx { | |||
57 | u16 fs_sel, gs_sel, ldt_sel; | 57 | u16 fs_sel, gs_sel, ldt_sel; |
58 | int gs_ldt_reload_needed; | 58 | int gs_ldt_reload_needed; |
59 | int fs_reload_needed; | 59 | int fs_reload_needed; |
60 | int guest_efer_loaded; | ||
60 | }host_state; | 61 | }host_state; |
61 | 62 | ||
62 | }; | 63 | }; |
@@ -74,8 +75,6 @@ static DEFINE_PER_CPU(struct vmcs *, current_vmcs); | |||
74 | static struct page *vmx_io_bitmap_a; | 75 | static struct page *vmx_io_bitmap_a; |
75 | static struct page *vmx_io_bitmap_b; | 76 | static struct page *vmx_io_bitmap_b; |
76 | 77 | ||
77 | #define EFER_SAVE_RESTORE_BITS ((u64)EFER_SCE) | ||
78 | |||
79 | static struct vmcs_config { | 78 | static struct vmcs_config { |
80 | int size; | 79 | int size; |
81 | int order; | 80 | int order; |
@@ -138,18 +137,6 @@ static void save_msrs(struct kvm_msr_entry *e, int n) | |||
138 | rdmsrl(e[i].index, e[i].data); | 137 | rdmsrl(e[i].index, e[i].data); |
139 | } | 138 | } |
140 | 139 | ||
141 | static inline u64 msr_efer_save_restore_bits(struct kvm_msr_entry msr) | ||
142 | { | ||
143 | return (u64)msr.data & EFER_SAVE_RESTORE_BITS; | ||
144 | } | ||
145 | |||
146 | static inline int msr_efer_need_save_restore(struct vcpu_vmx *vmx) | ||
147 | { | ||
148 | int efer_offset = vmx->msr_offset_efer; | ||
149 | return msr_efer_save_restore_bits(vmx->host_msrs[efer_offset]) != | ||
150 | msr_efer_save_restore_bits(vmx->guest_msrs[efer_offset]); | ||
151 | } | ||
152 | |||
153 | static inline int is_page_fault(u32 intr_info) | 140 | static inline int is_page_fault(u32 intr_info) |
154 | { | 141 | { |
155 | return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK | | 142 | return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK | |
@@ -351,16 +338,42 @@ static void reload_tss(void) | |||
351 | 338 | ||
352 | static void load_transition_efer(struct vcpu_vmx *vmx) | 339 | static void load_transition_efer(struct vcpu_vmx *vmx) |
353 | { | 340 | { |
354 | u64 trans_efer; | ||
355 | int efer_offset = vmx->msr_offset_efer; | 341 | int efer_offset = vmx->msr_offset_efer; |
342 | u64 host_efer = vmx->host_msrs[efer_offset].data; | ||
343 | u64 guest_efer = vmx->guest_msrs[efer_offset].data; | ||
344 | u64 ignore_bits; | ||
345 | |||
346 | if (efer_offset < 0) | ||
347 | return; | ||
348 | /* | ||
349 | * NX is emulated; LMA and LME handled by hardware; SCE meaninless | ||
350 | * outside long mode | ||
351 | */ | ||
352 | ignore_bits = EFER_NX | EFER_SCE; | ||
353 | #ifdef CONFIG_X86_64 | ||
354 | ignore_bits |= EFER_LMA | EFER_LME; | ||
355 | /* SCE is meaningful only in long mode on Intel */ | ||
356 | if (guest_efer & EFER_LMA) | ||
357 | ignore_bits &= ~(u64)EFER_SCE; | ||
358 | #endif | ||
359 | if ((guest_efer & ~ignore_bits) == (host_efer & ~ignore_bits)) | ||
360 | return; | ||
356 | 361 | ||
357 | trans_efer = vmx->host_msrs[efer_offset].data; | 362 | vmx->host_state.guest_efer_loaded = 1; |
358 | trans_efer &= ~EFER_SAVE_RESTORE_BITS; | 363 | guest_efer &= ~ignore_bits; |
359 | trans_efer |= msr_efer_save_restore_bits(vmx->guest_msrs[efer_offset]); | 364 | guest_efer |= host_efer & ignore_bits; |
360 | wrmsrl(MSR_EFER, trans_efer); | 365 | wrmsrl(MSR_EFER, guest_efer); |
361 | vmx->vcpu.stat.efer_reload++; | 366 | vmx->vcpu.stat.efer_reload++; |
362 | } | 367 | } |
363 | 368 | ||
369 | static void reload_host_efer(struct vcpu_vmx *vmx) | ||
370 | { | ||
371 | if (vmx->host_state.guest_efer_loaded) { | ||
372 | vmx->host_state.guest_efer_loaded = 0; | ||
373 | load_msrs(vmx->host_msrs + vmx->msr_offset_efer, 1); | ||
374 | } | ||
375 | } | ||
376 | |||
364 | static void vmx_save_host_state(struct kvm_vcpu *vcpu) | 377 | static void vmx_save_host_state(struct kvm_vcpu *vcpu) |
365 | { | 378 | { |
366 | struct vcpu_vmx *vmx = to_vmx(vcpu); | 379 | struct vcpu_vmx *vmx = to_vmx(vcpu); |
@@ -406,8 +419,7 @@ static void vmx_save_host_state(struct kvm_vcpu *vcpu) | |||
406 | } | 419 | } |
407 | #endif | 420 | #endif |
408 | load_msrs(vmx->guest_msrs, vmx->save_nmsrs); | 421 | load_msrs(vmx->guest_msrs, vmx->save_nmsrs); |
409 | if (msr_efer_need_save_restore(vmx)) | 422 | load_transition_efer(vmx); |
410 | load_transition_efer(vmx); | ||
411 | } | 423 | } |
412 | 424 | ||
413 | static void vmx_load_host_state(struct vcpu_vmx *vmx) | 425 | static void vmx_load_host_state(struct vcpu_vmx *vmx) |
@@ -436,8 +448,7 @@ static void vmx_load_host_state(struct vcpu_vmx *vmx) | |||
436 | reload_tss(); | 448 | reload_tss(); |
437 | save_msrs(vmx->guest_msrs, vmx->save_nmsrs); | 449 | save_msrs(vmx->guest_msrs, vmx->save_nmsrs); |
438 | load_msrs(vmx->host_msrs, vmx->save_nmsrs); | 450 | load_msrs(vmx->host_msrs, vmx->save_nmsrs); |
439 | if (msr_efer_need_save_restore(vmx)) | 451 | reload_host_efer(vmx); |
440 | load_msrs(vmx->host_msrs + vmx->msr_offset_efer, 1); | ||
441 | } | 452 | } |
442 | 453 | ||
443 | /* | 454 | /* |
@@ -727,8 +738,10 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) | |||
727 | #ifdef CONFIG_X86_64 | 738 | #ifdef CONFIG_X86_64 |
728 | case MSR_EFER: | 739 | case MSR_EFER: |
729 | ret = kvm_set_msr_common(vcpu, msr_index, data); | 740 | ret = kvm_set_msr_common(vcpu, msr_index, data); |
730 | if (vmx->host_state.loaded) | 741 | if (vmx->host_state.loaded) { |
742 | reload_host_efer(vmx); | ||
731 | load_transition_efer(vmx); | 743 | load_transition_efer(vmx); |
744 | } | ||
732 | break; | 745 | break; |
733 | case MSR_FS_BASE: | 746 | case MSR_FS_BASE: |
734 | vmcs_writel(GUEST_FS_BASE, data); | 747 | vmcs_writel(GUEST_FS_BASE, data); |