x86/fpu: Dont restore PKRU in fpregs_restore_userspace()
[linux-2.6-microblaze.git] / arch / x86 / kernel / fpu / core.c
index 571220a..470576c 100644 (file)
@@ -83,19 +83,23 @@ bool irq_fpu_usable(void)
 EXPORT_SYMBOL(irq_fpu_usable);
 
 /*
- * These must be called with preempt disabled. Returns
- * 'true' if the FPU state is still intact and we can
- * keep registers active.
+ * Save the FPU register state in fpu->state. The register state is
+ * preserved.
  *
- * The legacy FNSAVE instruction cleared all FPU state
- * unconditionally, so registers are essentially destroyed.
- * Modern FPU state can be kept in registers, if there are
- * no pending FP exceptions.
+ * Must be called with fpregs_lock() held.
+ *
+ * The legacy FNSAVE instruction clears all FPU state unconditionally, so
+ * register state has to be reloaded. That might be a pointless exercise
+ * when the FPU is going to be used by another task right after that. But
+ * this only affects 20+ years old 32bit systems and avoids conditionals all
+ * over the place.
+ *
+ * FXSAVE and all XSAVE variants preserve the FPU register state.
  */
-int copy_fpregs_to_fpstate(struct fpu *fpu)
+void save_fpregs_to_fpstate(struct fpu *fpu)
 {
        if (likely(use_xsave())) {
-               copy_xregs_to_kernel(&fpu->state.xsave);
+               os_xsave(&fpu->state.xsave);
 
                /*
                 * AVX512 state is tracked here because its use is
@@ -103,23 +107,49 @@ int copy_fpregs_to_fpstate(struct fpu *fpu)
                 */
                if (fpu->state.xsave.header.xfeatures & XFEATURE_MASK_AVX512)
                        fpu->avx512_timestamp = jiffies;
-               return 1;
+               return;
        }
 
        if (likely(use_fxsr())) {
-               copy_fxregs_to_kernel(fpu);
-               return 1;
+               fxsave(&fpu->state.fxsave);
+               return;
        }
 
        /*
         * Legacy FPU register saving, FNSAVE always clears FPU registers,
-        * so we have to mark them inactive:
+        * so we have to reload them from the memory state.
         */
        asm volatile("fnsave %[fp]; fwait" : [fp] "=m" (fpu->state.fsave));
+       frstor(&fpu->state.fsave);
+}
+EXPORT_SYMBOL(save_fpregs_to_fpstate);
 
-       return 0;
+void __restore_fpregs_from_fpstate(union fpregs_state *fpstate, u64 mask)
+{
+       /*
+        * AMD K7/K8 and later CPUs up to Zen don't save/restore
+        * FDP/FIP/FOP unless an exception is pending. Clear the x87 state
+        * here by setting it to fixed values.  "m" is a random variable
+        * that should be in L1.
+        */
+       if (unlikely(static_cpu_has_bug(X86_BUG_FXSAVE_LEAK))) {
+               asm volatile(
+                       "fnclex\n\t"
+                       "emms\n\t"
+                       "fildl %P[addr]"        /* set F?P to defined value */
+                       : : [addr] "m" (fpstate));
+       }
+
+       if (use_xsave()) {
+               os_xrstor(&fpstate->xsave, mask);
+       } else {
+               if (use_fxsr())
+                       fxrstor(&fpstate->fxsave);
+               else
+                       frstor(&fpstate->fsave);
+       }
 }
-EXPORT_SYMBOL(copy_fpregs_to_fpstate);
+EXPORT_SYMBOL_GPL(__restore_fpregs_from_fpstate);
 
 void kernel_fpu_begin_mask(unsigned int kfpu_mask)
 {
@@ -133,11 +163,7 @@ void kernel_fpu_begin_mask(unsigned int kfpu_mask)
        if (!(current->flags & PF_KTHREAD) &&
            !test_thread_flag(TIF_NEED_FPU_LOAD)) {
                set_thread_flag(TIF_NEED_FPU_LOAD);
-               /*
-                * Ignore return value -- we don't care if reg state
-                * is clobbered.
-                */
-               copy_fpregs_to_fpstate(&current->thread.fpu);
+               save_fpregs_to_fpstate(&current->thread.fpu);
        }
        __cpu_invalidate_fpregs_state();
 
@@ -160,27 +186,38 @@ void kernel_fpu_end(void)
 EXPORT_SYMBOL_GPL(kernel_fpu_end);
 
 /*
- * Save the FPU state (mark it for reload if necessary):
- *
- * This only ever gets called for the current task.
+ * Sync the FPU register state to current's memory register state when the
+ * current task owns the FPU. The hardware register state is preserved.
  */
-void fpu__save(struct fpu *fpu)
+void fpu_sync_fpstate(struct fpu *fpu)
 {
        WARN_ON_FPU(fpu != &current->thread.fpu);
 
        fpregs_lock();
        trace_x86_fpu_before_save(fpu);
 
-       if (!test_thread_flag(TIF_NEED_FPU_LOAD)) {
-               if (!copy_fpregs_to_fpstate(fpu)) {
-                       copy_kernel_to_fpregs(&fpu->state);
-               }
-       }
+       if (!test_thread_flag(TIF_NEED_FPU_LOAD))
+               save_fpregs_to_fpstate(fpu);
 
        trace_x86_fpu_after_save(fpu);
        fpregs_unlock();
 }
 
+static inline void fpstate_init_xstate(struct xregs_state *xsave)
+{
+       /*
+        * XRSTORS requires these bits set in xcomp_bv, or it will
+        * trigger #GP:
+        */
+       xsave->header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT | xfeatures_mask_all;
+}
+
+static inline void fpstate_init_fxstate(struct fxregs_state *fx)
+{
+       fx->cwd = 0x37f;
+       fx->mxcsr = MXCSR_DEFAULT;
+}
+
 /*
  * Legacy x87 fpstate state init:
  */
@@ -210,18 +247,18 @@ void fpstate_init(union fpregs_state *state)
 }
 EXPORT_SYMBOL_GPL(fpstate_init);
 
-int fpu__copy(struct task_struct *dst, struct task_struct *src)
+/* Clone current's FPU state on fork */
+int fpu_clone(struct task_struct *dst)
 {
+       struct fpu *src_fpu = &current->thread.fpu;
        struct fpu *dst_fpu = &dst->thread.fpu;
-       struct fpu *src_fpu = &src->thread.fpu;
 
+       /* The new task's FPU state cannot be valid in the hardware. */
        dst_fpu->last_cpu = -1;
 
-       if (!static_cpu_has(X86_FEATURE_FPU))
+       if (!cpu_feature_enabled(X86_FEATURE_FPU))
                return 0;
 
-       WARN_ON_FPU(src_fpu != &current->thread.fpu);
-
        /*
         * Don't let 'init optimized' areas of the XSAVE area
         * leak into the child task:
@@ -229,20 +266,16 @@ int fpu__copy(struct task_struct *dst, struct task_struct *src)
        memset(&dst_fpu->state.xsave, 0, fpu_kernel_xstate_size);
 
        /*
-        * If the FPU registers are not current just memcpy() the state.
-        * Otherwise save current FPU registers directly into the child's FPU
-        * context, without any memory-to-memory copying.
-        *
-        * ( The function 'fails' in the FNSAVE case, which destroys
-        *   register contents so we have to load them back. )
+        * If the FPU registers are not owned by current just memcpy() the
+        * state.  Otherwise save the FPU registers directly into the
+        * child's FPU context, without any memory-to-memory copying.
         */
        fpregs_lock();
        if (test_thread_flag(TIF_NEED_FPU_LOAD))
                memcpy(&dst_fpu->state, &src_fpu->state, fpu_kernel_xstate_size);
 
-       else if (!copy_fpregs_to_fpstate(dst_fpu))
-               copy_kernel_to_fpregs(&dst_fpu->state);
-
+       else
+               save_fpregs_to_fpstate(dst_fpu);
        fpregs_unlock();
 
        set_tsk_thread_flag(dst, TIF_NEED_FPU_LOAD);
@@ -253,63 +286,6 @@ int fpu__copy(struct task_struct *dst, struct task_struct *src)
        return 0;
 }
 
-/*
- * Activate the current task's in-memory FPU context,
- * if it has not been used before:
- */
-static void fpu__initialize(struct fpu *fpu)
-{
-       WARN_ON_FPU(fpu != &current->thread.fpu);
-
-       set_thread_flag(TIF_NEED_FPU_LOAD);
-       fpstate_init(&fpu->state);
-       trace_x86_fpu_init_state(fpu);
-}
-
-/*
- * This function must be called before we read a task's fpstate.
- *
- * There's two cases where this gets called:
- *
- * - for the current task (when coredumping), in which case we have
- *   to save the latest FPU registers into the fpstate,
- *
- * - or it's called for stopped tasks (ptrace), in which case the
- *   registers were already saved by the context-switch code when
- *   the task scheduled out.
- *
- * If the task has used the FPU before then save it.
- */
-void fpu__prepare_read(struct fpu *fpu)
-{
-       if (fpu == &current->thread.fpu)
-               fpu__save(fpu);
-}
-
-/*
- * This function must be called before we write a task's fpstate.
- *
- * Invalidate any cached FPU registers.
- *
- * After this function call, after registers in the fpstate are
- * modified and the child task has woken up, the child task will
- * restore the modified FPU state from the modified context. If we
- * didn't clear its cached status here then the cached in-registers
- * state pending on its former CPU could be restored, corrupting
- * the modifications.
- */
-void fpu__prepare_write(struct fpu *fpu)
-{
-       /*
-        * Only stopped child tasks can be used to modify the FPU
-        * state in the fpstate buffer:
-        */
-       WARN_ON_FPU(fpu == &current->thread.fpu);
-
-       /* Invalidate any cached state: */
-       __fpu_invalidate_fpregs_state(fpu);
-}
-
 /*
  * Drops current FPU state: deactivates the fpregs and
  * the fpstate. NOTE: it still leaves previous contents
@@ -340,61 +316,111 @@ void fpu__drop(struct fpu *fpu)
  * Clear FPU registers by setting them up from the init fpstate.
  * Caller must do fpregs_[un]lock() around it.
  */
-static inline void copy_init_fpstate_to_fpregs(u64 features_mask)
+static inline void restore_fpregs_from_init_fpstate(u64 features_mask)
 {
        if (use_xsave())
-               copy_kernel_to_xregs(&init_fpstate.xsave, features_mask);
-       else if (static_cpu_has(X86_FEATURE_FXSR))
-               copy_kernel_to_fxregs(&init_fpstate.fxsave);
+               os_xrstor(&init_fpstate.xsave, features_mask);
+       else if (use_fxsr())
+               fxrstor(&init_fpstate.fxsave);
        else
-               copy_kernel_to_fregs(&init_fpstate.fsave);
+               frstor(&init_fpstate.fsave);
+
+       pkru_write_default();
+}
 
-       if (boot_cpu_has(X86_FEATURE_OSPKE))
-               copy_init_pkru_to_fpregs();
+static inline unsigned int init_fpstate_copy_size(void)
+{
+       if (!use_xsave())
+               return fpu_kernel_xstate_size;
+
+       /* XSAVE(S) just needs the legacy and the xstate header part */
+       return sizeof(init_fpstate.xsave);
+}
+
+/* Temporary workaround. Will be removed once PKRU and XSTATE are untangled. */
+static inline void pkru_set_default_in_xstate(struct xregs_state *xsave)
+{
+       struct pkru_state *pk;
+
+       if (!cpu_feature_enabled(X86_FEATURE_OSPKE))
+               return;
+       /*
+        * Force XFEATURE_PKRU to be set in the header otherwise
+        * get_xsave_addr() does not work and it also needs to be set to
+        * make XRSTOR(S) load it.
+        */
+       xsave->header.xfeatures |= XFEATURE_MASK_PKRU;
+       pk = get_xsave_addr(xsave, XFEATURE_PKRU);
+       pk->pkru = pkru_get_init_value();
 }
 
 /*
- * Clear the FPU state back to init state.
- *
- * Called by sys_execve(), by the signal handler code and by various
- * error paths.
+ * Reset current->fpu memory state to the init values.
  */
-static void fpu__clear(struct fpu *fpu, bool user_only)
+static void fpu_reset_fpstate(void)
+{
+       struct fpu *fpu = &current->thread.fpu;
+
+       fpregs_lock();
+       fpu__drop(fpu);
+       /*
+        * This does not change the actual hardware registers. It just
+        * resets the memory image and sets TIF_NEED_FPU_LOAD so a
+        * subsequent return to usermode will reload the registers from the
+        * task's memory image.
+        *
+        * Do not use fpstate_init() here. Just copy init_fpstate which has
+        * the correct content already except for PKRU.
+        */
+       memcpy(&fpu->state, &init_fpstate, init_fpstate_copy_size());
+       pkru_set_default_in_xstate(&fpu->state.xsave);
+       set_thread_flag(TIF_NEED_FPU_LOAD);
+       fpregs_unlock();
+}
+
+/*
+ * Reset current's user FPU states to the init states.  current's
+ * supervisor states, if any, are not modified by this function.  The
+ * caller guarantees that the XSTATE header in memory is intact.
+ */
+void fpu__clear_user_states(struct fpu *fpu)
 {
        WARN_ON_FPU(fpu != &current->thread.fpu);
 
-       if (!static_cpu_has(X86_FEATURE_FPU)) {
-               fpu__drop(fpu);
-               fpu__initialize(fpu);
+       fpregs_lock();
+       if (!cpu_feature_enabled(X86_FEATURE_FPU)) {
+               fpu_reset_fpstate();
+               fpregs_unlock();
                return;
        }
 
-       fpregs_lock();
-
-       if (user_only) {
-               if (!fpregs_state_valid(fpu, smp_processor_id()) &&
-                   xfeatures_mask_supervisor())
-                       copy_kernel_to_xregs(&fpu->state.xsave,
-                                            xfeatures_mask_supervisor());
-               copy_init_fpstate_to_fpregs(xfeatures_mask_user());
-       } else {
-               copy_init_fpstate_to_fpregs(xfeatures_mask_all);
+       /*
+        * Ensure that current's supervisor states are loaded into their
+        * corresponding registers.
+        */
+       if (xfeatures_mask_supervisor() &&
+           !fpregs_state_valid(fpu, smp_processor_id())) {
+               os_xrstor(&fpu->state.xsave, xfeatures_mask_supervisor());
        }
 
+       /* Reset user states in registers. */
+       restore_fpregs_from_init_fpstate(xfeatures_mask_restore_user());
+
+       /*
+        * Now all FPU registers have their desired values.  Inform the FPU
+        * state machine that current's FPU registers are in the hardware
+        * registers. The memory image does not need to be updated because
+        * any operation relying on it has to save the registers first when
+        * current's FPU is marked active.
+        */
        fpregs_mark_activate();
        fpregs_unlock();
 }
 
-void fpu__clear_user_states(struct fpu *fpu)
-{
-       fpu__clear(fpu, true);
-}
-
-void fpu__clear_all(struct fpu *fpu)
+void fpu_flush_thread(void)
 {
-       fpu__clear(fpu, false);
+       fpu_reset_fpstate();
 }
-
 /*
  * Load FPU context before returning to userspace.
  */
@@ -403,7 +429,7 @@ void switch_fpu_return(void)
        if (!static_cpu_has(X86_FEATURE_FPU))
                return;
 
-       __fpregs_load_activate();
+       fpregs_restore_userregs();
 }
 EXPORT_SYMBOL_GPL(switch_fpu_return);