diff --git a/tools/tests/x86_emulator/test_x86_emulator.c b/tools/tests/x86_emulator/test_x86_emulator.c index 1b78bf7e45..86e298fa49 100644 --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -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__) diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c index 56aef3e66a..c3672e7de6 100644 --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -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 )