x86emul: support TBM insns Signed-off-by: Jan Beulich --- v2: Use decode_vex_gpr(). Introduce X86EMUL_OPC_XOP*(). --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -1244,6 +1244,234 @@ int main(int argc, char **argv) printf("okay\n"); } + printf("%-40s", "Testing bextr $0x0a03,(%ecx),%ebx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(bextr_imm); +#ifdef __x86_64__ + decl_insn(bextr64_imm); +#endif + + asm volatile ( put_insn(bextr_imm, "bextr $0x0a03, (%0), %%ebx") + :: "c" (NULL) ); + set_insn(bextr_imm); + + *res = 0xfedcba98; + regs.ecx = (unsigned long)res; + regs.eflags = 0xa43; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ebx != ((*res >> 3) & 0x3ff) || + *res != 0xfedcba98 || + (regs.eflags & 0xf6b) != 0x202 || !check_eip(bextr_imm) ) + goto fail; + printf("okay\n"); +#ifdef __x86_64__ + printf("%-40s", "Testing bextr $0x211e,(%r10),%r11..."); + + asm volatile ( put_insn(bextr64_imm, "bextr $0x211e, (%r10), %r11") ); + set_insn(bextr64_imm); + + res[0] = 0x76543210; + res[1] = 0xfedcba98; + regs.r10 = (unsigned long)res; + regs.eflags = 0xa43; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || + regs.r11 != (((unsigned long)(res[1] << 1) << 1) | + (res[0] >> 30)) || + res[0] != 0x76543210 || res[1] != 0xfedcba98 || + (regs.eflags & 0xf6b) != 0x202 || !check_eip(bextr64_imm) ) + goto fail; + printf("okay\n"); +#endif + } + else + printf("skipped\n"); + + res[0] = 0xfedcba98; + res[1] = 0x01234567; + regs.edx = (unsigned long)res; + + printf("%-40s", "Testing blcfill 4(%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blcfill); + + asm volatile ( put_insn(blcfill, "blcfill 4(%0), %%ecx") + :: "d" (NULL) ); + set_insn(blcfill); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[1] != 0x01234567 || + regs.ecx != ((res[1] + 1) & res[1]) || + (regs.eflags & 0xfeb) != 0x202 || !check_eip(blcfill) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blci 4(%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blci); + + asm volatile ( put_insn(blci, "blci 4(%0), %%ecx") + :: "d" (NULL) ); + set_insn(blci); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[1] != 0x01234567 || + regs.ecx != (~(res[1] + 1) | res[1]) || + (regs.eflags & 0xfeb) != 0x282 || !check_eip(blci) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blcic 4(%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blcic); + + asm volatile ( put_insn(blcic, "blcic 4(%0), %%ecx") + :: "d" (NULL) ); + set_insn(blcic); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[1] != 0x01234567 || + regs.ecx != ((res[1] + 1) & ~res[1]) || + (regs.eflags & 0xfeb) != 0x202 || !check_eip(blcic) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blcmsk 4(%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blcmsk); + + asm volatile ( put_insn(blcmsk, "blcmsk 4(%0), %%ecx") + :: "d" (NULL) ); + set_insn(blcmsk); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[1] != 0x01234567 || + regs.ecx != ((res[1] + 1) ^ res[1]) || + (regs.eflags & 0xfeb) != 0x202 || !check_eip(blcmsk) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blcs 4(%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blcs); + + asm volatile ( put_insn(blcs, "blcs 4(%0), %%ecx") + :: "d" (NULL) ); + set_insn(blcs); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[1] != 0x01234567 || + regs.ecx != ((res[1] + 1) | res[1]) || + (regs.eflags & 0xfeb) != 0x202 || !check_eip(blcs) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blsfill (%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blsfill); + + asm volatile ( put_insn(blsfill, "blsfill (%0), %%ecx") + :: "d" (NULL) ); + set_insn(blsfill); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[0] != 0xfedcba98 || + regs.ecx != ((res[0] - 1) | res[0]) || + (regs.eflags & 0xfeb) != 0x282 || !check_eip(blsfill) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing blsic (%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(blsic); + + asm volatile ( put_insn(blsic, "blsic (%0), %%ecx") + :: "d" (NULL) ); + set_insn(blsic); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[0] != 0xfedcba98 || + regs.ecx != ((res[0] - 1) | ~res[0]) || + (regs.eflags & 0xfeb) != 0x282 || !check_eip(blsic) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing t1mskc 4(%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(t1mskc); + + asm volatile ( put_insn(t1mskc, "t1mskc 4(%0), %%ecx") + :: "d" (NULL) ); + set_insn(t1mskc); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[1] != 0x01234567 || + regs.ecx != ((res[1] + 1) | ~res[1]) || + (regs.eflags & 0xfeb) != 0x282 || !check_eip(t1mskc) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + + printf("%-40s", "Testing tzmsk (%edx),%ecx..."); + if ( stack_exec && cpu_has_tbm ) + { + decl_insn(tzmsk); + + asm volatile ( put_insn(tzmsk, "tzmsk (%0), %%ecx") + :: "d" (NULL) ); + set_insn(tzmsk); + + regs.eflags = 0xac3; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || res[0] != 0xfedcba98 || + regs.ecx != ((res[0] - 1) & ~res[0]) || + (regs.eflags & 0xfeb) != 0x202 || !check_eip(tzmsk) ) + goto fail; + printf("okay\n"); + } + else + printf("skipped\n"); + printf("%-40s", "Testing movq %mm3,(%ecx)..."); if ( stack_exec && cpu_has_mmx ) { --- a/tools/tests/x86_emulator/x86_emulate.h +++ b/tools/tests/x86_emulator/x86_emulate.h @@ -125,6 +125,12 @@ static inline uint64_t xgetbv(uint32_t x (res.b & (1U << 8)) != 0; \ }) +#define cpu_has_tbm ({ \ + struct cpuid_leaf res; \ + emul_test_cpuid(0x80000001, 0, &res, NULL); \ + (res.c & (1U << 21)) != 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 @@ -1355,6 +1355,7 @@ static bool vcpu_has( #define vcpu_has_cr8_legacy() vcpu_has(0x80000001, ECX, 4, ctxt, ops) #define vcpu_has_lzcnt() vcpu_has(0x80000001, ECX, 5, ctxt, ops) #define vcpu_has_misalignsse() vcpu_has(0x80000001, ECX, 7, ctxt, ops) +#define vcpu_has_tbm() vcpu_has(0x80000001, ECX, 21, ctxt, ops) #define vcpu_has_bmi1() vcpu_has( 7, EBX, 3, ctxt, ops) #define vcpu_has_hle() vcpu_has( 7, EBX, 4, ctxt, ops) #define vcpu_has_bmi2() vcpu_has( 7, EBX, 8, ctxt, ops) @@ -6017,6 +6018,84 @@ x86_emulate( asm ( "rorl %b1,%k0" : "=g" (dst.val) : "c" (imm1), "0" (src.val) ); break; + case X86EMUL_OPC_XOP(09, 0x01): /* XOP Grp1 */ + switch ( modrm_reg & 7 ) + { + case 1: /* blcfill r/m,r */ + case 2: /* blsfill r/m,r */ + case 3: /* blcs r/m,r */ + case 4: /* tzmsk r/m,r */ + case 5: /* blcic r/m,r */ + case 6: /* blsic r/m,r */ + case 7: /* t1mskc r/m,r */ + host_and_vcpu_must_have(tbm); + break; + default: + goto cannot_emulate; + } + + xop_09_rm_rv: + { + uint8_t *buf = get_stub(stub); + typeof(vex) *pxop = container_of(buf + 1, typeof(vex), raw[0]); + + generate_exception_if(vex.l, EXC_UD); + + buf[0] = 0x8f; + *pxop = vex; + pxop->b = 1; + pxop->r = 1; + pxop->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([dst] "=&a" (dst.val), "c" (&src.val)); + + put_stub(stub); + break; + } + + case X86EMUL_OPC_XOP(09, 0x02): /* XOP Grp2 */ + switch ( modrm_reg & 7 ) + { + case 1: /* blcmsk r/m,r */ + case 6: /* blci r/m,r */ + host_and_vcpu_must_have(tbm); + goto xop_09_rm_rv; + } + goto cannot_emulate; + + case X86EMUL_OPC_XOP(0a, 0x10): /* bextr imm,r/m,r */ + { + uint8_t *buf = get_stub(stub); + typeof(vex) *pxop = container_of(buf + 1, typeof(vex), raw[0]); + + host_and_vcpu_must_have(tbm); + generate_exception_if(vex.l || vex.reg != 0xf, EXC_UD); + + if ( ea.type == OP_REG ) + src.val = *ea.reg; + else if ( (rc = read_ulong(ea.mem.seg, ea.mem.off, &src.val, op_bytes, + ctxt, ops)) != X86EMUL_OKAY ) + goto done; + + buf[0] = 0x8f; + *pxop = vex; + pxop->b = 1; + pxop->r = 1; + buf[3] = b; + buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */ + *(uint32_t *)(buf + 5) = imm1; + buf[9] = 0xc3; + + emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val)); + + put_stub(stub); + break; + } + default: goto cannot_emulate; } --- a/xen/arch/x86/x86_emulate/x86_emulate.h +++ b/xen/arch/x86/x86_emulate/x86_emulate.h @@ -564,6 +564,11 @@ struct x86_emulate_ctxt # define X86EMUL_OPC_EVEX_F2(ext, byte) \ (X86EMUL_OPC_F2(ext, byte) | X86EMUL_OPC_EVEX_) +#define X86EMUL_OPC_XOP(ext, byte) X86EMUL_OPC(0x8f##ext, byte) +#define X86EMUL_OPC_XOP_66(ext, byte) X86EMUL_OPC_66(0x8f##ext, byte) +#define X86EMUL_OPC_XOP_F3(ext, byte) X86EMUL_OPC_F3(0x8f##ext, byte) +#define X86EMUL_OPC_XOP_F2(ext, byte) X86EMUL_OPC_F2(0x8f##ext, byte) + struct x86_emulate_stub { union { void (*func)(void); --- a/xen/include/asm-x86/cpufeature.h +++ b/xen/include/asm-x86/cpufeature.h @@ -74,6 +74,7 @@ #define cpu_has_eist boot_cpu_has(X86_FEATURE_EIST) #define cpu_has_hypervisor boot_cpu_has(X86_FEATURE_HYPERVISOR) #define cpu_has_cmp_legacy boot_cpu_has(X86_FEATURE_CMP_LEGACY) +#define cpu_has_tbm boot_cpu_has(X86_FEATURE_TBM) enum _cache_type { CACHE_TYPE_NULL = 0,