From patchwork Fri Jun 11 07:45:12 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sheng Yang X-Patchwork-Id: 105511 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o5B7maXI016554 for ; Fri, 11 Jun 2010 07:48:36 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757386Ab0FKHse (ORCPT ); Fri, 11 Jun 2010 03:48:34 -0400 Received: from mga11.intel.com ([192.55.52.93]:54558 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752969Ab0FKHsd (ORCPT ); Fri, 11 Jun 2010 03:48:33 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga102.fm.intel.com with ESMTP; 11 Jun 2010 00:45:02 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.53,402,1272870000"; d="scan'208";a="806938138" Received: from syang10-desktop.sh.intel.com (HELO syang10-desktop) ([10.239.36.64]) by fmsmga001.fm.intel.com with ESMTP; 11 Jun 2010 00:48:11 -0700 Received: from yasker by syang10-desktop with local (Exim 4.71) (envelope-from ) id 1OMyvR-00025D-7s; Fri, 11 Jun 2010 15:45:13 +0800 From: Sheng Yang To: Avi Kivity , Marcelo Tosatti Cc: kvm@vger.kernel.org, Sheng Yang Subject: [PATCH v3] test: Add XSAVE unit test Date: Fri, 11 Jun 2010 15:45:12 +0800 Message-Id: <1276242312-7982-1-git-send-email-sheng@linux.intel.com> X-Mailer: git-send-email 1.7.0.4 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 11 Jun 2010 07:48:36 +0000 (UTC) diff --git a/kvm/test/config-x86-common.mak b/kvm/test/config-x86-common.mak index 800b635..0e1ccce 100644 --- a/kvm/test/config-x86-common.mak +++ b/kvm/test/config-x86-common.mak @@ -61,6 +61,8 @@ $(TEST_DIR)/msr.flat: $(cstart.o) $(TEST_DIR)/msr.o $(TEST_DIR)/idt_test.flat: $(cstart.o) $(TEST_DIR)/idt.o $(TEST_DIR)/idt_test.o +$(TEST_DIR)/xsave.flat: $(cstart.o) $(TEST_DIR)/idt.o $(TEST_DIR)/xsave.o + arch_clean: $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \ $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o diff --git a/kvm/test/config-x86_64.mak b/kvm/test/config-x86_64.mak index f9cd121..2da2906 100644 --- a/kvm/test/config-x86_64.mak +++ b/kvm/test/config-x86_64.mak @@ -5,6 +5,7 @@ ldarch = elf64-x86-64 CFLAGS += -D__x86_64__ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ - $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat + $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \ + $(TEST_DIR)/xsave.flat include config-x86-common.mak diff --git a/kvm/test/x86/xsave.c b/kvm/test/x86/xsave.c new file mode 100644 index 0000000..30e3b31 --- /dev/null +++ b/kvm/test/x86/xsave.c @@ -0,0 +1,260 @@ +#include "libcflat.h" +#include "idt.h" + +#ifdef __x86_64__ +#define uint64_t unsigned long +#else +#define uint64_t unsigned long long +#endif + +static inline void __cpuid(unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + /* ecx is often an input as well as an output. */ + asm volatile("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (*eax), "2" (*ecx)); +} + +/* + * Generic CPUID function + * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx + * resulting in stale register contents being returned. + */ +void cpuid(unsigned int op, + unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + *eax = op; + *ecx = 0; + __cpuid(eax, ebx, ecx, edx); +} + +/* Some CPUID calls want 'count' to be placed in ecx */ +void cpuid_count(unsigned int op, int count, + unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + *eax = op; + *ecx = count; + __cpuid(eax, ebx, ecx, edx); +} + +int xgetbv_checking(u32 index, u64 *result) +{ + u32 eax, edx; + + asm volatile(ASM_TRY("1f") + ".byte 0x0f,0x01,0xd0\n\t" /* xgetbv */ + "1:" + : "=a" (eax), "=d" (edx) + : "c" (index)); + *result = eax + ((u64)edx << 32); + return exception_vector(); +} + +int xsetbv_checking(u32 index, u64 value) +{ + u32 eax = value; + u32 edx = value >> 32; + + asm volatile(ASM_TRY("1f") + ".byte 0x0f,0x01,0xd1\n\t" /* xsetbv */ + "1:" + : : "a" (eax), "d" (edx), "c" (index)); + return exception_vector(); +} + +unsigned long read_cr4(void) +{ + unsigned long val; + asm volatile("mov %%cr4,%0" : "=r" (val)); + return val; +} + +int write_cr4_checking(unsigned long val) +{ + asm volatile(ASM_TRY("1f") + "mov %0,%%cr4\n\t" + "1:": : "r" (val)); + return exception_vector(); +} + +#define CPUID_1_ECX_XSAVE (1 << 26) +#define CPUID_1_ECX_OSXSAVE (1 << 27) +int check_cpuid_1_ecx(unsigned int bit) +{ + unsigned int eax, ebx, ecx, edx; + cpuid(1, &eax, &ebx, &ecx, &edx); + if (ecx & bit) + return 1; + return 0; +} + +uint64_t get_supported_xcr0(void) +{ + unsigned int eax, ebx, ecx, edx; + cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx); + printf("eax %x, ebx %x, ecx %x, edx %x\n", + eax, ebx, ecx, edx); + return eax + ((u64)edx << 32); +} + +#define X86_CR4_OSXSAVE 0x00040000 +#define XCR_XFEATURE_ENABLED_MASK 0x00000000 +#define XCR_XFEATURE_ILLEGAL_MASK 0x00000010 + +#define XSTATE_FP 0x1 +#define XSTATE_SSE 0x2 +#define XSTATE_YMM 0x4 + +static int total_tests, fail_tests; + +void pass_if(int condition) +{ + total_tests ++; + if (condition) + printf("Pass!\n"); + else { + printf("Fail!\n"); + fail_tests ++; + } +} + +void test_xsave(void) +{ + unsigned long cr4; + uint64_t supported_xcr0; + uint64_t test_bits; + u64 xcr0; + int r; + + printf("Legal instruction testing:\n"); + supported_xcr0 = get_supported_xcr0(); + printf("Supported XCR0 bits: 0x%x\n", supported_xcr0); + + printf("Check minimal XSAVE required bits: "); + test_bits = XSTATE_FP | XSTATE_SSE; + pass_if((supported_xcr0 & test_bits) == test_bits); + + printf("Set CR4 OSXSAVE: "); + cr4 = read_cr4(); + r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE); + pass_if(r == 0); + + printf("Check CPUID.1.ECX.OSXSAVE - expect 1: "); + pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE)); + + printf(" Legal tests\n"); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP): "); + test_bits = XSTATE_FP; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == 0); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, " + "XSTATE_FP | XSTATE_SSE): "); + test_bits = XSTATE_FP | XSTATE_SSE; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == 0); + printf(" xgetbv(XCR_XFEATURE_ENABLED_MASK): "); + r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0); + pass_if(r == 0); + printf(" Illegal tests\n"); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, 0) - expect #GP: "); + test_bits = 0; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == GP_VECTOR); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_SSE) " + "- expect #GP: "); + test_bits = XSTATE_SSE; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == GP_VECTOR); + if (supported_xcr0 & XSTATE_YMM) { + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, " + "XSTATE_YMM) - expect #GP: "); + test_bits = XSTATE_YMM; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == GP_VECTOR); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, " + "XSTATE_FP | XSTATE_YMM) - expect #GP: "); + test_bits = XSTATE_FP | XSTATE_YMM; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == GP_VECTOR); + } + printf(" xsetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) " + "- expect #GP: "); + test_bits = XSTATE_SSE; + r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits); + pass_if(r == GP_VECTOR); + printf(" xgetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) " + "- expect #GP: "); + test_bits = XSTATE_SSE; + r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits); + pass_if(r == GP_VECTOR); + + printf("Unset CR4 OSXSAVE: "); + cr4 &= ~X86_CR4_OSXSAVE; + r = write_cr4_checking(cr4); + pass_if(r == 0); + printf("Check CPUID.1.ECX.OSXSAVE - expect 0: "); + pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0); + printf(" Illegal tests:\n"); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP) - expect #UD: "); + test_bits = XSTATE_FP; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == UD_VECTOR); + printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, " + "XSTATE_FP | XSTATE_SSE) - expect #UD: "); + test_bits = XSTATE_FP | XSTATE_SSE; + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits); + pass_if(r == UD_VECTOR); + printf(" Illegal tests:\n"); + printf(" xgetbv(XCR_XFEATURE_ENABLED_MASK) - expect #UD: "); + r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0); + pass_if(r == UD_VECTOR); +} + +void test_no_xsave(void) +{ + unsigned long cr4; + u64 xcr0; + int r; + + printf("Check CPUID.1.ECX.OSXSAVE - expect 0: "); + pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0); + + printf("Illegal instruction testing:\n"); + + printf("Set OSXSAVE in CR4 - expect #GP: "); + cr4 = read_cr4(); + r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE); + pass_if(r == GP_VECTOR); + + printf("Execute xgetbv - expect #UD: "); + r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0); + pass_if(r == UD_VECTOR); + + printf("Execute xsetbv - expect #UD: "); + r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, 0x3); + pass_if(r == UD_VECTOR); +} + +int main(void) +{ + setup_idt(); + if (check_cpuid_1_ecx(CPUID_1_ECX_XSAVE)) { + printf("CPU has XSAVE feature\n"); + test_xsave(); + } else { + printf("CPU don't has XSAVE feature\n"); + test_no_xsave(); + } + printf("Total test: %d\n", total_tests); + if (fail_tests == 0) + printf("ALL PASS!\n"); + else + printf("Fail %d tests.\n", fail_tests); + return 1; +}