Extended state AMD backwards compatibility updates (XCC)
authorMichael Taufen <mtaufen@gmail.com>
Thu, 3 Mar 2016 21:32:05 +0000 (13:32 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 10 Mar 2016 18:55:41 +0000 (13:55 -0500)
Rebuild your universe (kernel headers and user apps)!

These updates allow Akaros to defer to FXSAVE instructions in the event
that the processor does not support the XSAVE instructions. This is
necessary for Akaros to run on older AMD processors (pre bulldozer).

Akaros will still refuse to boot if you do not have support for FXSAVE.

These updates also include additional CPU feature detection,
particularly x86 vendor detection and support for the XSAVE instruction.

Finally, these updates allow the use of XSAVE in the absence of
XSAVEOPT, because it was an easy patch and we don't have to be that
mean.

Signed-off-by: Michael Taufen <mtaufen@gmail.com>
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/cpuinfo.c
kern/arch/x86/init.c
kern/arch/x86/ros/cpu_feat.h
kern/arch/x86/smp_boot.c
kern/arch/x86/trap.h

index 3219421..6135741 100644 (file)
@@ -24,12 +24,13 @@ void print_cpuinfo(void)
        char vendor_id[13];
        int max_std_lvl, max_extd_lvl;
        extern char _start[];
-       bool is_intel;
 
        if (sizeof(long) == 8)
                printk("64 bit Kernel Booting...\n");
        else
                printk("32 bit Kernel Booting...\n");
+
+       // x86 Vendor Detection:
        asm volatile ("cpuid;"
                  "movl    %%ebx, (%2);"
                  "movl    %%edx, 4(%2);"
@@ -41,10 +42,15 @@ void print_cpuinfo(void)
        vendor_id[12] = '\0';
        cprintf("Vendor ID: %s\n", vendor_id);
        /* not a great check - old intel P5s have no vendor id */
-       is_intel = !strcmp(vendor_id, "GenuineIntel");
+       if (!strcmp(vendor_id, "GenuineIntel"))
+               cpu_set_feat(CPU_FEAT_X86_VENDOR_INTEL);
+       else if (!strcmp(vendor_id, "AuthenticAMD"))
+               cpu_set_feat(CPU_FEAT_X86_VENDOR_AMD);
+
+
        /* intel supports a way to hide the upper leaves of cpuid, beyond 3.  the
         * bios might have done this, so we'll make sure it is off. */
-       if (is_intel) {
+       if (cpu_has_feat(CPU_FEAT_X86_VENDOR_INTEL)) {
                msr_val = read_msr(IA32_MISC_ENABLE);
                if (msr_val & (1 << 22))
                        write_msr(IA32_MISC_ENABLE, msr_val & ~(1 << 22));
@@ -85,14 +91,14 @@ void print_cpuinfo(void)
        else
                cprintf("x2APIC Not Detected\n");
        /* Not sure how to detect AMD HW virt yet. */
-       if ((ecx & 0x00000060) && is_intel) {
+       if ((ecx & 0x00000060) && cpu_has_feat(CPU_FEAT_X86_VENDOR_INTEL)) {
                msr_val = read_msr(IA32_FEATURE_CONTROL);
                printd("64 Bit Feature Control: 0x%08x\n", msr_val);
                if ((msr_val & 0x5) == 0x5)
                        printk("Hardware virtualization supported\n");
                else
                        printk("Hardware virtualization not supported\n");
-       } else { 
+       } else {
                printk("Hardware virtualization not detected.  (AMD?)\n");
        }
        /* FP and SSE Checks */
@@ -164,16 +170,20 @@ void print_cpuinfo(void)
                printk("Always running APIC *not* detected\n");
 
        /* TODO: Eventually consolidate all of our "cpuid" stuff. */
+       #define CPUID_FXSR_SUPPORT          (1 << 24)
        #define CPUID_XSAVE_SUPPORT         (1 << 26)
        #define CPUID_XSAVEOPT_SUPPORT      (1 << 0)
 
+       cpuid(0x01, 0x00, 0, 0, &ecx, &edx);
+       if (CPUID_FXSR_SUPPORT & edx)
+               cpu_set_feat(CPU_FEAT_X86_FXSR);
+       if (CPUID_XSAVE_SUPPORT & ecx)
+               cpu_set_feat(CPU_FEAT_X86_XSAVE);
+
        cpuid(0x0d, 0x01, &eax, 0, 0, 0);
-       if (CPUID_XSAVEOPT_SUPPORT & eax) {
-               cpuid(0x01, 0x00, 0, 0, &ecx, 0);
-               /* XSAVEOPT should imply XSAVE */
-               assert(CPUID_XSAVE_SUPPORT & ecx);
+       if (CPUID_XSAVEOPT_SUPPORT & eax)
                cpu_set_feat(CPU_FEAT_X86_XSAVEOPT);
-       }
+
 }
 
 #define BIT_SPACING "        "
index 6b88995..ebb33e7 100644 (file)
@@ -1,7 +1,7 @@
 /* See COPYRIGHT for copyright information. */
 
+#include <ros/common.h>
 #include <smp.h>
-
 #include <arch/x86.h>
 #include <arch/pci.h>
 #include <arch/console.h>
@@ -88,75 +88,118 @@ void ancillary_state_init(void)
        uint32_t eax, ebx, ecx, edx;
        uint64_t proc_supported_features; /* proc supported user state components */
 
-       /* TODO: switch between XSAVEOPT and pre-XSAVE FP ops */
-       if (!cpu_has_feat(CPU_FEAT_X86_XSAVEOPT))
-               panic("No XSAVEOPT support! Refusing to boot.\n");
-
-       // Next determine the user state components supported
-       // by the processor and set x86_default_xcr0.
-       cpuid(0x0d, 0x00, &eax, 0, 0, &edx);
-       proc_supported_features = ((uint64_t)edx << 32) | eax;
-
-       // Intersection of processor-supported and Akaros-supported
-       // features is the Akaros-wide default at runtime.
-       x86_default_xcr0 = X86_MAX_XCR0 & proc_supported_features;
-
-       /*
-        *      Make sure CR4.OSXSAVE is set and set the local xcr0 to the default.
-        *      We will do both of these things again during per-cpu init,
-        *      but we are about to use XSAVE to build our default extended state
-        *      record, so we need them enabled.
-        *      You must set CR4_OSXSAVE before setting xcr0, or a #UD fault occurs.
-        */
-       lcr4(rcr4() | CR4_OSXSAVE);
-       lxcr0(x86_default_xcr0);
-
-       /*
-        *      Build a default set of extended state values that we can later use to
-        *      initialize extended state on other cores, or restore on this core.
-        *      We need to use FNINIT to reset the FPU before saving, in case boot
-        *      agents used the FPU or it is dirty for some reason. An old comment that
-        *      used to be here said "had this happen on c89, which had a full FP stack
-        *      after booting." Note that FNINIT does not clear the data registers,
-        *      but it tags them all as empty (0b11).
-        */
-
-       asm volatile ("fninit");
-
-       // Zero the default extended state memory region before saving.
-       memset(&x86_default_fpu, 0x00, sizeof(struct ancillary_state));
-
-       /*
-        *      Save only the x87 FPU state so that the extended state registers
-        *      remain zeroed in the default. There is a caveat to this that involves
-        *  the MXCSR register, this is handled below.
-        *      We use XSAVE64 instead of XSAVEOPT64 (save_fp_state uses XSAVEOPT64),
-        *      because XSAVEOPT64 may decide to skip saving a state component
-        *      if that state component is in its initial configuration, and
-        *      we just used FNINIT to put the x87 in its initial configuration.
-        *      We can be confident that the x87 bit (bit 0) is set in xcr0, because
-        *      Intel requires it to be set at all times.
-        */
-       edx = 0x0;
-       eax = 0x1;
-       asm volatile("xsave64 %0" : : "m"(x86_default_fpu), "a"(eax), "d"(edx));
-
-       /* We must set the MXCSR field in the default state struct to its
-        * power-on value of 0x1f80. This masks all SIMD floating
-        * point exceptions and clears all SIMD floating-point exception
-        * flags, sets rounding control to round-nearest, disables
-        * flush-to-zero mode, and disables denormals-are-zero mode.
-        *
-        * We don't actually have to set the MXCSR itself here,
-        * because it will be set from the default state struct when
-        * we perform per-cpu init.
-        *
-        * Right now, we set the MXCSR through fp_head_64d. Since
-        * the mxcsr is at the same offset in all fp header formats
-        * implemented for Akaros, this will function correctly for
-        * all supported operating modes.
-        */
-        x86_default_fpu.fp_head_64d.mxcsr = 0x1f80;
+       // If you don't at least have FXSAVE and FXRSTOR
+       // (includes OSFXSR), you don't boot.
+       if (!cpu_has_feat(CPU_FEAT_X86_FXSR))
+               panic("No FXSAVE/FXRSTOR (FXSR) support! Refusing to boot.");
+
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVEOPT)) {
+               // Next determine the user state components supported
+               // by the processor and set x86_default_xcr0.
+               cpuid(0x0d, 0x00, &eax, 0, 0, &edx);
+               proc_supported_features = ((uint64_t)edx << 32) | eax;
+
+               // Intersection of processor-supported and Akaros-supported
+               // features is the Akaros-wide default at runtime.
+               x86_default_xcr0 = X86_MAX_XCR0 & proc_supported_features;
+
+               /*
+                * Make sure CR4.OSXSAVE is set and set the local xcr0 to the default.
+                * We will do both of these things again during per-cpu init,
+                * but we are about to use XSAVE to build our default extended state
+                * record, so we need them enabled.
+                * You must set CR4_OSXSAVE before setting xcr0, or a #UD fault occurs.
+                */
+               lcr4(rcr4() | CR4_OSXSAVE);
+               lxcr0(x86_default_xcr0);
+
+               /*
+                * Build a default set of extended state values that we can later use
+                * to initialize extended state on other cores, or restore on this
+                * core. We need to use FNINIT to reset the FPU before saving, in case
+                * boot agents used the FPU or it is dirty for some reason. An old
+                * comment that used to be here said "had this happen on c89, which had
+                * a full FP stack after booting." Note that FNINIT does not clear the
+                * data registers, but it tags them all as empty (0b11).
+                */
+
+               // Zero the default extended state memory region before saving.
+               // It may be possible for memset to clobber SSE registers.
+               memset(&x86_default_fpu, 0x00, sizeof(struct ancillary_state));
+               asm volatile ("fninit");
+
+               /*
+                * Save only the x87 FPU state so that the extended state registers
+                * remain zeroed in the default. There is a caveat to this that
+                * involves the MXCSR register, this is handled below.
+                * We use XSAVE64 instead of XSAVEOPT64 (save_fp_state uses
+                * XSAVEOPT64), because XSAVEOPT64 may decide to skip saving a state
+                * component if that state component is in its initial configuration,
+                * and we just used FNINIT to put the x87 in its initial configuration.
+                * We can be confident that the x87 bit (bit 0) is set in xcr0, because
+                * Intel requires it to be set at all times.
+                */
+               edx = 0x0;
+               eax = 0x1;
+               asm volatile("xsave64 %0" : : "m"(x86_default_fpu), "a"(eax), "d"(edx));
+
+               /* We must set the MXCSR field in the default state struct to its
+                * power-on value of 0x1f80. This masks all SIMD floating
+                * point exceptions and clears all SIMD floating-point exception
+                * flags, sets rounding control to round-nearest, disables
+                * flush-to-zero mode, and disables denormals-are-zero mode.
+                *
+                * We don't actually have to set the MXCSR itself here,
+                * because it will be set from the default state struct when
+                * we perform per-cpu init.
+                *
+                * Right now, we set the MXCSR through fp_head_64d. Since
+                * the mxcsr is at the same offset in all fp header formats
+                * implemented for Akaros, this will function correctly for
+                * all supported operating modes.
+                */
+                x86_default_fpu.fp_head_64d.mxcsr = 0x1f80;
+       } else {
+               // Since no program should try to use XSAVE features
+               // on this processor, we set x86_default_xcr0 to 0x0
+               x86_default_xcr0 = 0x0;
+
+               /*
+                * Build a default set of extended state values that we can later use to
+                * initialize extended state on other cores, or restore on this core.
+                * We need to use FNINIT to reset the FPU before saving, in case boot
+                * agents used the FPU or it is dirty for some reason. An old comment
+                * that used to be here said "had this happen on c89, which had a full
+                * FP stack after booting." Note that FNINIT does not clear the data
+                * registers, but it tags them all as empty (0b11).
+                */
+
+               // Zero the default extended state memory region before saving.
+               // It may be possible for memset to clobber SSE registers.
+               memset(&x86_default_fpu, 0x00, sizeof(struct ancillary_state));
+               asm volatile ("fninit");
+
+               // Save the x87 FPU state
+               asm volatile("fxsave64 %0" : : "m"(x86_default_fpu));
+
+               /*
+                * Because FXSAVE may have also saved junk from the XMM registers,
+                * depending on how the hardware was implemented and the setting
+                * of CR4.OSFXSR, we manually zero the region of the ancillary_state
+                * containing those registers after saving FPU state. There are 16
+                * XMM registers, each 128 bits, for a total size of 256 bytes.
+                */
+               memset(&(x86_default_fpu.xmm0), 0x00, 256);
+
+               /*
+                * Finally, because Only the Paranoid Survive, we set the MXCSR
+                * for our default state. It should have been saved by FXSAVE,
+                * but who knows if the default value is still there at this
+                * point in the boot process.
+                */
+               x86_default_fpu.fp_head_64d.mxcsr = 0x1f80;
+       }
+
 }
 
 void arch_init(void)
index cffa14c..c6faf31 100644 (file)
 
 #pragma once
 
-#define CPU_FEAT_X86_XSAVEOPT                  (__CPU_FEAT_ARCH_START + 0)
-#define CPU_FEAT_X86_FSGSBASE                  (__CPU_FEAT_ARCH_START + 1)
+#define CPU_FEAT_X86_VENDOR_INTEL              (__CPU_FEAT_ARCH_START + 0)
+#define CPU_FEAT_X86_VENDOR_AMD                        (__CPU_FEAT_ARCH_START + 1)
+#define CPU_FEAT_X86_FXSR                              (__CPU_FEAT_ARCH_START + 2)
+#define CPU_FEAT_X86_XSAVE                             (__CPU_FEAT_ARCH_START + 3)
+#define CPU_FEAT_X86_XSAVEOPT                  (__CPU_FEAT_ARCH_START + 4)
+#define CPU_FEAT_X86_FSGSBASE                  (__CPU_FEAT_ARCH_START + 5)
 #define __NR_CPU_FEAT                                  (__CPU_FEAT_ARCH_START + 64)
index 8ffa6dc..f58e7c0 100644 (file)
@@ -284,13 +284,23 @@ void __arch_pcpu_init(uint32_t coreid)
 
        if (cpu_has_feat(CPU_FEAT_X86_FSGSBASE))
                lcr4(rcr4() | CR4_FSGSBASE);
-       /* Enable SSE instructions.  We might have to do more, like masking certain
-        * flags or exceptions in the MXCSR, or at least handle the SIMD exceptions.
-        * We don't do it for FP yet either, so YMMV. */
-       lcr4(rcr4() | CR4_OSFXSR | CR4_OSXMME | CR4_OSXSAVE);
 
-       // Set xcr0 to the Akaros-wide default
-       lxcr0(x86_default_xcr0);
+       /*
+        * Enable SSE instructions.
+        * CR4.OSFXSR enables SSE and ensures that MXCSR/XMM gets saved with FXSAVE
+        * CR4.OSXSAVE enables XSAVE instructions. Only set if XSAVEOPT supported.
+        * CR4.OSXMME indicates OS support for software exception handlers for
+        * SIMD floating-point exceptions (turn it on to get #XM exceptions
+        * in the event of a SIMD error instead of #UD exceptions).
+        */
+       lcr4(rcr4() | CR4_OSFXSR | CR4_OSXMME);
+
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVEOPT)) {
+               // You MUST set CR4.OSXSAVE before loading xcr0
+               lcr4(rcr4() | CR4_OSXSAVE);
+               // Set xcr0 to the Akaros-wide default
+               lxcr0(x86_default_xcr0);
+       }
 
        // Initialize fpu and extended state by restoring our default XSAVE area.
        init_fp_state();
index c842e24..24b7a83 100644 (file)
 #include <arch/pci.h>
 #include <arch/pic.h>
 #include <arch/topology.h>
+#include <cpu_feat.h>
 #include <arch/io.h>
 #include <stdio.h>
 
@@ -164,9 +165,17 @@ static inline void save_fp_state(struct ancillary_state *silly)
 {
        uint32_t eax, edx;
 
-       edx = x86_default_xcr0 >> 32;
-       eax = x86_default_xcr0;
-       asm volatile("xsaveopt64 %0" : : "m"(*silly), "a"(eax), "d"(edx));
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVEOPT)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile("xsaveopt64 %0" : : "m"(*silly), "a"(eax), "d"(edx));
+       } else if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile("xsave64 %0" : : "m"(*silly), "a"(eax), "d"(edx));
+       } else {
+               asm volatile("fxsave64 %0" : : "m"(*silly));
+       }
 }
 
 static inline void init_fp_state(void);
@@ -175,19 +184,34 @@ static inline void restore_fp_state(struct ancillary_state *silly)
        int err = 0;
        uint32_t eax, edx;
 
-       edx = x86_default_xcr0 >> 32;
-       eax = x86_default_xcr0;
-       asm volatile(ASM_STAC               ";"
-                        "1: xrstor64 %1         ;"
-                    "2: " ASM_CLAC "        ;"
-                    ".section .fixup, \"ax\";"
-                    "3: mov %4, %0          ;"
-                    "   jmp 2b              ;"
-                    ".previous              ;"
-                    _ASM_EXTABLE(1b, 3b)
-                    : "=r" (err)
-                    : "m"(*silly), "a"(eax), "d"(edx),
-                      "i" (-EINVAL), "0" (err));
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile(ASM_STAC               ";"
+                            "1: xrstor64 %1         ;"
+                            "2: " ASM_CLAC "        ;"
+                            ".section .fixup, \"ax\";"
+                            "3: mov %4, %0          ;"
+                            "   jmp 2b              ;"
+                            ".previous              ;"
+                            _ASM_EXTABLE(1b, 3b)
+                            : "=r" (err)
+                            : "m"(*silly), "a"(eax), "d"(edx),
+                              "i" (-EINVAL), "0" (err));
+       } else {
+               asm volatile(ASM_STAC               ";"
+                            "1: fxrstor64 %1         ;"
+                            "2: " ASM_CLAC "        ;"
+                            ".section .fixup, \"ax\";"
+                            "3: mov %2, %0          ;"
+                            "   jmp 2b              ;"
+                            ".previous              ;"
+                            _ASM_EXTABLE(1b, 3b)
+                            : "=r" (err)
+                            : "m"(*silly),
+                              "i" (-EINVAL), "0" (err));
+       }
+
 
        if (err) {
                printk("Error restoring fp state!");