x86emul: support POPCNT
authorJan Beulich <jbeulich@suse.com>
Tue, 17 Jan 2017 09:32:54 +0000 (10:32 +0100)
committerJan Beulich <jbeulich@suse.com>
Tue, 17 Jan 2017 09:32:54 +0000 (10:32 +0100)
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
tools/tests/x86_emulator/test_x86_emulator.c
tools/tests/x86_emulator/x86_emulate.h
xen/arch/x86/x86_emulate/x86_emulate.c
xen/include/asm-x86/cpufeature.h

index 0d5655aa3dcecf2a659abdc47882fb749dd8cbd2..1cd3581db3c1141059f9cb6d515220557a447584 100644 (file)
@@ -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;
index 8bc2e432d03b78592f3851364e2ca61dd14ac253..596d55a74fa50d8dc8e6fefb8782fbbea5577cd6 100644 (file)
@@ -81,6 +81,12 @@ static inline uint64_t xgetbv(uint32_t xcr)
     (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); \
index 20285b2206a8e85bbfecbd0c57e385aa6df0e87d..6d6c53df47e1e9eb48cf2a6a2614652eb6ca3de9 100644 (file)
@@ -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)
@@ -2103,8 +2104,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).
@@ -5614,6 +5619,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 )
         {
index f34d01c9763ee41d534c72f08d94d59e87b10ca6..ba1c5e5655784e18fb38d7f335dfcbe3144acc7e 100644 (file)
@@ -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)