Upgrade parlib fp state handling, use proc_global_info (XCC)
authorMichael Taufen <mtaufen@gmail.com>
Fri, 11 Mar 2016 00:04:50 +0000 (16:04 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 22 Mar 2016 20:54:53 +0000 (16:54 -0400)
Rebuild kernel headers and all user apps!

This upgrades parlib so it also has the fp state upgrades
recently made to the Akaros kernel (xsave, xsaveopt, xrstor),
and also makes Akaros use proc_global_info for x86_default_xcr0

Signed-off-by: Michael Taufen <mtaufen@gmail.com>
[ touched up a checkpatch warning ]
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/init.c
kern/arch/x86/ros/trapframe.h
kern/arch/x86/smp_boot.c
kern/arch/x86/trap.c
kern/arch/x86/trap.h
kern/arch/x86/vmm/intel/vmx.c
kern/arch/x86/vmm/vmm.c
kern/include/ros/procinfo.h
user/parlib/include/parlib/x86/arch.h

index cb1f23d..4c02909 100644 (file)
 #include <monitor.h>
 #include <arch/usb.h>
 #include <assert.h>
+#include <ros/procinfo.h>
 #include <cpu_feat.h>
 
-/*
- *     x86_default_xcr0 is the Akaros-wide
- *     default value for the xcr0 register.
- *
- *     It is set on every processor during
- *     per-cpu init.
- */
-uint64_t x86_default_xcr0;
+
 struct ancillary_state x86_default_fpu;
 uint32_t kerndate;
 
@@ -95,13 +89,14 @@ void ancillary_state_init(void)
 
        if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
                // Next determine the user state components supported
-               // by the processor and set x86_default_xcr0.
+               // by the processor and set x86_default_xcr0 in proc_global_info.
                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;
+               __proc_global_info.x86_default_xcr0 = X86_MAX_XCR0 &
+                                                     proc_supported_features;
 
                /*
                 * Make sure CR4.OSXSAVE is set and set the local xcr0 to the default.
@@ -111,7 +106,7 @@ void ancillary_state_init(void)
                 * You must set CR4_OSXSAVE before setting xcr0, or a #UD fault occurs.
                 */
                lcr4(rcr4() | CR4_OSXSAVE);
-               lxcr0(x86_default_xcr0);
+               lxcr0(__proc_global_info.x86_default_xcr0);
 
                /*
                 * Build a default set of extended state values that we can later use
@@ -165,7 +160,7 @@ void ancillary_state_init(void)
        } else {
                // Since no program should try to use XSAVE features
                // on this processor, we set x86_default_xcr0 to 0x0
-               x86_default_xcr0 = 0x0;
+               __proc_global_info.x86_default_xcr0 = 0x0;
 
                /*
                 * Build a default set of extended state values that we can later use to
index 835e711..d45f636 100644 (file)
@@ -81,7 +81,7 @@ typedef struct {
  *  This may be a superset of available state components on a given
  *  processor. We CPUID at boot and determine the intersection
  *  of Akaros-supported and processor-supported features, and we
- *  save this value to x86_default_xcr0 in arch/x86/init.c.
+ *  save this value to __proc_global_info.x86_default_xcr0 in arch/x86/init.c.
  *  We guarantee that the set of feature components specified by
  *  X86_MAX_XCR0 will fit in the ancillary_state struct.
  *  If you add to the mask, make sure you also extend ancillary_state!
index ef2a5c5..ba50b1a 100644 (file)
@@ -24,6 +24,7 @@
 #include <kmalloc.h>
 #include <cpu_feat.h>
 #include <arch/fsgsbase.h>
+#include <ros/procinfo.h>
 
 #include "vmm/vmm.h"
 
@@ -299,7 +300,7 @@ void __arch_pcpu_init(uint32_t coreid)
                // You MUST set CR4.OSXSAVE before loading xcr0
                lcr4(rcr4() | CR4_OSXSAVE);
                // Set xcr0 to the Akaros-wide default
-               lxcr0(x86_default_xcr0);
+               lxcr0(__proc_global_info.x86_default_xcr0);
        }
 
        // Initialize fpu and extended state by restoring our default XSAVE area.
index 7bde8b0..a5a4abf 100644 (file)
@@ -19,6 +19,7 @@
 #include <kmalloc.h>
 #include <ex_table.h>
 #include <arch/mptables.h>
+#include <ros/procinfo.h>
 
 taskstate_t ts;
 
@@ -792,7 +793,7 @@ static bool handle_vmexit_xsetbv(struct vm_trapframe *tf)
        // vm_rfbm & (~x86_default_xcr0) is nonzero if any bits
        // are set in vm_rfbm but not x86_default_xcr0
 
-       if (vm_rfbm & (~x86_default_xcr0))
+       if (vm_rfbm & (~__proc_global_info.x86_default_xcr0))
                return FALSE;
 
 
index bc02ab8..ab28349 100644 (file)
@@ -159,10 +159,10 @@ extern void sysenter_handler(void);
 
 /* Defined and set up in in arch/init.c, used for XMM initialization */
 extern struct ancillary_state x86_default_fpu;
-extern uint64_t x86_default_xcr0;
 
 static inline void save_fp_state(struct ancillary_state *silly)
 {
+       uint64_t x86_default_xcr0 = __proc_global_info.x86_default_xcr0;
        uint32_t eax, edx;
 
        /* PLEASE NOTE:
@@ -211,6 +211,7 @@ static inline void save_fp_state(struct ancillary_state *silly)
 static inline void init_fp_state(void);
 static inline void restore_fp_state(struct ancillary_state *silly)
 {
+       uint64_t x86_default_xcr0 = __proc_global_info.x86_default_xcr0;
        int err = 0;
        uint32_t eax, edx;
 
index 6f789ab..2363594 100644 (file)
 #include <trap.h>
 
 #include <smp.h>
+#include <ros/procinfo.h>
 
 #define currentcpu (&per_cpu_info[core_id()])
 
@@ -1131,7 +1132,7 @@ struct guest_pcore *create_guest_pcore(struct proc *p,
        vmx_setup_vmcs(gpc);
        ret = vmx_setup_initial_guest_state(p, gpci);
        vmx_unload_guest_pcore(gpc);
-       gpc->xcr0 = x86_default_xcr0;
+       gpc->xcr0 = __proc_global_info.x86_default_xcr0;
 
        gpc->posted_irq_desc = gpci->posted_irq_desc;
 
index c0a1679..c5f6aaf 100644 (file)
@@ -21,6 +21,7 @@
 #include <umem.h>
 
 #include <arch/x86.h>
+#include <ros/procinfo.h>
 
 
 /* TODO: have better cpuid info storage and checks */
@@ -205,7 +206,7 @@ void unload_guest_pcore(struct proc *p, int guest_pcoreid)
 
        /* Save guest's xcr0 and restore Akaros's default. */
        gpc->xcr0 = rxcr0();
-       lxcr0(x86_default_xcr0);
+       lxcr0(__proc_global_info.x86_default_xcr0);
 
        /* As soon as we unlock, this gpc can be started on another core */
        spin_unlock(&p->vmm.lock);
index 611626c..2a56c69 100644 (file)
@@ -67,6 +67,7 @@ typedef struct procinfo {
 /* We align this so that the kernel can easily allocate it in the BSS */
 struct proc_global_info {
        unsigned long cpu_feats[__NR_CPU_FEAT_BITS];
+       uint64_t x86_default_xcr0;
 } __attribute__((aligned(PGSIZE)));
 #define PROCGINFO_NUM_PAGES  (sizeof(struct proc_global_info) / PGSIZE)
 
index 0392ad1..5f80938 100644 (file)
@@ -2,13 +2,15 @@
 
 #include <ros/trapframe.h>
 #include <ros/arch/mmu.h>
+#include <ros/procinfo.h>
+#include <parlib/cpu_feat.h>
 
 __BEGIN_DECLS
 
 #define ARCH_CL_SIZE 64
 #ifdef __x86_64__
 
-#define internal_function 
+#define internal_function
 #define X86_REG_BP                                     "rbp"
 #define X86_REG_SP                                     "rsp"
 #define X86_REG_IP                                     "rip"
@@ -65,12 +67,90 @@ static inline void cpu_relax(void)
 
 static inline void save_fp_state(struct ancillary_state *silly)
 {
-       asm volatile("fxsave %0" : : "m"(*silly));
+       uint64_t x86_default_xcr0 = __proc_global_info.x86_default_xcr0;
+       uint32_t eax, edx;
+
+       /* PLEASE NOTE:
+        * AMD CPUs ignore the FOP/FIP/FDP fields when there is
+        * no pending exception. When you are on AMD, we zero these fields in the
+        * ancillary_state argument before saving. This way, if you are on AMD and
+        * re-using an ancillary_state memory region, an old save's information
+        * won't leak into your new data. The side-effect of this is that you can't
+        * trust these fields to report accurate information on AMD unless an
+        * exception was pending. Granted, AMD says that only exception handlers
+        * should care about FOP/FIP/FDP, so that's probably okay.
+        *
+        * You should also note that on newer Intel 64 processors, while the value
+        * of the FOP is always saved and restored, it contains the opcode of the
+        * most recent x87 FPU instruction that triggered an unmasked exception,
+        * rather than simply the most recent opcode. Some older Xeons and P4s had
+        * the fopcode compatibility mode feature, which you could use to make the
+        * FOP update on every x87 non-control instruction, but that has been
+        * eliminated in newer hardware.
+        *
+        */
+       if (cpu_has_feat(CPU_FEAT_X86_VENDOR_AMD)) {
+               silly->fp_head_64d.fop      = 0x0;
+               silly->fp_head_64d.fpu_ip   = 0x0;
+               silly->fp_head_64d.cs       = 0x0;
+               silly->fp_head_64d.padding1 = 0x0; // padding1 is FIP or rsvd, proc dep.
+               silly->fp_head_64d.fpu_dp   = 0x0;
+               silly->fp_head_64d.ds       = 0x0;
+               silly->fp_head_64d.padding2 = 0x0; // padding2 is FDP or rsvd, proc dep.
+       }
+
+
+       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));
+       }
 }
 
+// NOTE: If you try to restore from a garbage ancillary_state,
+//       you might trigger a fault and crash your program.
 static inline void restore_fp_state(struct ancillary_state *silly)
 {
-       asm volatile("fxrstor %0" : : "m"(*silly));
+       uint64_t x86_default_xcr0 = __proc_global_info.x86_default_xcr0;
+       uint32_t eax, edx;
+
+       /*
+        * Since AMD CPUs ignore the FOP/FIP/FDP fields when there is
+        * no pending exception, we clear those fields before restoring
+        * when we are both on AMD and there is no pending exception in
+        * the ancillary_state argument to restore_fp_state.
+        * If there is a pending exception in the ancillary_state,
+        * these fields will be written to the FPU upon executing
+        * a restore instruction, and there is nothing to worry about.
+        *
+        * See CVE-2006-1056 and CVE-2013-2076 on cve.mitre.org.
+        *
+        * We check for a pending exception by checking FSW.ES (bit 7)
+        *
+        * FNINIT clears FIP and FDP and, even though it is technically a
+        * control instruction, it clears FOP because it is initializing the FPU.
+        *
+        * NOTE: This might not be the most efficient way to do things, and
+        *       could be an optimization target for context switch performance
+        *       on AMD processors in the future.
+        */
+       if (!(silly->fp_head_64d.fsw & 0x80)
+               && cpu_has_feat(CPU_FEAT_X86_VENDOR_AMD))
+               asm volatile ("fninit;");
+
+       if (cpu_has_feat(CPU_FEAT_X86_XSAVE)) {
+               edx = x86_default_xcr0 >> 32;
+               eax = x86_default_xcr0;
+               asm volatile("xrstor64 %0" : : "m"(*silly));
+       } else {
+               asm volatile("fxrstor64 %0" : : "m"(*silly));
+       }
 }
 
 __END_DECLS