x86emul: support VME and PVI ... affecting POPF, CLI, and STI. Signed-off-by: Jan Beulich --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -433,6 +433,8 @@ typedef union { #define CR0_EM (1<<2) #define CR0_TS (1<<3) +#define CR4_VME (1<<0) +#define CR4_PVI (1<<1) #define CR4_TSD (1<<2) #define CR4_OSFXSR (1<<9) #define CR4_OSXMMEXCPT (1<<10) @@ -1178,6 +1180,15 @@ _mode_iopl( fail_if(_iopl < 0); \ _iopl; \ }) +#define mode_pvi() ({ \ + unsigned long cr4 = 0; \ + if ( ops->read_cr && get_cpl(ctxt, ops) == 3 ) \ + { \ + rc = ops->read_cr(4, &cr4, ctxt); \ + if ( rc != X86EMUL_OKAY ) goto done; \ + } \ + !!(cr4 & (_regs._eflags & EFLG_VM ? CR4_VME : CR4_PVI)); \ +}) static int ioport_access_check( unsigned int first_port, @@ -3273,12 +3284,22 @@ x86_emulate( case 0x9d: /* popf */ { uint32_t mask = EFLG_VIP | EFLG_VIF | EFLG_VM; + unsigned long cr4 = 0; if ( !mode_ring0() ) { - generate_exception_if((_regs._eflags & EFLG_VM) && - MASK_EXTR(_regs._eflags, EFLG_IOPL) != 3, - EXC_GP, 0); + if ( _regs._eflags & EFLG_VM ) + { + if ( op_bytes == 2 && ops->read_cr ) + { + rc = ops->read_cr(4, &cr4, ctxt); + if ( rc != X86EMUL_OKAY ) + goto done; + } + generate_exception_if(!(cr4 & CR4_VME) && + MASK_EXTR(_regs._eflags, EFLG_IOPL) != 3, + EXC_GP, 0); + } mask |= EFLG_IOPL; if ( !mode_iopl() ) mask |= EFLG_IF; @@ -3290,7 +3311,20 @@ x86_emulate( &dst.val, op_bytes, ctxt, ops)) != 0 ) goto done; if ( op_bytes == 2 ) + { dst.val = (uint16_t)dst.val | (_regs._eflags & 0xffff0000u); + if ( cr4 & CR4_VME ) + { + if ( dst.val & EFLG_IF ) + { + generate_exception_if(_regs._eflags & EFLG_VIP, EXC_GP, 0); + dst.val |= EFLG_VIF; + } + else + dst.val &= ~EFLG_VIF; + mask &= ~EFLG_VIF; + } + } dst.val &= EFLAGS_MODIFIABLE; _regs._eflags &= mask; _regs._eflags |= (dst.val & ~mask) | EFLG_MBS; @@ -4397,16 +4431,29 @@ x86_emulate( break; case 0xfa: /* cli */ - generate_exception_if(!mode_iopl(), EXC_GP, 0); - _regs._eflags &= ~EFLG_IF; + if ( mode_iopl() ) + _regs._eflags &= ~EFLG_IF; + else + { + generate_exception_if(!mode_pvi(), EXC_GP, 0); + _regs._eflags &= ~EFLG_VIF; + } break; case 0xfb: /* sti */ - generate_exception_if(!mode_iopl(), EXC_GP, 0); - if ( !(_regs._eflags & EFLG_IF) ) + if ( mode_iopl() ) { + if ( !(_regs._eflags & EFLG_IF) ) + ctxt->retire.sti = true; _regs._eflags |= EFLG_IF; - ctxt->retire.sti = true; + } + else + { + generate_exception_if((_regs._eflags & EFLG_VIP) || !mode_pvi(), + EXC_GP, 0); + if ( !(_regs._eflags & EFLG_VIF) ) + ctxt->retire.sti = true; + _regs._eflags |= EFLG_VIF; } break;