x86, xsave: save/restore the extended state context in sigframe
authorSuresh Siddha <suresh.b.siddha@intel.com>
Tue, 29 Jul 2008 17:29:25 +0000 (10:29 -0700)
committerIngo Molnar <mingo@elte.hu>
Wed, 30 Jul 2008 17:49:27 +0000 (19:49 +0200)
On cpu's supporting xsave/xrstor, fpstate pointer in the sigcontext, will
include the extended state information along with fpstate information. Presence
of extended state information is indicated by the presence
of FP_XSTATE_MAGIC1 at fpstate.sw_reserved.magic1 and FP_XSTATE_MAGIC2
at fpstate + (fpstate.sw_reserved.extended_size - FP_XSTATE_MAGIC2_SIZE).

Extended feature bit mask that is saved in the memory layout is represented
by the fpstate.sw_reserved.xstate_bv

For RT signal frames, UC_FP_XSTATE in the uc_flags also indicate the
presence of extended state information in the sigcontext's fpstate
pointer.

Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/ia32/ia32_signal.c
arch/x86/kernel/i387.c
arch/x86/kernel/signal_32.c
arch/x86/kernel/signal_64.c
arch/x86/kernel/xsave.c
include/asm-x86/i387.h
include/asm-x86/ucontext.h
include/asm-x86/xsave.h

index c596eabbe98b119b6b4009a8c531018656ee96c5..f25a10124005c5a4911ba563a87ee94320ddd811 100644 (file)
@@ -544,7 +544,10 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                goto give_sigsegv;
 
        /* Create the ucontext.  */
-       err |= __put_user(0, &frame->uc.uc_flags);
+       if (cpu_has_xsave)
+               err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+       else
+               err |= __put_user(0, &frame->uc.uc_flags);
        err |= __put_user(0, &frame->uc.uc_link);
        err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
        err |= __put_user(sas_ss_flags(regs->sp),
index 7daf3a011dd94dda358e3abd68f9038e8fbe1d16..cbb9dc474a21f95305b0d0d12b951c84716dd573 100644 (file)
@@ -26,6 +26,7 @@
 # define _fpstate_ia32         _fpstate
 # define _xstate_ia32          _xstate
 # define sig_xstate_ia32_size   sig_xstate_size
+# define fx_sw_reserved_ia32   fx_sw_reserved
 # define user_i387_ia32_struct user_i387_struct
 # define user32_fxsr_struct    user_fxsr_struct
 #endif
@@ -447,12 +448,30 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
        if (err)
                return -1;
 
-       if (__copy_to_user(&buf->_fxsr_env[0], fx,
-                          sizeof(struct i387_fxsave_struct)))
+       if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
                return -1;
        return 1;
 }
 
+static int save_i387_xsave(void __user *buf)
+{
+       struct _fpstate_ia32 __user *fx = buf;
+       int err = 0;
+
+       if (save_i387_fxsave(fx) < 0)
+               return -1;
+
+       err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32,
+                            sizeof(struct _fpx_sw_bytes));
+       err |= __put_user(FP_XSTATE_MAGIC2,
+                         (__u32 __user *) (buf + sig_xstate_ia32_size
+                                           - FP_XSTATE_MAGIC2_SIZE));
+       if (err)
+               return -1;
+
+       return 1;
+}
+
 int save_i387_xstate_ia32(void __user *buf)
 {
        struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
@@ -477,6 +496,8 @@ int save_i387_xstate_ia32(void __user *buf)
 
        unlazy_fpu(tsk);
 
+       if (cpu_has_xsave)
+               return save_i387_xsave(fp);
        if (cpu_has_fxsr)
                return save_i387_fxsave(fp);
        else
@@ -491,14 +512,15 @@ static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
                                sizeof(struct i387_fsave_struct));
 }
 
-static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
+static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
+                              unsigned int size)
 {
        struct task_struct *tsk = current;
        struct user_i387_ia32_struct env;
        int err;
 
        err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
-                              sizeof(struct i387_fxsave_struct));
+                              size);
        /* mxcsr reserved bits must be masked to zero for security reasons */
        tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
        if (err || __copy_from_user(&env, buf, sizeof(env)))
@@ -508,6 +530,51 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
        return 0;
 }
 
+static int restore_i387_xsave(void __user *buf)
+{
+       struct _fpx_sw_bytes fx_sw_user;
+       struct _fpstate_ia32 __user *fx_user =
+                       ((struct _fpstate_ia32 __user *) buf);
+       struct i387_fxsave_struct __user *fx =
+               (struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0];
+       struct xsave_hdr_struct *xsave_hdr =
+                               &current->thread.xstate->xsave.xsave_hdr;
+       unsigned int lmask, hmask;
+       int err;
+
+       if (check_for_xstate(fx, buf, &fx_sw_user))
+               goto fx_only;
+
+       lmask = fx_sw_user.xstate_bv;
+       hmask = fx_sw_user.xstate_bv >> 32;
+
+       err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);
+
+       xsave_hdr->xstate_bv &=  (pcntxt_lmask | (((u64) pcntxt_hmask) << 32));
+       /*
+        * These bits must be zero.
+        */
+       xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;
+
+       /*
+        * Init the state that is not present in the memory layout
+        * and enabled by the OS.
+        */
+       lmask = ~(pcntxt_lmask & ~lmask);
+       hmask = ~(pcntxt_hmask & ~hmask);
+       xsave_hdr->xstate_bv &=  (lmask | (((u64) hmask) << 32));
+
+       return err;
+fx_only:
+       /*
+        * Couldn't find the extended state information in the memory
+        * layout. Restore the FP/SSE and init the other extended state
+        * enabled by the OS.
+        */
+       xsave_hdr->xstate_bv = XSTATE_FPSSE;
+       return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct));
+}
+
 int restore_i387_xstate_ia32(void __user *buf)
 {
        int err;
@@ -535,8 +602,11 @@ int restore_i387_xstate_ia32(void __user *buf)
        }
 
        if (HAVE_HWFP) {
-               if (cpu_has_fxsr)
-                       err = restore_i387_fxsave(fp);
+               if (cpu_has_xsave)
+                       err = restore_i387_xsave(buf);
+               else if (cpu_has_fxsr)
+                       err = restore_i387_fxsave(fp, sizeof(struct
+                                                          i387_fxsave_struct));
                else
                        err = restore_i387_fsave(fp);
        } else {
index 690cc616ac075abe7abffa12188c4e55e18aba31..0f98d69fbdb0343e024eca7d8a2c32f4e86de202 100644 (file)
@@ -441,7 +441,10 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                goto give_sigsegv;
 
        /* Create the ucontext.  */
-       err |= __put_user(0, &frame->uc.uc_flags);
+       if (cpu_has_xsave)
+               err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+       else
+               err |= __put_user(0, &frame->uc.uc_flags);
        err |= __put_user(0, &frame->uc.uc_link);
        err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
        err |= __put_user(sas_ss_flags(regs->sp),
index ddf6123a55c89ab6ea0ab0550bf830bdb90c8349..2621b98f5bf6635a5713bef4b0f3bbaf65ba4e41 100644 (file)
@@ -192,7 +192,7 @@ get_stack(struct k_sigaction *ka, struct pt_regs *regs, unsigned long size)
                        sp = current->sas_ss_sp + current->sas_ss_size;
        }
 
-       return (void __user *)round_down(sp - size, 16);
+       return (void __user *)round_down(sp - size, 64);
 }
 
 static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
@@ -226,7 +226,10 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        }
                
        /* Create the ucontext.  */
-       err |= __put_user(0, &frame->uc.uc_flags);
+       if (cpu_has_xsave)
+               err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+       else
+               err |= __put_user(0, &frame->uc.uc_flags);
        err |= __put_user(0, &frame->uc.uc_link);
        err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
        err |= __put_user(sas_ss_flags(regs->sp),
index 608e72d7ca64f72e7ea414d05c4c64cbe43bd9f7..dd66d0714c1820a17aae498e14b67aa746cbd65f 100644 (file)
@@ -6,12 +6,68 @@
 #include <linux/bootmem.h>
 #include <linux/compat.h>
 #include <asm/i387.h>
+#ifdef CONFIG_IA32_EMULATION
+#include <asm/sigcontext32.h>
+#endif
 
 /*
  * Supported feature mask by the CPU and the kernel.
  */
 unsigned int pcntxt_hmask, pcntxt_lmask;
 
+struct _fpx_sw_bytes fx_sw_reserved;
+#ifdef CONFIG_IA32_EMULATION
+struct _fpx_sw_bytes fx_sw_reserved_ia32;
+#endif
+
+/*
+ * Check for the presence of extended state information in the
+ * user fpstate pointer in the sigcontext.
+ */
+int check_for_xstate(struct i387_fxsave_struct __user *buf,
+                    void __user *fpstate,
+                    struct _fpx_sw_bytes *fx_sw_user)
+{
+       int min_xstate_size = sizeof(struct i387_fxsave_struct) +
+                             sizeof(struct xsave_hdr_struct);
+       unsigned int magic2;
+       int err;
+
+       err = __copy_from_user(fx_sw_user, &buf->sw_reserved[0],
+                              sizeof(struct _fpx_sw_bytes));
+
+       if (err)
+               return err;
+
+       /*
+        * First Magic check failed.
+        */
+       if (fx_sw_user->magic1 != FP_XSTATE_MAGIC1)
+               return -1;
+
+       /*
+        * Check for error scenarios.
+        */
+       if (fx_sw_user->xstate_size < min_xstate_size ||
+           fx_sw_user->xstate_size > xstate_size ||
+           fx_sw_user->xstate_size > fx_sw_user->extended_size)
+               return -1;
+
+       err = __get_user(magic2, (__u32 *) (((void *)fpstate) +
+                                           fx_sw_user->extended_size -
+                                           FP_XSTATE_MAGIC2_SIZE));
+       /*
+        * Check for the presence of second magic word at the end of memory
+        * layout. This detects the case where the user just copied the legacy
+        * fpstate layout with out copying the extended state information
+        * in the memory layout.
+        */
+       if (err || magic2 != FP_XSTATE_MAGIC2)
+               return -1;
+
+       return 0;
+}
+
 #ifdef CONFIG_X86_64
 /*
  * Signal frame handlers.
@@ -28,15 +84,18 @@ int save_i387_xstate(void __user *buf)
        BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
                        sizeof(tsk->thread.xstate->fxsave));
 
-       if ((unsigned long)buf % 16)
+       if ((unsigned long)buf % 64)
                printk("save_i387_xstate: bad fpstate %p\n", buf);
 
        if (!used_math())
                return 0;
        clear_used_math(); /* trigger finit */
        if (task_thread_info(tsk)->status & TS_USEDFPU) {
-               err = save_i387_checking((struct i387_fxsave_struct __user *)
-                                        buf);
+               if (task_thread_info(tsk)->status & TS_XSAVE)
+                       err = xsave_user(buf);
+               else
+                       err = fxsave_user(buf);
+
                if (err)
                        return err;
                task_thread_info(tsk)->status &= ~TS_USEDFPU;
@@ -46,23 +105,77 @@ int save_i387_xstate(void __user *buf)
                                   xstate_size))
                        return -1;
        }
+
+       if (task_thread_info(tsk)->status & TS_XSAVE) {
+               struct _fpstate __user *fx = buf;
+
+               err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved,
+                                    sizeof(struct _fpx_sw_bytes));
+
+               err |= __put_user(FP_XSTATE_MAGIC2,
+                                 (__u32 __user *) (buf + sig_xstate_size
+                                                   - FP_XSTATE_MAGIC2_SIZE));
+       }
+
        return 1;
 }
 
+/*
+ * Restore the extended state if present. Otherwise, restore the FP/SSE
+ * state.
+ */
+int restore_user_xstate(void __user *buf)
+{
+       struct _fpx_sw_bytes fx_sw_user;
+       unsigned int lmask, hmask;
+       int err;
+
+       if (((unsigned long)buf % 64) ||
+            check_for_xstate(buf, buf, &fx_sw_user))
+               goto fx_only;
+
+       lmask = fx_sw_user.xstate_bv;
+       hmask = fx_sw_user.xstate_bv >> 32;
+
+       /*
+        * restore the state passed by the user.
+        */
+       err = xrestore_user(buf, lmask, hmask);
+       if (err)
+               return err;
+
+       /*
+        * init the state skipped by the user.
+        */
+       lmask = pcntxt_lmask & ~lmask;
+       hmask = pcntxt_hmask & ~hmask;
+
+       xrstor_state(init_xstate_buf, lmask, hmask);
+
+       return 0;
+
+fx_only:
+       /*
+        * couldn't find the extended state information in the
+        * memory layout. Restore just the FP/SSE and init all
+        * the other extended state.
+        */
+       xrstor_state(init_xstate_buf, pcntxt_lmask & ~XSTATE_FPSSE,
+                    pcntxt_hmask);
+       return fxrstor_checking((__force struct i387_fxsave_struct *)buf);
+}
+
 /*
  * This restores directly out of user space. Exceptions are handled.
  */
 int restore_i387_xstate(void __user *buf)
 {
        struct task_struct *tsk = current;
-       int err;
+       int err = 0;
 
        if (!buf) {
-               if (used_math()) {
-                       clear_fpu(tsk);
-                       clear_used_math();
-               }
-
+               if (used_math())
+                       goto clear;
                return 0;
        } else
                if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
@@ -78,12 +191,17 @@ int restore_i387_xstate(void __user *buf)
                clts();
                task_thread_info(current)->status |= TS_USEDFPU;
        }
-       err = fxrstor_checking((__force struct i387_fxsave_struct *)buf);
+       if (task_thread_info(tsk)->status & TS_XSAVE)
+               err = restore_user_xstate(buf);
+       else
+               err = fxrstor_checking((__force struct i387_fxsave_struct *)
+                                      buf);
        if (unlikely(err)) {
                /*
                 * Encountered an error while doing the restore from the
                 * user buffer, clear the fpu state.
                 */
+clear:
                clear_fpu(tsk);
                clear_used_math();
        }
@@ -91,6 +209,38 @@ int restore_i387_xstate(void __user *buf)
 }
 #endif
 
+/*
+ * Prepare the SW reserved portion of the fxsave memory layout, indicating
+ * the presence of the extended state information in the memory layout
+ * pointed by the fpstate pointer in the sigcontext.
+ * This will be saved when ever the FP and extended state context is
+ * saved on the user stack during the signal handler delivery to the user.
+ */
+void prepare_fx_sw_frame(void)
+{
+       int size_extended = (xstate_size - sizeof(struct i387_fxsave_struct)) +
+                            FP_XSTATE_MAGIC2_SIZE;
+
+       sig_xstate_size = sizeof(struct _fpstate) + size_extended;
+
+#ifdef CONFIG_IA32_EMULATION
+       sig_xstate_ia32_size = sizeof(struct _fpstate_ia32) + size_extended;
+#endif
+
+       memset(&fx_sw_reserved, 0, sizeof(fx_sw_reserved));
+
+       fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
+       fx_sw_reserved.extended_size = sig_xstate_size;
+       fx_sw_reserved.xstate_bv = pcntxt_lmask |
+                                        (((u64) (pcntxt_hmask)) << 32);
+       fx_sw_reserved.xstate_size = xstate_size;
+#ifdef CONFIG_IA32_EMULATION
+       memcpy(&fx_sw_reserved_ia32, &fx_sw_reserved,
+              sizeof(struct _fpx_sw_bytes));
+       fx_sw_reserved_ia32.extended_size = sig_xstate_ia32_size;
+#endif
+}
+
 /*
  * Represents init state for the supported extended state.
  */
@@ -162,6 +312,8 @@ void __init xsave_cntxt_init(void)
 
        xstate_size = ebx;
 
+       prepare_fx_sw_frame();
+
        setup_xstate_init();
 
        printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%Lx, "
index dc3745e8040a70001cf66ddf6467e78c9021bc22..d3dda7161954304417efb5dc81b1946440dcea4e 100644 (file)
@@ -31,8 +31,10 @@ extern user_regset_active_fn fpregs_active, xfpregs_active;
 extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get;
 extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
 
+extern struct _fpx_sw_bytes fx_sw_reserved;
 #ifdef CONFIG_IA32_EMULATION
 extern unsigned int sig_xstate_ia32_size;
+extern struct _fpx_sw_bytes fx_sw_reserved_ia32;
 struct _fpstate_ia32;
 struct _xstate_ia32;
 extern int save_i387_xstate_ia32(void __user *buf);
@@ -104,7 +106,7 @@ static inline void clear_fpu_state(struct task_struct *tsk)
                          X86_FEATURE_FXSAVE_LEAK);
 }
 
-static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
+static inline int fxsave_user(struct i387_fxsave_struct __user *fx)
 {
        int err;
 
index 9948dd328084ef66461d2dc92c996235fc78eb5e..89eaa5456a7e1e86f25f92c41bd85d2bc629ec33 100644 (file)
@@ -1,6 +1,12 @@
 #ifndef ASM_X86__UCONTEXT_H
 #define ASM_X86__UCONTEXT_H
 
+#define UC_FP_XSTATE   0x1     /* indicates the presence of extended state
+                                * information in the memory layout pointed
+                                * by the fpstate pointer in the ucontext's
+                                * sigcontext struct (uc_mcontext).
+                                */
+
 struct ucontext {
        unsigned long     uc_flags;
        struct ucontext  *uc_link;
index b716511aede2a8415c4012d2c214e96d787fa657..b7f64b9fcd94f1716c0abc9edef52125cd85427e 100644 (file)
@@ -29,6 +29,9 @@ extern struct xsave_struct *init_xstate_buf;
 extern void xsave_cntxt_init(void);
 extern void xsave_init(void);
 extern int init_fpu(struct task_struct *child);
+extern int check_for_xstate(struct i387_fxsave_struct __user *buf,
+                           void __user *fpstate,
+                           struct _fpx_sw_bytes *sw);
 
 static inline int xrstor_checking(struct xsave_struct *fx)
 {
@@ -48,7 +51,7 @@ static inline int xrstor_checking(struct xsave_struct *fx)
        return err;
 }
 
-static inline int xsave_check(struct xsave_struct __user *buf)
+static inline int xsave_user(struct xsave_struct __user *buf)
 {
        int err;
        __asm__ __volatile__("1: .byte " REX_PREFIX "0x0f,0xae,0x27\n"