]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/xen.git/commitdiff
x86emul: support clzero
authorJan Beulich <jbeulich@suse.com>
Thu, 14 Jan 2016 09:32:35 +0000 (10:32 +0100)
committerJan Beulich <jbeulich@suse.com>
Thu, 14 Jan 2016 09:32:35 +0000 (10:32 +0100)
... in anticipation of this possibly going to get used by guests for
basic thinks like memset() or clearing or pages.

Since the emulation doesn't use clzero itself, checking the guest's
CPUID for the feature to be exposed is (intentionally) being avoided
here. All that's required is sensible guest side data for the clflush
line size.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Aravind Gopalakrishnan <aravind.gopalakrishnan@amd.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
tools/tests/x86_emulator/test_x86_emulator.c
xen/arch/x86/x86_emulate/x86_emulate.c

index 1b78bf7e450f15eeed12d9fe73696e56ba963293..86e298fa496cf9e919929b64084ed0dd33988dfa 100644 (file)
@@ -82,6 +82,12 @@ static int cpuid(
     return X86EMUL_OKAY;
 }
 
+#define cache_line_size() ({ \
+    unsigned int eax = 1, ebx, ecx = 0, edx; \
+    cpuid(&eax, &ebx, &ecx, &edx, NULL); \
+    edx & (1U << 19) ? (ebx >> 5) & 0x7f8 : 0; \
+})
+
 #define cpu_has_mmx ({ \
     unsigned int eax = 1, ecx = 0, edx; \
     cpuid(&eax, &ecx, &ecx, &edx, NULL); \
@@ -873,6 +879,35 @@ int main(int argc, char **argv)
 #undef set_insn
 #undef check_eip
 
+    j = cache_line_size();
+    snprintf(instr, (char *)res + MMAP_SZ - instr,
+             "Testing clzero (%u-byte line)...", j);
+    printf("%-40s", instr);
+    if ( j >= sizeof(*res) && j <= MMAP_SZ / 4 )
+    {
+        instr[0] = 0x0f; instr[1] = 0x01; instr[2] = 0xfc;
+        regs.eflags = 0x200;
+        regs.eip    = (unsigned long)&instr[0];
+        regs.eax    = (unsigned long)res + MMAP_SZ / 2 + j - 1;
+        memset((void *)res + MMAP_SZ / 4, ~0, 3 * MMAP_SZ / 4);
+        rc = x86_emulate(&ctxt, &emulops);
+        if ( (rc != X86EMUL_OKAY) ||
+             (regs.eax != (unsigned long)res + MMAP_SZ / 2 + j - 1) ||
+             (regs.eflags != 0x200) ||
+             (regs.eip != (unsigned long)&instr[3]) ||
+             (res[MMAP_SZ / 2 / sizeof(*res) - 1] != ~0U) ||
+             (res[(MMAP_SZ / 2 + j) / sizeof(*res)] != ~0U) )
+            goto fail;
+        for ( i = 0; i < j; i += sizeof(*res) )
+            if ( res[(MMAP_SZ / 2 + i) / sizeof(*res)] )
+                break;
+        if ( i < j )
+            goto fail;
+        printf("okay\n");
+    }
+    else
+        printf("skipped\n");
+
     for ( j = 1; j <= 2; j++ )
     {
 #if defined(__i386__)
index 56aef3e66ab5674acfbf680e296cdf0e5042bf15..c3672e7de6b9809597c52a8e9dbd2be466bdc31f 100644 (file)
@@ -1069,6 +1069,7 @@ static bool_t vcpu_has(
     return rc == X86EMUL_OKAY;
 }
 
+#define vcpu_has_clflush() vcpu_has(       1, EDX, 19, ctxt, ops)
 #define vcpu_has_lzcnt() vcpu_has(0x80000001, ECX,  5, ctxt, ops)
 #define vcpu_has_bmi1()  vcpu_has(0x00000007, EBX,  3, ctxt, ops)
 
@@ -3851,6 +3852,45 @@ x86_emulate(
             if ( (rc = ops->vmfunc(ctxt) != X86EMUL_OKAY) )
                 goto done;
             goto no_writeback;
+       case 0xfc: /* clzero */ {
+            unsigned int eax = 1, ebx = 0, dummy = 0;
+            unsigned long zero = 0;
+
+            base = ad_bytes == 8 ? _regs.eax :
+                   ad_bytes == 4 ? (uint32_t)_regs.eax : (uint16_t)_regs.eax;
+            limit = 0;
+            if ( vcpu_has_clflush() &&
+                 ops->cpuid(&eax, &ebx, &dummy, &dummy, ctxt) == X86EMUL_OKAY )
+                limit = ((ebx >> 8) & 0xff) * 8;
+            generate_exception_if(limit < sizeof(long) ||
+                                  (limit & (limit - 1)), EXC_UD, -1);
+            base &= ~(limit - 1);
+            if ( override_seg == -1 )
+                override_seg = x86_seg_ds;
+            if ( ops->rep_stos )
+            {
+                unsigned long nr_reps = limit / sizeof(zero);
+
+                rc = ops->rep_stos(&zero, override_seg, base, sizeof(zero),
+                                   &nr_reps, ctxt);
+                if ( rc == X86EMUL_OKAY )
+                {
+                    base += nr_reps * sizeof(zero);
+                    limit -= nr_reps * sizeof(zero);
+                }
+                else if ( rc != X86EMUL_UNHANDLEABLE )
+                    goto done;
+            }
+            while ( limit )
+            {
+                rc = ops->write(override_seg, base, &zero, sizeof(zero), ctxt);
+                if ( rc != X86EMUL_OKAY )
+                    goto done;
+                base += sizeof(zero);
+                limit -= sizeof(zero);
+            }
+            goto no_writeback;
+        }
         }
 
         switch ( modrm_reg & 7 )