diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 868c26036d..4664264b2f 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -1193,6 +1193,14 @@ int arch_set_info_guest( if ( !__addr_ok(c.nat->ldt_base) ) return -EINVAL; + + /* + * IRET in Long Mode discards EFLAGS.VM, but in FRED mode ERET + * cares that it is zero. + * + * Guests can't see FRED, so emulate IRET behaviour. + */ + c.nat->user_regs.rflags &= ~X86_EFLAGS_VM; } #ifdef CONFIG_COMPAT else @@ -1205,6 +1213,18 @@ int arch_set_info_guest( for ( i = 0; i < ARRAY_SIZE(c.cmp->trap_ctxt); i++ ) fixup_guest_code_selector(d, c.cmp->trap_ctxt[i].cs); + + /* + * Under 32bit Xen, PV guests could really use vm86 mode. Under + * 64bit Xen, vm86 mode can't be entered even by PV32 guests. + * + * For backwards compatibility, compat HYPERCALL_iret will arrange + * to deliver #GP at the target of the IRET rather than to fail + * the IRET itself, but we can't arrange for the same behaviour + * here. Reject the hypercall as the next best option. + */ + if ( c.cmp->user_regs.eflags & X86_EFLAGS_VM ) + return -EINVAL; } #endif @@ -1244,7 +1264,7 @@ int arch_set_info_guest( v->arch.user_regs.rax = c.nat->user_regs.rax; v->arch.user_regs.rip = c.nat->user_regs.rip; v->arch.user_regs.cs = c.nat->user_regs.cs; - v->arch.user_regs.rflags = c.nat->user_regs.rflags; + v->arch.user_regs.rflags = (c.nat->user_regs.rflags & X86_EFLAGS_ALL) | X86_EFLAGS_MBS; v->arch.user_regs.rsp = c.nat->user_regs.rsp; v->arch.user_regs.ss = c.nat->user_regs.ss; v->arch.pv.es = c.nat->user_regs.es; @@ -1268,7 +1288,7 @@ int arch_set_info_guest( v->arch.user_regs.eax = c.cmp->user_regs.eax; v->arch.user_regs.eip = c.cmp->user_regs.eip; v->arch.user_regs.cs = c.cmp->user_regs.cs; - v->arch.user_regs.eflags = c.cmp->user_regs.eflags; + v->arch.user_regs.eflags = (c.cmp->user_regs.eflags & X86_EFLAGS_ALL) | X86_EFLAGS_MBS; v->arch.user_regs.esp = c.cmp->user_regs.esp; v->arch.user_regs.ss = c.cmp->user_regs.ss; v->arch.pv.es = c.cmp->user_regs.es; diff --git a/xen/arch/x86/hvm/domain.c b/xen/arch/x86/hvm/domain.c index 155d61db13..a0e811ea47 100644 --- a/xen/arch/x86/hvm/domain.c +++ b/xen/arch/x86/hvm/domain.c @@ -194,7 +194,7 @@ int arch_set_info_hvm_guest(struct vcpu *v, const struct vcpu_hvm_context *ctx) uregs->rsi = regs->esi; uregs->rdi = regs->edi; uregs->rip = regs->eip; - uregs->rflags = regs->eflags; + uregs->rflags = (regs->eflags & X86_EFLAGS_ALL) | X86_EFLAGS_MBS; v->arch.hvm.guest_cr[0] = regs->cr0; v->arch.hvm.guest_cr[3] = regs->cr3; @@ -245,7 +245,7 @@ int arch_set_info_hvm_guest(struct vcpu *v, const struct vcpu_hvm_context *ctx) uregs->rsi = regs->rsi; uregs->rdi = regs->rdi; uregs->rip = regs->rip; - uregs->rflags = regs->rflags; + uregs->rflags = (regs->rflags & X86_EFLAGS_ALL) | X86_EFLAGS_MBS; v->arch.hvm.guest_cr[0] = regs->cr0; v->arch.hvm.guest_cr[3] = regs->cr3; diff --git a/xen/arch/x86/include/asm/x86-defns.h b/xen/arch/x86/include/asm/x86-defns.h index 0a0ba83de7..edeb0b4ff9 100644 --- a/xen/arch/x86/include/asm/x86-defns.h +++ b/xen/arch/x86/include/asm/x86-defns.h @@ -27,6 +27,13 @@ (X86_EFLAGS_CF | X86_EFLAGS_PF | X86_EFLAGS_AF | \ X86_EFLAGS_ZF | X86_EFLAGS_SF | X86_EFLAGS_OF) +#define X86_EFLAGS_ALL \ + (X86_EFLAGS_ARITH_MASK | X86_EFLAGS_TF | X86_EFLAGS_IF | \ + X86_EFLAGS_DF | X86_EFLAGS_OF | X86_EFLAGS_IOPL | \ + X86_EFLAGS_NT | X86_EFLAGS_RF | X86_EFLAGS_VM | \ + X86_EFLAGS_AC | X86_EFLAGS_VIF | X86_EFLAGS_VIP | \ + X86_EFLAGS_ID) + /* * Intel CPU flags in CR0 */ diff --git a/xen/arch/x86/pv/dom0_build.c b/xen/arch/x86/pv/dom0_build.c index 9a11a0a16b..075a3646c2 100644 --- a/xen/arch/x86/pv/dom0_build.c +++ b/xen/arch/x86/pv/dom0_build.c @@ -1024,7 +1024,7 @@ static int __init dom0_construct(const struct boot_domain *bd) regs->rip = parms.virt_entry; regs->rsp = vstack_end; regs->rsi = vstartinfo_start; - regs->eflags = X86_EFLAGS_IF; + regs->eflags = X86_EFLAGS_IF | X86_EFLAGS_MBS; /* * We don't call arch_set_info_guest(), so some initialisation needs doing diff --git a/xen/arch/x86/pv/iret.c b/xen/arch/x86/pv/iret.c index d3a1fb2c68..39ce316b8d 100644 --- a/xen/arch/x86/pv/iret.c +++ b/xen/arch/x86/pv/iret.c @@ -80,8 +80,9 @@ long do_iret(void) regs->rip = iret_saved.rip; regs->cs = iret_saved.cs | 3; /* force guest privilege */ - regs->rflags = ((iret_saved.rflags & ~(X86_EFLAGS_IOPL|X86_EFLAGS_VM)) - | X86_EFLAGS_IF); + regs->rflags = ((iret_saved.rflags & X86_EFLAGS_ALL & + ~(X86_EFLAGS_IOPL | X86_EFLAGS_VM)) | + X86_EFLAGS_IF | X86_EFLAGS_MBS); regs->rsp = iret_saved.rsp; regs->ss = iret_saved.ss | 3; /* force guest privilege */ @@ -143,7 +144,8 @@ int compat_iret(void) if ( VM_ASSIST(v->domain, architectural_iopl) ) v->arch.pv.iopl = eflags & X86_EFLAGS_IOPL; - regs->eflags = (eflags & ~X86_EFLAGS_IOPL) | X86_EFLAGS_IF; + regs->eflags = ((eflags & X86_EFLAGS_ALL & ~X86_EFLAGS_IOPL) | + X86_EFLAGS_IF | X86_EFLAGS_MBS); if ( unlikely(eflags & X86_EFLAGS_VM) ) {