x86emul: support BMI1 insns Signed-off-by: Jan Beulich --- v2: Introduce decode_vex_gpr(). --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -892,6 +892,133 @@ int main(int argc, char **argv) #define check_eip(which) (regs.eip == (unsigned long)(which) + \ (unsigned long)which##_len) + printf("%-40s", "Testing andn (%edx),%ecx,%ebx..."); + if ( stack_exec && cpu_has_bmi1 ) + { + decl_insn(andn); + + asm volatile ( put_insn(andn, "andn (%0), %%ecx, %%ebx") + :: "d" (NULL) ); + set_insn(andn); + + *res = 0xfedcba98; + regs.ecx = 0xcccc3333; + regs.edx = (unsigned long)res; + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ebx != 0x32108888 || + regs.ecx != 0xcccc3333 || *res != 0xfedcba98 || + (regs.eflags & 0xfeb) != 0x202 || !check_eip(andn) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing bextr %edx,(%ecx),%ebx..."); + if ( stack_exec && cpu_has_bmi1 ) + { + decl_insn(bextr); +#ifdef __x86_64__ + decl_insn(bextr64); +#endif + + asm volatile ( put_insn(bextr, "bextr %%edx, (%0), %%ebx") + :: "c" (NULL) ); + set_insn(bextr); + + regs.ecx = (unsigned long)res; + regs.edx = 0x0a03; + regs.eflags = 0xa43; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ebx != ((*res >> 3) & 0x3ff) || + regs.edx != 0x0a03 || *res != 0xfedcba98 || + (regs.eflags & 0xf6b) != 0x202 || !check_eip(bextr) ) + goto fail; + printf("okay\n"); +#ifdef __x86_64__ + printf("%-40s", "Testing bextr %r9,(%r10),%r11..."); + + asm volatile ( put_insn(bextr64, "bextr %r9, (%r10), %r11") ); + set_insn(bextr64); + + res[0] = 0x76543210; + res[1] = 0xfedcba98; + regs.r10 = (unsigned long)res; + regs.r9 = 0x211e; + regs.eflags = 0xa43; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.r9 != 0x211e || + regs.r11 != (((unsigned long)(res[1] << 1) << 1) | + (res[0] >> 30)) || + res[0] != 0x76543210 || res[1] != 0xfedcba98 || + (regs.eflags & 0xf6b) != 0x202 || !check_eip(bextr64) ) + goto fail; + printf("okay\n"); +#endif + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blsi (%edx),%ecx..."); + if ( stack_exec && cpu_has_bmi1 ) + { + decl_insn(blsi); + + asm volatile ( put_insn(blsi, "blsi (%0), %%ecx") + :: "d" (NULL) ); + set_insn(blsi); + + *res = 0xfedcba98; + regs.edx = (unsigned long)res; + regs.eflags = 0xac2; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ecx != 8 || *res != 0xfedcba98 || + (regs.eflags & 0xf6b) != 0x203 || !check_eip(blsi) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blsmsk (%edx),%ecx..."); + if ( stack_exec && cpu_has_bmi1 ) + { + decl_insn(blsmsk); + + asm volatile ( put_insn(blsmsk, "blsmsk (%0), %%ecx") + :: "d" (NULL) ); + set_insn(blsmsk); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ecx != 0xf || *res != 0xfedcba98 || + (regs.eflags & 0xf6b) != 0x202 || !check_eip(blsmsk) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blsr (%edx),%ecx..."); + if ( stack_exec && cpu_has_bmi1 ) + { + decl_insn(blsr); + + asm volatile ( put_insn(blsr, "blsr (%0), %%ecx") + :: "d" (NULL) ); + set_insn(blsr); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ecx != 0xfedcba90 || + (regs.eflags & 0xf6b) != 0x202 || !check_eip(blsr) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + printf("%-40s", "Testing adcx/adox ..."); { static const unsigned int data[] = { --- a/tools/tests/x86_emulator/x86_emulate.h +++ b/tools/tests/x86_emulator/x86_emulate.h @@ -113,6 +113,12 @@ static inline uint64_t xgetbv(uint32_t x (res.b & (1U << 5)) != 0; \ }) +#define cpu_has_bmi1 ({ \ + struct cpuid_leaf res; \ + emul_test_cpuid(7, 0, &res, NULL); \ + (res.b & (1U << 3)) != 0; \ +}) + int emul_test_cpuid( uint32_t leaf, uint32_t subleaf, --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -676,6 +676,16 @@ do{ asm volatile ( #define __emulate_1op_8byte(_op, _dst, _eflags) #endif /* __i386__ */ +#define emulate_stub(dst, src...) do { \ + unsigned long tmp; \ + asm volatile ( _PRE_EFLAGS("[efl]", "[msk]", "[tmp]") \ + "call *%[stub];" \ + _POST_EFLAGS("[efl]", "[msk]", "[tmp]") \ + : dst, [tmp] "=&r" (tmp), [efl] "+g" (_regs._eflags) \ + : [stub] "r" (stub.func), \ + [msk] "i" (EFLAGS_MASK), ## src ); \ +} while (0) + /* Fetch next part of the instruction being emulated. */ #define insn_fetch_bytes(_size) \ ({ unsigned long _x = 0, _ip = state->ip; \ @@ -1687,6 +1697,12 @@ decode_register( return p; } +static void *decode_vex_gpr(unsigned int vex_reg, struct cpu_user_regs *regs, + const struct x86_emulate_ctxt *ctxt) +{ + return decode_register(~vex_reg & (mode_64bit() ? 0xf : 7), regs, 0); +} + static bool is_aligned(enum x86_segment seg, unsigned long offs, unsigned int size, struct x86_emulate_ctxt *ctxt, const struct x86_emulate_ops *ops) @@ -2295,7 +2311,10 @@ x86_decode( } } else + { + ASSERT(op_bytes == 4); vex.b = 1; + } switch ( b ) { case 0x62: @@ -5866,6 +5885,65 @@ x86_emulate( break; #endif + case X86EMUL_OPC_VEX(0x0f38, 0xf2): /* andn r/m,r,r */ + case X86EMUL_OPC_VEX(0x0f38, 0xf7): /* bextr r,r/m,r */ + { + uint8_t *buf = get_stub(stub); + typeof(vex) *pvex = container_of(buf + 1, typeof(vex), raw[0]); + + host_and_vcpu_must_have(bmi1); + generate_exception_if(vex.l, EXC_UD); + + buf[0] = 0xc4; + *pvex = vex; + pvex->b = 1; + pvex->r = 1; + pvex->reg = ~0; /* rAX */ + buf[3] = b; + buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */ + buf[5] = 0xc3; + + src.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); + emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val), "a" (*src.reg)); + + put_stub(stub); + break; + } + + case X86EMUL_OPC_VEX(0x0f38, 0xf3): /* Grp 17 */ + { + uint8_t *buf = get_stub(stub); + typeof(vex) *pvex = container_of(buf + 1, typeof(vex), raw[0]); + + switch ( modrm_reg & 7 ) + { + case 1: /* blsr r,r/m */ + case 2: /* blsmsk r,r/m */ + case 3: /* blsi r,r/m */ + host_and_vcpu_must_have(bmi1); + break; + default: + goto cannot_emulate; + } + + generate_exception_if(vex.l, EXC_UD); + + buf[0] = 0xc4; + *pvex = vex; + pvex->b = 1; + pvex->r = 1; + pvex->reg = ~0; /* rAX */ + buf[3] = b; + buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */ + buf[5] = 0xc3; + + dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); + emulate_stub("=&a" (dst.val), "c" (&src.val)); + + put_stub(stub); + break; + } + case X86EMUL_OPC_66(0x0f38, 0xf6): /* adcx r/m,r */ case X86EMUL_OPC_F3(0x0f38, 0xf6): /* adox r/m,r */ { --- a/xen/include/asm-x86/cpufeature.h +++ b/xen/include/asm-x86/cpufeature.h @@ -57,6 +57,7 @@ #define cpu_has_xsave boot_cpu_has(X86_FEATURE_XSAVE) #define cpu_has_avx boot_cpu_has(X86_FEATURE_AVX) #define cpu_has_lwp boot_cpu_has(X86_FEATURE_LWP) +#define cpu_has_bmi1 boot_cpu_has(X86_FEATURE_BMI1) #define cpu_has_mpx boot_cpu_has(X86_FEATURE_MPX) #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) #define cpu_has_rdtscp boot_cpu_has(X86_FEATURE_RDTSCP)