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. Signed-off-by: Avi Kivity <avi@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
This commit is contained in:
parent
114be429c8
commit
78ac8b47c5
|
@ -77,6 +77,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
|
||||
|
@ -131,7 +133,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;
|
||||
|
@ -818,18 +820,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);
|
||||
}
|
||||
|
||||
|
@ -1483,8 +1490,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) |
|
||||
|
@ -1557,8 +1564,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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue