OSDN Git Service

Merge tag 'powerpc-5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[uclinux-h8/linux.git] / arch / powerpc / kernel / process.c
index 794b754..016bd83 100644 (file)
@@ -471,49 +471,58 @@ EXPORT_SYMBOL(giveup_all);
 
 #ifdef CONFIG_PPC_BOOK3S_64
 #ifdef CONFIG_PPC_FPU
-static int restore_fp(struct task_struct *tsk)
+static bool should_restore_fp(void)
 {
-       if (tsk->thread.load_fp) {
-               load_fp_state(&current->thread.fp_state);
+       if (current->thread.load_fp) {
                current->thread.load_fp++;
-               return 1;
+               return true;
        }
-       return 0;
+       return false;
+}
+
+static void do_restore_fp(void)
+{
+       load_fp_state(&current->thread.fp_state);
 }
 #else
-static int restore_fp(struct task_struct *tsk) { return 0; }
+static bool should_restore_fp(void) { return false; }
+static void do_restore_fp(void) { }
 #endif /* CONFIG_PPC_FPU */
 
 #ifdef CONFIG_ALTIVEC
-#define loadvec(thr) ((thr).load_vec)
-static int restore_altivec(struct task_struct *tsk)
+static bool should_restore_altivec(void)
 {
-       if (cpu_has_feature(CPU_FTR_ALTIVEC) && (tsk->thread.load_vec)) {
-               load_vr_state(&tsk->thread.vr_state);
-               tsk->thread.used_vr = 1;
-               tsk->thread.load_vec++;
-
-               return 1;
+       if (cpu_has_feature(CPU_FTR_ALTIVEC) && (current->thread.load_vec)) {
+               current->thread.load_vec++;
+               return true;
        }
-       return 0;
+       return false;
+}
+
+static void do_restore_altivec(void)
+{
+       load_vr_state(&current->thread.vr_state);
+       current->thread.used_vr = 1;
 }
 #else
-#define loadvec(thr) 0
-static inline int restore_altivec(struct task_struct *tsk) { return 0; }
+static bool should_restore_altivec(void) { return false; }
+static void do_restore_altivec(void) { }
 #endif /* CONFIG_ALTIVEC */
 
 #ifdef CONFIG_VSX
-static int restore_vsx(struct task_struct *tsk)
+static bool should_restore_vsx(void)
 {
-       if (cpu_has_feature(CPU_FTR_VSX)) {
-               tsk->thread.used_vsr = 1;
-               return 1;
-       }
-
-       return 0;
+       if (cpu_has_feature(CPU_FTR_VSX))
+               return true;
+       return false;
+}
+static void do_restore_vsx(void)
+{
+       current->thread.used_vsr = 1;
 }
 #else
-static inline int restore_vsx(struct task_struct *tsk) { return 0; }
+static bool should_restore_vsx(void) { return false; }
+static void do_restore_vsx(void) { }
 #endif /* CONFIG_VSX */
 
 /*
@@ -529,32 +538,42 @@ static inline int restore_vsx(struct task_struct *tsk) { return 0; }
 void notrace restore_math(struct pt_regs *regs)
 {
        unsigned long msr;
-
-       if (!MSR_TM_ACTIVE(regs->msr) &&
-               !current->thread.load_fp && !loadvec(current->thread))
-               return;
+       unsigned long new_msr = 0;
 
        msr = regs->msr;
-       msr_check_and_set(msr_all_available);
 
        /*
-        * Only reload if the bit is not set in the user MSR, the bit BEING set
-        * indicates that the registers are hot
+        * new_msr tracks the facilities that are to be restored. Only reload
+        * if the bit is not set in the user MSR (if it is set, the registers
+        * are live for the user thread).
         */
-       if ((!(msr & MSR_FP)) && restore_fp(current))
-               msr |= MSR_FP | current->thread.fpexc_mode;
+       if ((!(msr & MSR_FP)) && should_restore_fp())
+               new_msr |= MSR_FP | current->thread.fpexc_mode;
 
-       if ((!(msr & MSR_VEC)) && restore_altivec(current))
-               msr |= MSR_VEC;
+       if ((!(msr & MSR_VEC)) && should_restore_altivec())
+               new_msr |= MSR_VEC;
 
-       if ((msr & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC) &&
-                       restore_vsx(current)) {
-               msr |= MSR_VSX;
+       if ((!(msr & MSR_VSX)) && should_restore_vsx()) {
+               if (((msr | new_msr) & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC))
+                       new_msr |= MSR_VSX;
        }
 
-       msr_check_and_clear(msr_all_available);
+       if (new_msr) {
+               msr_check_and_set(new_msr);
+
+               if (new_msr & MSR_FP)
+                       do_restore_fp();
+
+               if (new_msr & MSR_VEC)
+                       do_restore_altivec();
+
+               if (new_msr & MSR_VSX)
+                       do_restore_vsx();
 
-       regs->msr = msr;
+               msr_check_and_clear(new_msr);
+
+               regs->msr |= new_msr;
+       }
 }
 #endif
 
@@ -1599,6 +1618,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
 {
        struct pt_regs *childregs, *kregs;
        extern void ret_from_fork(void);
+       extern void ret_from_fork_scv(void);
        extern void ret_from_kernel_thread(void);
        void (*f)(void);
        unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
@@ -1635,7 +1655,9 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
                if (usp)
                        childregs->gpr[1] = usp;
                p->thread.regs = childregs;
-               childregs->gpr[3] = 0;  /* Result from fork() */
+               /* 64s sets this in ret_from_fork */
+               if (!IS_ENABLED(CONFIG_PPC_BOOK3S_64))
+                       childregs->gpr[3] = 0;  /* Result from fork() */
                if (clone_flags & CLONE_SETTLS) {
                        if (!is_32bit_task())
                                childregs->gpr[13] = tls;
@@ -1643,7 +1665,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
                                childregs->gpr[2] = tls;
                }
 
-               f = ret_from_fork;
+               if (trap_is_scv(regs))
+                       f = ret_from_fork_scv;
+               else
+                       f = ret_from_fork;
        }
        childregs->msr &= ~(MSR_FP|MSR_VEC|MSR_VSX);
        sp -= STACK_FRAME_OVERHEAD;