From 6a274c08f2f4dfac7167bbd849621f3a2b55d424 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 2 Jul 2008 14:06:37 +1000 Subject: [PATCH] powerpc: Clean up copy_to/from_user for vsx and fpr This merges and cleans up some of the ugly copy/to from user code which is required for the new fpr and vsx layout in the thread_struct. Also fixes some hard coded buffer sizes and removes a redundant fpr_flush_to_thread. Signed-off-by: Michael Neuling Signed-off-by: Paul Mackerras --- arch/powerpc/kernel/signal.h | 10 +++ arch/powerpc/kernel/signal_32.c | 109 ++++++++++++++++++++++---------- arch/powerpc/kernel/signal_64.c | 43 ++++--------- 3 files changed, 96 insertions(+), 66 deletions(-) diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 77efb3d5465a..28f4b9f5fe5e 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -24,6 +24,16 @@ extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs); +extern unsigned long copy_fpr_to_user(void __user *to, + struct task_struct *task); +extern unsigned long copy_fpr_from_user(struct task_struct *task, + void __user *from); +#ifdef CONFIG_VSX +extern unsigned long copy_vsx_to_user(void __user *to, + struct task_struct *task); +extern unsigned long copy_vsx_from_user(struct task_struct *task, + void __user *from); +#endif #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 349d3487d920..9991e2a58bf4 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -328,6 +328,75 @@ struct rt_sigframe { int abigap[56]; }; +#ifdef CONFIG_VSX +unsigned long copy_fpr_to_user(void __user *to, + struct task_struct *task) +{ + double buf[ELF_NFPREG]; + int i; + + /* save FPR copy to local buffer then write to the thread_struct */ + for (i = 0; i < (ELF_NFPREG - 1) ; i++) + buf[i] = task->thread.TS_FPR(i); + memcpy(&buf[i], &task->thread.fpscr, sizeof(double)); + return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); +} + +unsigned long copy_fpr_from_user(struct task_struct *task, + void __user *from) +{ + double buf[ELF_NFPREG]; + int i; + + if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) + return 1; + for (i = 0; i < (ELF_NFPREG - 1) ; i++) + task->thread.TS_FPR(i) = buf[i]; + memcpy(&task->thread.fpscr, &buf[i], sizeof(double)); + + return 0; +} + +unsigned long copy_vsx_to_user(void __user *to, + struct task_struct *task) +{ + double buf[ELF_NVSRHALFREG]; + int i; + + /* save FPR copy to local buffer then write to the thread_struct */ + for (i = 0; i < ELF_NVSRHALFREG; i++) + buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET]; + return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); +} + +unsigned long copy_vsx_from_user(struct task_struct *task, + void __user *from) +{ + double buf[ELF_NVSRHALFREG]; + int i; + + if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) + return 1; + for (i = 0; i < ELF_NVSRHALFREG ; i++) + task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; + return 0; +} +#else +inline unsigned long copy_fpr_to_user(void __user *to, + struct task_struct *task) +{ + return __copy_to_user(to, task->thread.fpr, + ELF_NFPREG * sizeof(double)); +} + +inline unsigned long copy_fpr_from_user(struct task_struct *task, + void __user *from) +{ + return __copy_from_user(task->thread.fpr, from, + ELF_NFPREG * sizeof(double)); +} +#endif + /* * Save the current user registers on the user stack. * We only save the altivec/spe registers if the process has used @@ -337,10 +406,6 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, int sigret) { unsigned long msr = regs->msr; -#ifdef CONFIG_VSX - double buf[32]; - int i; -#endif /* Make sure floating point registers are stored in regs */ flush_fp_to_thread(current); @@ -370,14 +435,9 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) return 1; #endif /* CONFIG_ALTIVEC */ -#ifdef CONFIG_VSX - /* save FPR copy to local buffer then write to the thread_struct */ - flush_fp_to_thread(current); - for (i = 0; i < 32 ; i++) - buf[i] = current->thread.TS_FPR(i); - memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); - if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double))) + if (copy_fpr_to_user(&frame->mc_fregs, current)) return 1; +#ifdef CONFIG_VSX /* * Copy VSR 0-31 upper half from thread_struct to local * buffer, then write that to userspace. Also set MSR_VSX in @@ -386,18 +446,10 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, */ if (current->thread.used_vsr) { flush_vsx_to_thread(current); - for (i = 0; i < 32 ; i++) - buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; - if (__copy_to_user(&frame->mc_vsregs, buf, - ELF_NVSRHALFREG * sizeof(double))) + if (copy_vsx_to_user(&frame->mc_vsregs, current)) return 1; msr |= MSR_VSX; } -#else - /* save floating-point registers */ - if (__copy_to_user(&frame->mc_fregs, current->thread.fpr, - ELF_NFPREG * sizeof(double))) - return 1; #endif /* CONFIG_VSX */ #ifdef CONFIG_SPE /* save spe registers */ @@ -442,7 +494,6 @@ static long restore_user_regs(struct pt_regs *regs, unsigned int save_r2 = 0; unsigned long msr; #ifdef CONFIG_VSX - double buf[32]; int i; #endif @@ -490,13 +541,10 @@ static long restore_user_regs(struct pt_regs *regs, if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) return 1; #endif /* CONFIG_ALTIVEC */ + if (copy_fpr_from_user(current, &sr->mc_fregs)) + return 1; #ifdef CONFIG_VSX - if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs))) - return 1; - for (i = 0; i < 32 ; i++) - current->thread.TS_FPR(i) = buf[i]; - memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); /* * Force the process to reload the VSX registers from * current->thread when it next does VSX instruction. @@ -507,18 +555,11 @@ static long restore_user_regs(struct pt_regs *regs, * Restore altivec registers from the stack to a local * buffer, then write this out to the thread_struct */ - if (__copy_from_user(buf, &sr->mc_vsregs, - sizeof(sr->mc_vsregs))) + if (copy_vsx_from_user(current, &sr->mc_vsregs)) return 1; - for (i = 0; i < 32 ; i++) - current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; } else if (current->thread.used_vsr) for (i = 0; i < 32 ; i++) current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; -#else - if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, - sizeof(sr->mc_fregs))) - return 1; #endif /* CONFIG_VSX */ /* * force the process to reload the FP registers from diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 8214e57aab67..93ebfb6944b6 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -89,10 +89,6 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, #endif unsigned long msr = regs->msr; long err = 0; -#ifdef CONFIG_VSX - double buf[FP_REGS_SIZE]; - int i; -#endif flush_fp_to_thread(current); @@ -117,12 +113,9 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, err |= __put_user(0, &sc->v_regs); #endif /* CONFIG_ALTIVEC */ flush_fp_to_thread(current); + /* copy fpr regs and fpscr */ + err |= copy_fpr_to_user(&sc->fp_regs, current); #ifdef CONFIG_VSX - /* Copy FP to local buffer then write that out */ - for (i = 0; i < 32 ; i++) - buf[i] = current->thread.TS_FPR(i); - memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); - err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE); /* * Copy VSX low doubleword to local buffer for formatting, * then out to userspace. Update v_regs to point after the @@ -131,17 +124,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, if (current->thread.used_vsr) { flush_vsx_to_thread(current); v_regs += ELF_NVRREG; - for (i = 0; i < 32 ; i++) - buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; - err |= __copy_to_user(v_regs, buf, 32 * sizeof(double)); + err |= copy_vsx_to_user(v_regs, current); /* set MSR_VSX in the MSR value in the frame to * indicate that sc->vs_reg) contains valid data. */ msr |= MSR_VSX; } -#else /* CONFIG_VSX */ - /* copy fpr regs and fpscr */ - err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); #endif /* CONFIG_VSX */ err |= __put_user(&sc->gp_regs, &sc->regs); WARN_ON(!FULL_REGS(regs)); @@ -164,14 +152,13 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, { #ifdef CONFIG_ALTIVEC elf_vrreg_t __user *v_regs; -#endif -#ifdef CONFIG_VSX - double buf[FP_REGS_SIZE]; - int i; #endif unsigned long err = 0; unsigned long save_r13 = 0; unsigned long msr; +#ifdef CONFIG_VSX + int i; +#endif /* If this is not a signal return, we preserve the TLS in r13 */ if (!sig) @@ -234,15 +221,9 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, else current->thread.vrsave = 0; #endif /* CONFIG_ALTIVEC */ -#ifdef CONFIG_VSX /* restore floating point */ - err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE); - if (err) - return err; - for (i = 0; i < 32 ; i++) - current->thread.TS_FPR(i) = buf[i]; - memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); - + err |= copy_fpr_from_user(current, &sc->fp_regs); +#ifdef CONFIG_VSX /* * Get additional VSX data. Update v_regs to point after the * VMX data. Copy VSX low doubleword from userspace to local @@ -250,14 +231,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, */ v_regs += ELF_NVRREG; if ((msr & MSR_VSX) != 0) - err |= __copy_from_user(buf, v_regs, 32 * sizeof(double)); + err |= copy_vsx_from_user(current, v_regs); else - memset(buf, 0, 32 * sizeof(double)); + for (i = 0; i < 32 ; i++) + current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; - for (i = 0; i < 32 ; i++) - current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; #else - err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); #endif return err; } -- 2.20.1