x86emul: support POPCNT Signed-off-by: Jan Beulich --- TBD: Alternative code needed for binutils < 2.18? --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -684,6 +684,52 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); + printf("%-40s", "Testing popcnt (%edx),%cx..."); + if ( cpu_has_popcnt ) + { + instr[0] = 0x66; instr[1] = 0xf3; + instr[2] = 0x0f; instr[3] = 0xb8; instr[4] = 0x0a; + + *res = 0xfedcba98; + regs.edx = (unsigned long)res; + regs.eflags = 0xac3; + regs.eip = (unsigned long)&instr[0]; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || (uint16_t)regs.ecx != 8 || *res != 0xfedcba98 || + (regs.eflags & 0xfeb) != 0x202 || + (regs.eip != (unsigned long)&instr[5]) ) + goto fail; + printf("okay\n"); + + printf("%-40s", "Testing popcnt (%edx),%ecx..."); + regs.eflags = 0xac3; + regs.eip = (unsigned long)&instr[1]; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ecx != 20 || *res != 0xfedcba98 || + (regs.eflags & 0xfeb) != 0x202 || + (regs.eip != (unsigned long)&instr[5]) ) + goto fail; + printf("okay\n"); + +#ifdef __x86_64__ + printf("%-40s", "Testing popcnt (%rdx),%rcx..."); + instr[0] = 0xf3; + instr[1] = 0x48; + res[1] = 0x12345678; + regs.eflags = 0xac3; + regs.eip = (unsigned long)&instr[0]; + rc = x86_emulate(&ctxt, &emulops); + if ( (rc != X86EMUL_OKAY) || regs.ecx != 33 || + res[0] != 0xfedcba98 || res[1] != 0x12345678 || + (regs.eflags & 0xfeb) != 0x202 || + (regs.eip != (unsigned long)&instr[5]) ) + goto fail; + printf("okay\n"); +#endif + } + else + printf("skipped\n"); + printf("%-40s", "Testing lar (null selector)..."); instr[0] = 0x0f; instr[1] = 0x02; instr[2] = 0xc1; regs.eflags = 0x240; --- a/tools/tests/x86_emulator/x86_emulate.h +++ b/tools/tests/x86_emulator/x86_emulate.h @@ -81,6 +81,12 @@ static inline uint64_t xgetbv(uint32_t x (res.d & (1U << 26)) != 0; \ }) +#define cpu_has_popcnt ({ \ + struct cpuid_leaf res; \ + emul_test_cpuid(1, 0, &res, NULL); \ + (res.c & (1U << 23)) != 0; \ +}) + #define cpu_has_xsave ({ \ struct cpuid_leaf res; \ emul_test_cpuid(1, 0, &res, NULL); \ --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -1335,6 +1335,7 @@ static bool vcpu_has( #define vcpu_has_cx16() vcpu_has( 1, ECX, 13, ctxt, ops) #define vcpu_has_sse4_2() vcpu_has( 1, ECX, 20, ctxt, ops) #define vcpu_has_movbe() vcpu_has( 1, ECX, 22, ctxt, ops) +#define vcpu_has_popcnt() vcpu_has( 1, ECX, 23, ctxt, ops) #define vcpu_has_avx() vcpu_has( 1, ECX, 28, ctxt, ops) #define vcpu_has_lahf_lm() vcpu_has(0x80000001, ECX, 0, ctxt, ops) #define vcpu_has_cr8_legacy() vcpu_has(0x80000001, ECX, 4, ctxt, ops) @@ -2078,8 +2079,12 @@ x86_decode_twobyte( op_bytes = mode_64bit() ? 8 : 4; break; + case 0xb8: /* jmpe / popcnt */ + if ( rep_prefix() ) + ctxt->opcode |= MASK_INSR(vex.pfx, X86EMUL_OPC_PFX_MASK); + break; + /* Intentionally not handling here despite being modified by F3: - case 0xb8: jmpe / popcnt case 0xbc: bsf / tzcnt case 0xbd: bsr / lzcnt * They're being dealt with in the execution phase (if at all). @@ -5603,6 +5608,14 @@ x86_emulate( dst.val = (uint16_t)src.val; break; + case X86EMUL_OPC_F3(0x0f, 0xb8): /* popcnt r/m,r */ + host_and_vcpu_must_have(popcnt); + asm ( "popcnt %1,%0" : "=r" (dst.val) : "rm" (src.val) ); + _regs._eflags &= ~EFLAGS_MASK; + if ( !dst.val ) + _regs._eflags |= EFLG_ZF; + break; + case X86EMUL_OPC(0x0f, 0xba): /* Grp8 */ switch ( modrm_reg & 7 ) { --- a/xen/include/asm-x86/cpufeature.h +++ b/xen/include/asm-x86/cpufeature.h @@ -40,6 +40,7 @@ #define cpu_has_mmx 1 #define cpu_has_sse3 boot_cpu_has(X86_FEATURE_SSE3) #define cpu_has_sse4_2 boot_cpu_has(X86_FEATURE_SSE4_2) +#define cpu_has_popcnt boot_cpu_has(X86_FEATURE_POPCNT) #define cpu_has_htt boot_cpu_has(X86_FEATURE_HTT) #define cpu_has_nx boot_cpu_has(X86_FEATURE_NX) #define cpu_has_clflush boot_cpu_has(X86_FEATURE_CLFLUSH)