KVM: VMX: Save/restore rflags.vm correctly in real mode
PATCH 1/1 KVM: VMX: Save/restore rflags.vm correctly in real mode
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.
--- arch/x86/kvm/vmx.c | 24 +++++++++++++++--------- 1 files changed, 15 insertions(+), 9 deletions(-)
arch/x86/kvm/vmx.c +15 -9
@@ -76,6 +76,8 @@ module_param(emulate_invalid_guest_state, bool, S_IRUGO);#define KVM_PMODE_VM_CR4_ALWAYS_ON (X86_CR4_PAE | X86_CR4_VMXE)#define KVM_RMODE_VM_CR4_ALWAYS_ON (X86_CR4_VME | X86_CR4_PAE | X86_CR4_VMXE) +#define RMODE_GUEST_OWNED_EFLAGS_BITS (~(X86_EFLAGS_IOPL | X86_EFLAGS_VM)) +/* * These 2 parameters are used to config the controls for Pause-Loop Exiting: * ple_gap: upper bound on the amount of time between two successive
@@ -130,7 +132,7 @@ struct vcpu_vmx { } host_state;struct {int vm86_active; -u8 save_iopl; +ulong save_rflags;struct kvm_save_segment {u16 selector;unsigned long base;
@@ -854,18 +856,23 @@ static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu)static unsigned long vmx_get_rflags (struct kvm_vcpu *vcpu) { -unsigned long rflags; +unsigned long rflags, save_rflags; rflags =vmcs_readl (GUEST_RFLAGS ); -if (to_vmx (vcpu)->rmode.vm86_active) - rflags &= ~(unsigned long )(X86_EFLAGS_IOPL |X86_EFLAGS_VM ); +if (to_vmx (vcpu)->rmode.vm86_active) { + rflags &=RMODE_GUEST_OWNED_EFLAGS_BITS ; + save_rflags =to_vmx (vcpu)->rmode.save_rflags; + rflags |= save_rflags & ~RMODE_GUEST_OWNED_EFLAGS_BITS ; + }return rflags; }static void vmx_set_rflags (struct kvm_vcpu *vcpu,unsigned long rflags) { -if (to_vmx (vcpu)->rmode.vm86_active) +if (to_vmx (vcpu)->rmode.vm86_active) { +to_vmx (vcpu)->rmode.save_rflags = rflags; rflags |=X86_EFLAGS_IOPL |X86_EFLAGS_VM ; + }vmcs_writel (GUEST_RFLAGS , rflags); }
@@ -1519,8 +1526,8 @@ static void enter_pmode(struct kvm_vcpu *vcpu)vmcs_write32 (GUEST_TR_AR_BYTES , vmx->rmode.tr.ar); flags =vmcs_readl (GUEST_RFLAGS ); - flags &= ~(X86_EFLAGS_IOPL |X86_EFLAGS_VM ); - flags |= (vmx->rmode.save_iopl <<IOPL_SHIFT ); + flags &=RMODE_GUEST_OWNED_EFLAGS_BITS ; + flags |= vmx->rmode.save_rflags & ~RMODE_GUEST_OWNED_EFLAGS_BITS ;vmcs_writel (GUEST_RFLAGS , flags); vmcs_writel(GUEST_CR4, (vmcs_readl(GUEST_CR4) & ~X86_CR4_VME) |
@@ -1593,8 +1600,7 @@ static void enter_rmode(struct kvm_vcpu *vcpu)vmcs_write32 (GUEST_TR_AR_BYTES ,0x008b ); flags =vmcs_readl (GUEST_RFLAGS ); - vmx->rmode.save_iopl - = (flags &X86_EFLAGS_IOPL ) >>IOPL_SHIFT ; + vmx->rmode.save_rflags = flags; flags |=X86_EFLAGS_IOPL |X86_EFLAGS_VM ;
Signature · 1 line
1.7.0.4
Quoted 7 lines · Avi Kivity
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)
Quoted 7 lines
Ends up with rflags.vm clear due to
KVM_SET_SREGStriggeringenter_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 <[email protected]>
Tested and applied, thanks.