diff options
author | Avi Kivity <avi@redhat.com> | 2010-04-08 11:19:35 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-04-26 10:48:05 -0400 |
commit | 01b0fb7b85b4b8a3027e5cc7663ae8b2193b1104 (patch) | |
tree | d3ceb1eb0b57c4a68f280c0d051c9ca05e573be4 /arch | |
parent | ec861e75183fa0049917cbfa44d8a17e7ece7b4f (diff) |
KVM: VMX: Save/restore rflags.vm correctly in real mode
(Cherry-picked from commit 78ac8b47c566dd6177a3b9b291b756ccb70670b7)
Currently we set eflags.vm unconditionally when entering real mode emulation
through virtual-8086 mode, and clear it unconditionally when we enter protected
mode. The means that the following sequence
KVM_SET_REGS (rflags.vm=1)
KVM_SET_SREGS (cr0.pe=1)
Ends up with rflags.vm clear due to KVM_SET_SREGS triggering enter_pmode().
Fix by shadowing rflags.vm (and rflags.iopl) correctly while in real mode:
reads and writes to those bits access a shadow register instead of the actual
register.
Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kvm/vmx.c | 24 |
1 files changed, 15 insertions, 9 deletions
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 85904fa41f03..3acbe194e525 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -61,6 +61,8 @@ module_param_named(unrestricted_guest, | |||
61 | static int __read_mostly emulate_invalid_guest_state = 0; | 61 | static int __read_mostly emulate_invalid_guest_state = 0; |
62 | module_param(emulate_invalid_guest_state, bool, S_IRUGO); | 62 | module_param(emulate_invalid_guest_state, bool, S_IRUGO); |
63 | 63 | ||
64 | #define RMODE_GUEST_OWNED_EFLAGS_BITS (~(X86_EFLAGS_IOPL | X86_EFLAGS_VM)) | ||
65 | |||
64 | /* | 66 | /* |
65 | * These 2 parameters are used to config the controls for Pause-Loop Exiting: | 67 | * These 2 parameters are used to config the controls for Pause-Loop Exiting: |
66 | * ple_gap: upper bound on the amount of time between two successive | 68 | * ple_gap: upper bound on the amount of time between two successive |
@@ -115,7 +117,7 @@ struct vcpu_vmx { | |||
115 | } host_state; | 117 | } host_state; |
116 | struct { | 118 | struct { |
117 | int vm86_active; | 119 | int vm86_active; |
118 | u8 save_iopl; | 120 | ulong save_rflags; |
119 | struct kvm_save_segment { | 121 | struct kvm_save_segment { |
120 | u16 selector; | 122 | u16 selector; |
121 | unsigned long base; | 123 | unsigned long base; |
@@ -787,18 +789,23 @@ static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu) | |||
787 | 789 | ||
788 | static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu) | 790 | static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu) |
789 | { | 791 | { |
790 | unsigned long rflags; | 792 | unsigned long rflags, save_rflags; |
791 | 793 | ||
792 | rflags = vmcs_readl(GUEST_RFLAGS); | 794 | rflags = vmcs_readl(GUEST_RFLAGS); |
793 | if (to_vmx(vcpu)->rmode.vm86_active) | 795 | if (to_vmx(vcpu)->rmode.vm86_active) { |
794 | rflags &= ~(unsigned long)(X86_EFLAGS_IOPL | X86_EFLAGS_VM); | 796 | rflags &= RMODE_GUEST_OWNED_EFLAGS_BITS; |
797 | save_rflags = to_vmx(vcpu)->rmode.save_rflags; | ||
798 | rflags |= save_rflags & ~RMODE_GUEST_OWNED_EFLAGS_BITS; | ||
799 | } | ||
795 | return rflags; | 800 | return rflags; |
796 | } | 801 | } |
797 | 802 | ||
798 | static void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags) | 803 | static void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags) |
799 | { | 804 | { |
800 | if (to_vmx(vcpu)->rmode.vm86_active) | 805 | if (to_vmx(vcpu)->rmode.vm86_active) { |
806 | to_vmx(vcpu)->rmode.save_rflags = rflags; | ||
801 | rflags |= X86_EFLAGS_IOPL | X86_EFLAGS_VM; | 807 | rflags |= X86_EFLAGS_IOPL | X86_EFLAGS_VM; |
808 | } | ||
802 | vmcs_writel(GUEST_RFLAGS, rflags); | 809 | vmcs_writel(GUEST_RFLAGS, rflags); |
803 | } | 810 | } |
804 | 811 | ||
@@ -1431,8 +1438,8 @@ static void enter_pmode(struct kvm_vcpu *vcpu) | |||
1431 | vmcs_write32(GUEST_TR_AR_BYTES, vmx->rmode.tr.ar); | 1438 | vmcs_write32(GUEST_TR_AR_BYTES, vmx->rmode.tr.ar); |
1432 | 1439 | ||
1433 | flags = vmcs_readl(GUEST_RFLAGS); | 1440 | flags = vmcs_readl(GUEST_RFLAGS); |
1434 | flags &= ~(X86_EFLAGS_IOPL | X86_EFLAGS_VM); | 1441 | flags &= RMODE_GUEST_OWNED_EFLAGS_BITS; |
1435 | flags |= (vmx->rmode.save_iopl << IOPL_SHIFT); | 1442 | flags |= vmx->rmode.save_rflags & ~RMODE_GUEST_OWNED_EFLAGS_BITS; |
1436 | vmcs_writel(GUEST_RFLAGS, flags); | 1443 | vmcs_writel(GUEST_RFLAGS, flags); |
1437 | 1444 | ||
1438 | vmcs_writel(GUEST_CR4, (vmcs_readl(GUEST_CR4) & ~X86_CR4_VME) | | 1445 | vmcs_writel(GUEST_CR4, (vmcs_readl(GUEST_CR4) & ~X86_CR4_VME) | |
@@ -1501,8 +1508,7 @@ static void enter_rmode(struct kvm_vcpu *vcpu) | |||
1501 | vmcs_write32(GUEST_TR_AR_BYTES, 0x008b); | 1508 | vmcs_write32(GUEST_TR_AR_BYTES, 0x008b); |
1502 | 1509 | ||
1503 | flags = vmcs_readl(GUEST_RFLAGS); | 1510 | flags = vmcs_readl(GUEST_RFLAGS); |
1504 | vmx->rmode.save_iopl | 1511 | vmx->rmode.save_rflags = flags; |
1505 | = (flags & X86_EFLAGS_IOPL) >> IOPL_SHIFT; | ||
1506 | 1512 | ||
1507 | flags |= X86_EFLAGS_IOPL | X86_EFLAGS_VM; | 1513 | flags |= X86_EFLAGS_IOPL | X86_EFLAGS_VM; |
1508 | 1514 | ||