xen: mask XSAVE from cpuid
authorJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Sun, 8 Mar 2009 01:09:27 +0000 (17:09 -0800)
committerJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Mon, 30 Mar 2009 16:25:28 +0000 (09:25 -0700)
Xen leaves XSAVE set in cpuid, but doesn't allow cr4.OSXSAVE
to be set.  This confuses the kernel and it ends up crashing on
an xsetbv instruction.

At boot time, try to set cr4.OSXSAVE, and mask XSAVE out of
cpuid it we can't.  This will produce a spurious error from Xen,
but allows us to support XSAVE if/when Xen does.

This also factors out the cpuid mask decisions to boot time.

Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
arch/x86/xen/enlighten.c

index 75b7a0f..da33e0c 100644 (file)
@@ -168,21 +168,23 @@ static void __init xen_banner(void)
               xen_feature(XENFEAT_mmu_pt_update_preserve_ad) ? " (preserve-AD)" : "");
 }
 
+static __read_mostly unsigned int cpuid_leaf1_edx_mask = ~0;
+static __read_mostly unsigned int cpuid_leaf1_ecx_mask = ~0;
+
 static void xen_cpuid(unsigned int *ax, unsigned int *bx,
                      unsigned int *cx, unsigned int *dx)
 {
+       unsigned maskecx = ~0;
        unsigned maskedx = ~0;
 
        /*
         * Mask out inconvenient features, to try and disable as many
         * unsupported kernel subsystems as possible.
         */
-       if (*ax == 1)
-               maskedx = ~((1 << X86_FEATURE_APIC) |  /* disable APIC */
-                           (1 << X86_FEATURE_ACPI) |  /* disable ACPI */
-                           (1 << X86_FEATURE_MCE)  |  /* disable MCE */
-                           (1 << X86_FEATURE_MCA)  |  /* disable MCA */
-                           (1 << X86_FEATURE_ACC));   /* thermal monitoring */
+       if (*ax == 1) {
+               maskecx = cpuid_leaf1_ecx_mask;
+               maskedx = cpuid_leaf1_edx_mask;
+       }
 
        asm(XEN_EMULATE_PREFIX "cpuid"
                : "=a" (*ax),
@@ -190,9 +192,43 @@ static void xen_cpuid(unsigned int *ax, unsigned int *bx,
                  "=c" (*cx),
                  "=d" (*dx)
                : "0" (*ax), "2" (*cx));
+
+       *cx &= maskecx;
        *dx &= maskedx;
 }
 
+static __init void xen_init_cpuid_mask(void)
+{
+       unsigned int ax, bx, cx, dx;
+
+       cpuid_leaf1_edx_mask =
+               ~((1 << X86_FEATURE_MCE)  |  /* disable MCE */
+                 (1 << X86_FEATURE_MCA)  |  /* disable MCA */
+                 (1 << X86_FEATURE_ACC));   /* thermal monitoring */
+
+       if (!xen_initial_domain())
+               cpuid_leaf1_edx_mask &=
+                       ~((1 << X86_FEATURE_APIC) |  /* disable local APIC */
+                         (1 << X86_FEATURE_ACPI));  /* disable ACPI */
+
+       ax = 1;
+       xen_cpuid(&ax, &bx, &cx, &dx);
+
+       /* cpuid claims we support xsave; try enabling it to see what happens */
+       if (cx & (1 << (X86_FEATURE_XSAVE % 32))) {
+               unsigned long cr4;
+
+               set_in_cr4(X86_CR4_OSXSAVE);
+               
+               cr4 = read_cr4();
+
+               if ((cr4 & X86_CR4_OSXSAVE) == 0)
+                       cpuid_leaf1_ecx_mask &= ~(1 << (X86_FEATURE_XSAVE % 32));
+
+               clear_in_cr4(X86_CR4_OSXSAVE);
+       }
+}
+
 static void xen_set_debugreg(int reg, unsigned long val)
 {
        HYPERVISOR_set_debugreg(reg, val);
@@ -901,6 +937,8 @@ asmlinkage void __init xen_start_kernel(void)
 
        xen_init_irq_ops();
 
+       xen_init_cpuid_mask();
+
 #ifdef CONFIG_X86_LOCAL_APIC
        /*
         * set up the basic apic ops.