| From 5598473a5b40c47a8c5349dd2c2630797169cf1a Mon Sep 17 00:00:00 2001 |
| From: "David S. Miller" <davem@davemloft.net> |
| Date: Sat, 20 Aug 2011 17:14:54 -0700 |
| Subject: sparc: Allow handling signals when stack is corrupted. |
| |
| From: "David S. Miller" <davem@davemloft.net> |
| |
| commit 5598473a5b40c47a8c5349dd2c2630797169cf1a upstream. |
| |
| If we can't push the pending register windows onto the user's stack, |
| we disallow signal delivery even if the signal would be delivered on a |
| valid seperate signal stack. |
| |
| Add a register window save area in the signal frame, and store any |
| unsavable windows there. |
| |
| On sigreturn, if any windows are still queued up in the signal frame, |
| try to push them back onto the stack and if that fails we kill the |
| process immediately. |
| |
| This allows the debug/tst-longjmp_chk2 glibc test case to pass. |
| |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| arch/sparc/include/asm/sigcontext.h | 14 ++ |
| arch/sparc/kernel/Makefile | 1 |
| arch/sparc/kernel/signal32.c | 184 ++++++++++++++++++++---------------- |
| arch/sparc/kernel/signal_32.c | 172 +++++++++++++++------------------ |
| arch/sparc/kernel/signal_64.c | 108 +++++++++------------ |
| arch/sparc/kernel/sigutil.h | 9 + |
| arch/sparc/kernel/sigutil_32.c | 120 +++++++++++++++++++++++ |
| arch/sparc/kernel/sigutil_64.c | 93 ++++++++++++++++++ |
| 8 files changed, 468 insertions(+), 233 deletions(-) |
| |
| --- a/arch/sparc/include/asm/sigcontext.h |
| +++ b/arch/sparc/include/asm/sigcontext.h |
| @@ -45,6 +45,19 @@ typedef struct { |
| int si_mask; |
| } __siginfo32_t; |
| |
| +#define __SIGC_MAXWIN 7 |
| + |
| +typedef struct { |
| + unsigned long locals[8]; |
| + unsigned long ins[8]; |
| +} __siginfo_reg_window; |
| + |
| +typedef struct { |
| + int wsaved; |
| + __siginfo_reg_window reg_window[__SIGC_MAXWIN]; |
| + unsigned long rwbuf_stkptrs[__SIGC_MAXWIN]; |
| +} __siginfo_rwin_t; |
| + |
| #ifdef CONFIG_SPARC64 |
| typedef struct { |
| unsigned int si_float_regs [64]; |
| @@ -73,6 +86,7 @@ struct sigcontext { |
| unsigned long ss_size; |
| } sigc_stack; |
| unsigned long sigc_mask; |
| + __siginfo_rwin_t * sigc_rwin_save; |
| }; |
| |
| #else |
| --- a/arch/sparc/kernel/Makefile |
| +++ b/arch/sparc/kernel/Makefile |
| @@ -24,6 +24,7 @@ obj-$(CONFIG_SPARC32) += sun4m_irq.o s |
| |
| obj-y += process_$(BITS).o |
| obj-y += signal_$(BITS).o |
| +obj-y += sigutil_$(BITS).o |
| obj-$(CONFIG_SPARC32) += ioport.o |
| obj-y += setup_$(BITS).o |
| obj-y += idprom.o |
| --- a/arch/sparc/kernel/signal32.c |
| +++ b/arch/sparc/kernel/signal32.c |
| @@ -29,6 +29,8 @@ |
| #include <asm/visasm.h> |
| #include <asm/compat_signal.h> |
| |
| +#include "sigutil.h" |
| + |
| #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
| |
| /* This magic should be in g_upper[0] for all upper parts |
| @@ -44,14 +46,14 @@ typedef struct { |
| struct signal_frame32 { |
| struct sparc_stackf32 ss; |
| __siginfo32_t info; |
| - /* __siginfo_fpu32_t * */ u32 fpu_save; |
| + /* __siginfo_fpu_t * */ u32 fpu_save; |
| unsigned int insns[2]; |
| unsigned int extramask[_COMPAT_NSIG_WORDS - 1]; |
| unsigned int extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */ |
| /* Only valid if (info.si_regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */ |
| siginfo_extra_v8plus_t v8plus; |
| - __siginfo_fpu_t fpu_state; |
| -}; |
| + /* __siginfo_rwin_t * */u32 rwin_save; |
| +} __attribute__((aligned(8))); |
| |
| typedef struct compat_siginfo{ |
| int si_signo; |
| @@ -110,18 +112,14 @@ struct rt_signal_frame32 { |
| compat_siginfo_t info; |
| struct pt_regs32 regs; |
| compat_sigset_t mask; |
| - /* __siginfo_fpu32_t * */ u32 fpu_save; |
| + /* __siginfo_fpu_t * */ u32 fpu_save; |
| unsigned int insns[2]; |
| stack_t32 stack; |
| unsigned int extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */ |
| /* Only valid if (regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */ |
| siginfo_extra_v8plus_t v8plus; |
| - __siginfo_fpu_t fpu_state; |
| -}; |
| - |
| -/* Align macros */ |
| -#define SF_ALIGNEDSZ (((sizeof(struct signal_frame32) + 15) & (~15))) |
| -#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame32) + 15) & (~15))) |
| + /* __siginfo_rwin_t * */u32 rwin_save; |
| +} __attribute__((aligned(8))); |
| |
| int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) |
| { |
| @@ -192,30 +190,13 @@ int copy_siginfo_from_user32(siginfo_t * |
| return 0; |
| } |
| |
| -static int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| -{ |
| - unsigned long *fpregs = current_thread_info()->fpregs; |
| - unsigned long fprs; |
| - int err; |
| - |
| - err = __get_user(fprs, &fpu->si_fprs); |
| - fprs_write(0); |
| - regs->tstate &= ~TSTATE_PEF; |
| - if (fprs & FPRS_DL) |
| - err |= copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32)); |
| - if (fprs & FPRS_DU) |
| - err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32)); |
| - err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); |
| - err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); |
| - current_thread_info()->fpsaved[0] |= fprs; |
| - return err; |
| -} |
| - |
| void do_sigreturn32(struct pt_regs *regs) |
| { |
| struct signal_frame32 __user *sf; |
| + compat_uptr_t fpu_save; |
| + compat_uptr_t rwin_save; |
| unsigned int psr; |
| - unsigned pc, npc, fpu_save; |
| + unsigned pc, npc; |
| sigset_t set; |
| unsigned seta[_COMPAT_NSIG_WORDS]; |
| int err, i; |
| @@ -273,8 +254,13 @@ void do_sigreturn32(struct pt_regs *regs |
| pt_regs_clear_syscall(regs); |
| |
| err |= __get_user(fpu_save, &sf->fpu_save); |
| - if (fpu_save) |
| - err |= restore_fpu_state32(regs, &sf->fpu_state); |
| + if (!err && fpu_save) |
| + err |= restore_fpu_state(regs, compat_ptr(fpu_save)); |
| + err |= __get_user(rwin_save, &sf->rwin_save); |
| + if (!err && rwin_save) { |
| + if (restore_rwin_state(compat_ptr(rwin_save))) |
| + goto segv; |
| + } |
| err |= __get_user(seta[0], &sf->info.si_mask); |
| err |= copy_from_user(seta+1, &sf->extramask, |
| (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int)); |
| @@ -300,7 +286,9 @@ segv: |
| asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) |
| { |
| struct rt_signal_frame32 __user *sf; |
| - unsigned int psr, pc, npc, fpu_save, u_ss_sp; |
| + unsigned int psr, pc, npc, u_ss_sp; |
| + compat_uptr_t fpu_save; |
| + compat_uptr_t rwin_save; |
| mm_segment_t old_fs; |
| sigset_t set; |
| compat_sigset_t seta; |
| @@ -359,8 +347,8 @@ asmlinkage void do_rt_sigreturn32(struct |
| pt_regs_clear_syscall(regs); |
| |
| err |= __get_user(fpu_save, &sf->fpu_save); |
| - if (fpu_save) |
| - err |= restore_fpu_state32(regs, &sf->fpu_state); |
| + if (!err && fpu_save) |
| + err |= restore_fpu_state(regs, compat_ptr(fpu_save)); |
| err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t)); |
| err |= __get_user(u_ss_sp, &sf->stack.ss_sp); |
| st.ss_sp = compat_ptr(u_ss_sp); |
| @@ -376,6 +364,12 @@ asmlinkage void do_rt_sigreturn32(struct |
| do_sigaltstack((stack_t __user *) &st, NULL, (unsigned long)sf); |
| set_fs(old_fs); |
| |
| + err |= __get_user(rwin_save, &sf->rwin_save); |
| + if (!err && rwin_save) { |
| + if (restore_rwin_state(compat_ptr(rwin_save))) |
| + goto segv; |
| + } |
| + |
| switch (_NSIG_WORDS) { |
| case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32); |
| case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32); |
| @@ -433,26 +427,6 @@ static void __user *get_sigframe(struct |
| return (void __user *) sp; |
| } |
| |
| -static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| -{ |
| - unsigned long *fpregs = current_thread_info()->fpregs; |
| - unsigned long fprs; |
| - int err = 0; |
| - |
| - fprs = current_thread_info()->fpsaved[0]; |
| - if (fprs & FPRS_DL) |
| - err |= copy_to_user(&fpu->si_float_regs[0], fpregs, |
| - (sizeof(unsigned int) * 32)); |
| - if (fprs & FPRS_DU) |
| - err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, |
| - (sizeof(unsigned int) * 32)); |
| - err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); |
| - err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); |
| - err |= __put_user(fprs, &fpu->si_fprs); |
| - |
| - return err; |
| -} |
| - |
| /* The I-cache flush instruction only works in the primary ASI, which |
| * right now is the nucleus, aka. kernel space. |
| * |
| @@ -515,18 +489,23 @@ static int setup_frame32(struct k_sigact |
| int signo, sigset_t *oldset) |
| { |
| struct signal_frame32 __user *sf; |
| + int i, err, wsaved; |
| + void __user *tail; |
| int sigframe_size; |
| u32 psr; |
| - int i, err; |
| unsigned int seta[_COMPAT_NSIG_WORDS]; |
| |
| /* 1. Make sure everything is clean */ |
| synchronize_user_stack(); |
| save_and_clear_fpu(); |
| |
| - sigframe_size = SF_ALIGNEDSZ; |
| - if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) |
| - sigframe_size -= sizeof(__siginfo_fpu_t); |
| + wsaved = get_thread_wsaved(); |
| + |
| + sigframe_size = sizeof(*sf); |
| + if (current_thread_info()->fpsaved[0] & FPRS_FEF) |
| + sigframe_size += sizeof(__siginfo_fpu_t); |
| + if (wsaved) |
| + sigframe_size += sizeof(__siginfo_rwin_t); |
| |
| sf = (struct signal_frame32 __user *) |
| get_sigframe(&ka->sa, regs, sigframe_size); |
| @@ -534,8 +513,7 @@ static int setup_frame32(struct k_sigact |
| if (invalid_frame_pointer(sf, sigframe_size)) |
| goto sigill; |
| |
| - if (get_thread_wsaved() != 0) |
| - goto sigill; |
| + tail = (sf + 1); |
| |
| /* 2. Save the current process state */ |
| if (test_thread_flag(TIF_32BIT)) { |
| @@ -560,11 +538,22 @@ static int setup_frame32(struct k_sigact |
| &sf->v8plus.asi); |
| |
| if (psr & PSR_EF) { |
| - err |= save_fpu_state32(regs, &sf->fpu_state); |
| - err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); |
| + __siginfo_fpu_t __user *fp = tail; |
| + tail += sizeof(*fp); |
| + err |= save_fpu_state(regs, fp); |
| + err |= __put_user((u64)fp, &sf->fpu_save); |
| } else { |
| err |= __put_user(0, &sf->fpu_save); |
| } |
| + if (wsaved) { |
| + __siginfo_rwin_t __user *rwp = tail; |
| + tail += sizeof(*rwp); |
| + err |= save_rwin_state(wsaved, rwp); |
| + err |= __put_user((u64)rwp, &sf->rwin_save); |
| + set_thread_wsaved(0); |
| + } else { |
| + err |= __put_user(0, &sf->rwin_save); |
| + } |
| |
| switch (_NSIG_WORDS) { |
| case 4: seta[7] = (oldset->sig[3] >> 32); |
| @@ -580,10 +569,21 @@ static int setup_frame32(struct k_sigact |
| err |= __copy_to_user(sf->extramask, seta + 1, |
| (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int)); |
| |
| - err |= copy_in_user((u32 __user *)sf, |
| - (u32 __user *)(regs->u_regs[UREG_FP]), |
| - sizeof(struct reg_window32)); |
| - |
| + if (!wsaved) { |
| + err |= copy_in_user((u32 __user *)sf, |
| + (u32 __user *)(regs->u_regs[UREG_FP]), |
| + sizeof(struct reg_window32)); |
| + } else { |
| + struct reg_window *rp; |
| + |
| + rp = ¤t_thread_info()->reg_window[wsaved - 1]; |
| + for (i = 0; i < 8; i++) |
| + err |= __put_user(rp->locals[i], &sf->ss.locals[i]); |
| + for (i = 0; i < 6; i++) |
| + err |= __put_user(rp->ins[i], &sf->ss.ins[i]); |
| + err |= __put_user(rp->ins[6], &sf->ss.fp); |
| + err |= __put_user(rp->ins[7], &sf->ss.callers_pc); |
| + } |
| if (err) |
| goto sigsegv; |
| |
| @@ -613,7 +613,6 @@ static int setup_frame32(struct k_sigact |
| err |= __put_user(0x91d02010, &sf->insns[1]); /*t 0x10*/ |
| if (err) |
| goto sigsegv; |
| - |
| flush_signal_insns(address); |
| } |
| return 0; |
| @@ -632,18 +631,23 @@ static int setup_rt_frame32(struct k_sig |
| siginfo_t *info) |
| { |
| struct rt_signal_frame32 __user *sf; |
| + int i, err, wsaved; |
| + void __user *tail; |
| int sigframe_size; |
| u32 psr; |
| - int i, err; |
| compat_sigset_t seta; |
| |
| /* 1. Make sure everything is clean */ |
| synchronize_user_stack(); |
| save_and_clear_fpu(); |
| |
| - sigframe_size = RT_ALIGNEDSZ; |
| - if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) |
| - sigframe_size -= sizeof(__siginfo_fpu_t); |
| + wsaved = get_thread_wsaved(); |
| + |
| + sigframe_size = sizeof(*sf); |
| + if (current_thread_info()->fpsaved[0] & FPRS_FEF) |
| + sigframe_size += sizeof(__siginfo_fpu_t); |
| + if (wsaved) |
| + sigframe_size += sizeof(__siginfo_rwin_t); |
| |
| sf = (struct rt_signal_frame32 __user *) |
| get_sigframe(&ka->sa, regs, sigframe_size); |
| @@ -651,8 +655,7 @@ static int setup_rt_frame32(struct k_sig |
| if (invalid_frame_pointer(sf, sigframe_size)) |
| goto sigill; |
| |
| - if (get_thread_wsaved() != 0) |
| - goto sigill; |
| + tail = (sf + 1); |
| |
| /* 2. Save the current process state */ |
| if (test_thread_flag(TIF_32BIT)) { |
| @@ -677,11 +680,22 @@ static int setup_rt_frame32(struct k_sig |
| &sf->v8plus.asi); |
| |
| if (psr & PSR_EF) { |
| - err |= save_fpu_state32(regs, &sf->fpu_state); |
| - err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); |
| + __siginfo_fpu_t __user *fp = tail; |
| + tail += sizeof(*fp); |
| + err |= save_fpu_state(regs, fp); |
| + err |= __put_user((u64)fp, &sf->fpu_save); |
| } else { |
| err |= __put_user(0, &sf->fpu_save); |
| } |
| + if (wsaved) { |
| + __siginfo_rwin_t __user *rwp = tail; |
| + tail += sizeof(*rwp); |
| + err |= save_rwin_state(wsaved, rwp); |
| + err |= __put_user((u64)rwp, &sf->rwin_save); |
| + set_thread_wsaved(0); |
| + } else { |
| + err |= __put_user(0, &sf->rwin_save); |
| + } |
| |
| /* Update the siginfo structure. */ |
| err |= copy_siginfo_to_user32(&sf->info, info); |
| @@ -703,9 +717,21 @@ static int setup_rt_frame32(struct k_sig |
| } |
| err |= __copy_to_user(&sf->mask, &seta, sizeof(compat_sigset_t)); |
| |
| - err |= copy_in_user((u32 __user *)sf, |
| - (u32 __user *)(regs->u_regs[UREG_FP]), |
| - sizeof(struct reg_window32)); |
| + if (!wsaved) { |
| + err |= copy_in_user((u32 __user *)sf, |
| + (u32 __user *)(regs->u_regs[UREG_FP]), |
| + sizeof(struct reg_window32)); |
| + } else { |
| + struct reg_window *rp; |
| + |
| + rp = ¤t_thread_info()->reg_window[wsaved - 1]; |
| + for (i = 0; i < 8; i++) |
| + err |= __put_user(rp->locals[i], &sf->ss.locals[i]); |
| + for (i = 0; i < 6; i++) |
| + err |= __put_user(rp->ins[i], &sf->ss.ins[i]); |
| + err |= __put_user(rp->ins[6], &sf->ss.fp); |
| + err |= __put_user(rp->ins[7], &sf->ss.callers_pc); |
| + } |
| if (err) |
| goto sigsegv; |
| |
| --- a/arch/sparc/kernel/signal_32.c |
| +++ b/arch/sparc/kernel/signal_32.c |
| @@ -26,6 +26,8 @@ |
| #include <asm/pgtable.h> |
| #include <asm/cacheflush.h> /* flush_sig_insns */ |
| |
| +#include "sigutil.h" |
| + |
| #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
| |
| extern void fpsave(unsigned long *fpregs, unsigned long *fsr, |
| @@ -39,8 +41,8 @@ struct signal_frame { |
| unsigned long insns[2] __attribute__ ((aligned (8))); |
| unsigned int extramask[_NSIG_WORDS - 1]; |
| unsigned int extra_size; /* Should be 0 */ |
| - __siginfo_fpu_t fpu_state; |
| -}; |
| + __siginfo_rwin_t __user *rwin_save; |
| +} __attribute__((aligned(8))); |
| |
| struct rt_signal_frame { |
| struct sparc_stackf ss; |
| @@ -51,8 +53,8 @@ struct rt_signal_frame { |
| unsigned int insns[2]; |
| stack_t stack; |
| unsigned int extra_size; /* Should be 0 */ |
| - __siginfo_fpu_t fpu_state; |
| -}; |
| + __siginfo_rwin_t __user *rwin_save; |
| +} __attribute__((aligned(8))); |
| |
| /* Align macros */ |
| #define SF_ALIGNEDSZ (((sizeof(struct signal_frame) + 7) & (~7))) |
| @@ -79,43 +81,13 @@ asmlinkage int sys_sigsuspend(old_sigset |
| return _sigpause_common(set); |
| } |
| |
| -static inline int |
| -restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| -{ |
| - int err; |
| -#ifdef CONFIG_SMP |
| - if (test_tsk_thread_flag(current, TIF_USEDFPU)) |
| - regs->psr &= ~PSR_EF; |
| -#else |
| - if (current == last_task_used_math) { |
| - last_task_used_math = NULL; |
| - regs->psr &= ~PSR_EF; |
| - } |
| -#endif |
| - set_used_math(); |
| - clear_tsk_thread_flag(current, TIF_USEDFPU); |
| - |
| - if (!access_ok(VERIFY_READ, fpu, sizeof(*fpu))) |
| - return -EFAULT; |
| - |
| - err = __copy_from_user(¤t->thread.float_regs[0], &fpu->si_float_regs[0], |
| - (sizeof(unsigned long) * 32)); |
| - err |= __get_user(current->thread.fsr, &fpu->si_fsr); |
| - err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth); |
| - if (current->thread.fpqdepth != 0) |
| - err |= __copy_from_user(¤t->thread.fpqueue[0], |
| - &fpu->si_fpqueue[0], |
| - ((sizeof(unsigned long) + |
| - (sizeof(unsigned long *)))*16)); |
| - return err; |
| -} |
| - |
| asmlinkage void do_sigreturn(struct pt_regs *regs) |
| { |
| struct signal_frame __user *sf; |
| unsigned long up_psr, pc, npc; |
| sigset_t set; |
| __siginfo_fpu_t __user *fpu_save; |
| + __siginfo_rwin_t __user *rwin_save; |
| int err; |
| |
| /* Always make any pending restarted system calls return -EINTR */ |
| @@ -150,9 +122,11 @@ asmlinkage void do_sigreturn(struct pt_r |
| pt_regs_clear_syscall(regs); |
| |
| err |= __get_user(fpu_save, &sf->fpu_save); |
| - |
| if (fpu_save) |
| err |= restore_fpu_state(regs, fpu_save); |
| + err |= __get_user(rwin_save, &sf->rwin_save); |
| + if (rwin_save) |
| + err |= restore_rwin_state(rwin_save); |
| |
| /* This is pretty much atomic, no amount locking would prevent |
| * the races which exist anyways. |
| @@ -180,6 +154,7 @@ asmlinkage void do_rt_sigreturn(struct p |
| struct rt_signal_frame __user *sf; |
| unsigned int psr, pc, npc; |
| __siginfo_fpu_t __user *fpu_save; |
| + __siginfo_rwin_t __user *rwin_save; |
| mm_segment_t old_fs; |
| sigset_t set; |
| stack_t st; |
| @@ -207,8 +182,7 @@ asmlinkage void do_rt_sigreturn(struct p |
| pt_regs_clear_syscall(regs); |
| |
| err |= __get_user(fpu_save, &sf->fpu_save); |
| - |
| - if (fpu_save) |
| + if (!err && fpu_save) |
| err |= restore_fpu_state(regs, fpu_save); |
| err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t)); |
| |
| @@ -228,6 +202,12 @@ asmlinkage void do_rt_sigreturn(struct p |
| do_sigaltstack((const stack_t __user *) &st, NULL, (unsigned long)sf); |
| set_fs(old_fs); |
| |
| + err |= __get_user(rwin_save, &sf->rwin_save); |
| + if (!err && rwin_save) { |
| + if (restore_rwin_state(rwin_save)) |
| + goto segv; |
| + } |
| + |
| sigdelsetmask(&set, ~_BLOCKABLE); |
| spin_lock_irq(¤t->sighand->siglock); |
| current->blocked = set; |
| @@ -280,53 +260,23 @@ static inline void __user *get_sigframe( |
| return (void __user *) sp; |
| } |
| |
| -static inline int |
| -save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| -{ |
| - int err = 0; |
| -#ifdef CONFIG_SMP |
| - if (test_tsk_thread_flag(current, TIF_USEDFPU)) { |
| - put_psr(get_psr() | PSR_EF); |
| - fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr, |
| - ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); |
| - regs->psr &= ~(PSR_EF); |
| - clear_tsk_thread_flag(current, TIF_USEDFPU); |
| - } |
| -#else |
| - if (current == last_task_used_math) { |
| - put_psr(get_psr() | PSR_EF); |
| - fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr, |
| - ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); |
| - last_task_used_math = NULL; |
| - regs->psr &= ~(PSR_EF); |
| - } |
| -#endif |
| - err |= __copy_to_user(&fpu->si_float_regs[0], |
| - ¤t->thread.float_regs[0], |
| - (sizeof(unsigned long) * 32)); |
| - err |= __put_user(current->thread.fsr, &fpu->si_fsr); |
| - err |= __put_user(current->thread.fpqdepth, &fpu->si_fpqdepth); |
| - if (current->thread.fpqdepth != 0) |
| - err |= __copy_to_user(&fpu->si_fpqueue[0], |
| - ¤t->thread.fpqueue[0], |
| - ((sizeof(unsigned long) + |
| - (sizeof(unsigned long *)))*16)); |
| - clear_used_math(); |
| - return err; |
| -} |
| - |
| static int setup_frame(struct k_sigaction *ka, struct pt_regs *regs, |
| int signo, sigset_t *oldset) |
| { |
| struct signal_frame __user *sf; |
| - int sigframe_size, err; |
| + int sigframe_size, err, wsaved; |
| + void __user *tail; |
| |
| /* 1. Make sure everything is clean */ |
| synchronize_user_stack(); |
| |
| - sigframe_size = SF_ALIGNEDSZ; |
| - if (!used_math()) |
| - sigframe_size -= sizeof(__siginfo_fpu_t); |
| + wsaved = current_thread_info()->w_saved; |
| + |
| + sigframe_size = sizeof(*sf); |
| + if (used_math()) |
| + sigframe_size += sizeof(__siginfo_fpu_t); |
| + if (wsaved) |
| + sigframe_size += sizeof(__siginfo_rwin_t); |
| |
| sf = (struct signal_frame __user *) |
| get_sigframe(&ka->sa, regs, sigframe_size); |
| @@ -334,8 +284,7 @@ static int setup_frame(struct k_sigactio |
| if (invalid_frame_pointer(sf, sigframe_size)) |
| goto sigill_and_return; |
| |
| - if (current_thread_info()->w_saved != 0) |
| - goto sigill_and_return; |
| + tail = sf + 1; |
| |
| /* 2. Save the current process state */ |
| err = __copy_to_user(&sf->info.si_regs, regs, sizeof(struct pt_regs)); |
| @@ -343,17 +292,34 @@ static int setup_frame(struct k_sigactio |
| err |= __put_user(0, &sf->extra_size); |
| |
| if (used_math()) { |
| - err |= save_fpu_state(regs, &sf->fpu_state); |
| - err |= __put_user(&sf->fpu_state, &sf->fpu_save); |
| + __siginfo_fpu_t __user *fp = tail; |
| + tail += sizeof(*fp); |
| + err |= save_fpu_state(regs, fp); |
| + err |= __put_user(fp, &sf->fpu_save); |
| } else { |
| err |= __put_user(0, &sf->fpu_save); |
| } |
| + if (wsaved) { |
| + __siginfo_rwin_t __user *rwp = tail; |
| + tail += sizeof(*rwp); |
| + err |= save_rwin_state(wsaved, rwp); |
| + err |= __put_user(rwp, &sf->rwin_save); |
| + } else { |
| + err |= __put_user(0, &sf->rwin_save); |
| + } |
| |
| err |= __put_user(oldset->sig[0], &sf->info.si_mask); |
| err |= __copy_to_user(sf->extramask, &oldset->sig[1], |
| (_NSIG_WORDS - 1) * sizeof(unsigned int)); |
| - err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP], |
| - sizeof(struct reg_window32)); |
| + if (!wsaved) { |
| + err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP], |
| + sizeof(struct reg_window32)); |
| + } else { |
| + struct reg_window32 *rp; |
| + |
| + rp = ¤t_thread_info()->reg_window[wsaved - 1]; |
| + err |= __copy_to_user(sf, rp, sizeof(struct reg_window32)); |
| + } |
| if (err) |
| goto sigsegv; |
| |
| @@ -399,21 +365,24 @@ static int setup_rt_frame(struct k_sigac |
| int signo, sigset_t *oldset, siginfo_t *info) |
| { |
| struct rt_signal_frame __user *sf; |
| - int sigframe_size; |
| + int sigframe_size, wsaved; |
| + void __user *tail; |
| unsigned int psr; |
| int err; |
| |
| synchronize_user_stack(); |
| - sigframe_size = RT_ALIGNEDSZ; |
| - if (!used_math()) |
| - sigframe_size -= sizeof(__siginfo_fpu_t); |
| + wsaved = current_thread_info()->w_saved; |
| + sigframe_size = sizeof(*sf); |
| + if (used_math()) |
| + sigframe_size += sizeof(__siginfo_fpu_t); |
| + if (wsaved) |
| + sigframe_size += sizeof(__siginfo_rwin_t); |
| sf = (struct rt_signal_frame __user *) |
| get_sigframe(&ka->sa, regs, sigframe_size); |
| if (invalid_frame_pointer(sf, sigframe_size)) |
| goto sigill; |
| - if (current_thread_info()->w_saved != 0) |
| - goto sigill; |
| |
| + tail = sf + 1; |
| err = __put_user(regs->pc, &sf->regs.pc); |
| err |= __put_user(regs->npc, &sf->regs.npc); |
| err |= __put_user(regs->y, &sf->regs.y); |
| @@ -425,11 +394,21 @@ static int setup_rt_frame(struct k_sigac |
| err |= __put_user(0, &sf->extra_size); |
| |
| if (psr & PSR_EF) { |
| - err |= save_fpu_state(regs, &sf->fpu_state); |
| - err |= __put_user(&sf->fpu_state, &sf->fpu_save); |
| + __siginfo_fpu_t *fp = tail; |
| + tail += sizeof(*fp); |
| + err |= save_fpu_state(regs, fp); |
| + err |= __put_user(fp, &sf->fpu_save); |
| } else { |
| err |= __put_user(0, &sf->fpu_save); |
| } |
| + if (wsaved) { |
| + __siginfo_rwin_t *rwp = tail; |
| + tail += sizeof(*rwp); |
| + err |= save_rwin_state(wsaved, rwp); |
| + err |= __put_user(rwp, &sf->rwin_save); |
| + } else { |
| + err |= __put_user(0, &sf->rwin_save); |
| + } |
| err |= __copy_to_user(&sf->mask, &oldset->sig[0], sizeof(sigset_t)); |
| |
| /* Setup sigaltstack */ |
| @@ -437,8 +416,15 @@ static int setup_rt_frame(struct k_sigac |
| err |= __put_user(sas_ss_flags(regs->u_regs[UREG_FP]), &sf->stack.ss_flags); |
| err |= __put_user(current->sas_ss_size, &sf->stack.ss_size); |
| |
| - err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP], |
| - sizeof(struct reg_window32)); |
| + if (!wsaved) { |
| + err |= __copy_to_user(sf, (char *) regs->u_regs[UREG_FP], |
| + sizeof(struct reg_window32)); |
| + } else { |
| + struct reg_window32 *rp; |
| + |
| + rp = ¤t_thread_info()->reg_window[wsaved - 1]; |
| + err |= __copy_to_user(sf, rp, sizeof(struct reg_window32)); |
| + } |
| |
| err |= copy_siginfo_to_user(&sf->info, info); |
| |
| --- a/arch/sparc/kernel/signal_64.c |
| +++ b/arch/sparc/kernel/signal_64.c |
| @@ -34,6 +34,7 @@ |
| |
| #include "entry.h" |
| #include "systbls.h" |
| +#include "sigutil.h" |
| |
| #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
| |
| @@ -236,7 +237,7 @@ struct rt_signal_frame { |
| __siginfo_fpu_t __user *fpu_save; |
| stack_t stack; |
| sigset_t mask; |
| - __siginfo_fpu_t fpu_state; |
| + __siginfo_rwin_t *rwin_save; |
| }; |
| |
| static long _sigpause_common(old_sigset_t set) |
| @@ -266,33 +267,12 @@ asmlinkage long sys_sigsuspend(old_sigse |
| return _sigpause_common(set); |
| } |
| |
| -static inline int |
| -restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| -{ |
| - unsigned long *fpregs = current_thread_info()->fpregs; |
| - unsigned long fprs; |
| - int err; |
| - |
| - err = __get_user(fprs, &fpu->si_fprs); |
| - fprs_write(0); |
| - regs->tstate &= ~TSTATE_PEF; |
| - if (fprs & FPRS_DL) |
| - err |= copy_from_user(fpregs, &fpu->si_float_regs[0], |
| - (sizeof(unsigned int) * 32)); |
| - if (fprs & FPRS_DU) |
| - err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], |
| - (sizeof(unsigned int) * 32)); |
| - err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); |
| - err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); |
| - current_thread_info()->fpsaved[0] |= fprs; |
| - return err; |
| -} |
| - |
| void do_rt_sigreturn(struct pt_regs *regs) |
| { |
| struct rt_signal_frame __user *sf; |
| unsigned long tpc, tnpc, tstate; |
| __siginfo_fpu_t __user *fpu_save; |
| + __siginfo_rwin_t __user *rwin_save; |
| sigset_t set; |
| int err; |
| |
| @@ -325,8 +305,8 @@ void do_rt_sigreturn(struct pt_regs *reg |
| regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)); |
| |
| err |= __get_user(fpu_save, &sf->fpu_save); |
| - if (fpu_save) |
| - err |= restore_fpu_state(regs, &sf->fpu_state); |
| + if (!err && fpu_save) |
| + err |= restore_fpu_state(regs, fpu_save); |
| |
| err |= __copy_from_user(&set, &sf->mask, sizeof(sigset_t)); |
| err |= do_sigaltstack(&sf->stack, NULL, (unsigned long)sf); |
| @@ -334,6 +314,12 @@ void do_rt_sigreturn(struct pt_regs *reg |
| if (err) |
| goto segv; |
| |
| + err |= __get_user(rwin_save, &sf->rwin_save); |
| + if (!err && rwin_save) { |
| + if (restore_rwin_state(rwin_save)) |
| + goto segv; |
| + } |
| + |
| regs->tpc = tpc; |
| regs->tnpc = tnpc; |
| |
| @@ -351,34 +337,13 @@ segv: |
| } |
| |
| /* Checks if the fp is valid */ |
| -static int invalid_frame_pointer(void __user *fp, int fplen) |
| +static int invalid_frame_pointer(void __user *fp) |
| { |
| if (((unsigned long) fp) & 15) |
| return 1; |
| return 0; |
| } |
| |
| -static inline int |
| -save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| -{ |
| - unsigned long *fpregs = current_thread_info()->fpregs; |
| - unsigned long fprs; |
| - int err = 0; |
| - |
| - fprs = current_thread_info()->fpsaved[0]; |
| - if (fprs & FPRS_DL) |
| - err |= copy_to_user(&fpu->si_float_regs[0], fpregs, |
| - (sizeof(unsigned int) * 32)); |
| - if (fprs & FPRS_DU) |
| - err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, |
| - (sizeof(unsigned int) * 32)); |
| - err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); |
| - err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); |
| - err |= __put_user(fprs, &fpu->si_fprs); |
| - |
| - return err; |
| -} |
| - |
| static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, unsigned long framesize) |
| { |
| unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS; |
| @@ -414,34 +379,48 @@ setup_rt_frame(struct k_sigaction *ka, s |
| int signo, sigset_t *oldset, siginfo_t *info) |
| { |
| struct rt_signal_frame __user *sf; |
| - int sigframe_size, err; |
| + int wsaved, err, sf_size; |
| + void __user *tail; |
| |
| /* 1. Make sure everything is clean */ |
| synchronize_user_stack(); |
| save_and_clear_fpu(); |
| |
| - sigframe_size = sizeof(struct rt_signal_frame); |
| - if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) |
| - sigframe_size -= sizeof(__siginfo_fpu_t); |
| + wsaved = get_thread_wsaved(); |
| |
| + sf_size = sizeof(struct rt_signal_frame); |
| + if (current_thread_info()->fpsaved[0] & FPRS_FEF) |
| + sf_size += sizeof(__siginfo_fpu_t); |
| + if (wsaved) |
| + sf_size += sizeof(__siginfo_rwin_t); |
| sf = (struct rt_signal_frame __user *) |
| - get_sigframe(ka, regs, sigframe_size); |
| - |
| - if (invalid_frame_pointer (sf, sigframe_size)) |
| - goto sigill; |
| + get_sigframe(ka, regs, sf_size); |
| |
| - if (get_thread_wsaved() != 0) |
| + if (invalid_frame_pointer (sf)) |
| goto sigill; |
| |
| + tail = (sf + 1); |
| + |
| /* 2. Save the current process state */ |
| err = copy_to_user(&sf->regs, regs, sizeof (*regs)); |
| |
| if (current_thread_info()->fpsaved[0] & FPRS_FEF) { |
| - err |= save_fpu_state(regs, &sf->fpu_state); |
| - err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save); |
| + __siginfo_fpu_t __user *fpu_save = tail; |
| + tail += sizeof(__siginfo_fpu_t); |
| + err |= save_fpu_state(regs, fpu_save); |
| + err |= __put_user((u64)fpu_save, &sf->fpu_save); |
| } else { |
| err |= __put_user(0, &sf->fpu_save); |
| } |
| + if (wsaved) { |
| + __siginfo_rwin_t __user *rwin_save = tail; |
| + tail += sizeof(__siginfo_rwin_t); |
| + err |= save_rwin_state(wsaved, rwin_save); |
| + err |= __put_user((u64)rwin_save, &sf->rwin_save); |
| + set_thread_wsaved(0); |
| + } else { |
| + err |= __put_user(0, &sf->rwin_save); |
| + } |
| |
| /* Setup sigaltstack */ |
| err |= __put_user(current->sas_ss_sp, &sf->stack.ss_sp); |
| @@ -450,10 +429,17 @@ setup_rt_frame(struct k_sigaction *ka, s |
| |
| err |= copy_to_user(&sf->mask, oldset, sizeof(sigset_t)); |
| |
| - err |= copy_in_user((u64 __user *)sf, |
| - (u64 __user *)(regs->u_regs[UREG_FP]+STACK_BIAS), |
| - sizeof(struct reg_window)); |
| + if (!wsaved) { |
| + err |= copy_in_user((u64 __user *)sf, |
| + (u64 __user *)(regs->u_regs[UREG_FP] + |
| + STACK_BIAS), |
| + sizeof(struct reg_window)); |
| + } else { |
| + struct reg_window *rp; |
| |
| + rp = ¤t_thread_info()->reg_window[wsaved - 1]; |
| + err |= copy_to_user(sf, rp, sizeof(struct reg_window)); |
| + } |
| if (info) |
| err |= copy_siginfo_to_user(&sf->info, info); |
| else { |
| --- /dev/null |
| +++ b/arch/sparc/kernel/sigutil.h |
| @@ -0,0 +1,9 @@ |
| +#ifndef _SIGUTIL_H |
| +#define _SIGUTIL_H |
| + |
| +int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu); |
| +int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu); |
| +int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin); |
| +int restore_rwin_state(__siginfo_rwin_t __user *rp); |
| + |
| +#endif /* _SIGUTIL_H */ |
| --- /dev/null |
| +++ b/arch/sparc/kernel/sigutil_32.c |
| @@ -0,0 +1,120 @@ |
| +#include <linux/kernel.h> |
| +#include <linux/types.h> |
| +#include <linux/thread_info.h> |
| +#include <linux/uaccess.h> |
| +#include <linux/sched.h> |
| + |
| +#include <asm/sigcontext.h> |
| +#include <asm/fpumacro.h> |
| +#include <asm/ptrace.h> |
| + |
| +#include "sigutil.h" |
| + |
| +int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| +{ |
| + int err = 0; |
| +#ifdef CONFIG_SMP |
| + if (test_tsk_thread_flag(current, TIF_USEDFPU)) { |
| + put_psr(get_psr() | PSR_EF); |
| + fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr, |
| + ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); |
| + regs->psr &= ~(PSR_EF); |
| + clear_tsk_thread_flag(current, TIF_USEDFPU); |
| + } |
| +#else |
| + if (current == last_task_used_math) { |
| + put_psr(get_psr() | PSR_EF); |
| + fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr, |
| + ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); |
| + last_task_used_math = NULL; |
| + regs->psr &= ~(PSR_EF); |
| + } |
| +#endif |
| + err |= __copy_to_user(&fpu->si_float_regs[0], |
| + ¤t->thread.float_regs[0], |
| + (sizeof(unsigned long) * 32)); |
| + err |= __put_user(current->thread.fsr, &fpu->si_fsr); |
| + err |= __put_user(current->thread.fpqdepth, &fpu->si_fpqdepth); |
| + if (current->thread.fpqdepth != 0) |
| + err |= __copy_to_user(&fpu->si_fpqueue[0], |
| + ¤t->thread.fpqueue[0], |
| + ((sizeof(unsigned long) + |
| + (sizeof(unsigned long *)))*16)); |
| + clear_used_math(); |
| + return err; |
| +} |
| + |
| +int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| +{ |
| + int err; |
| +#ifdef CONFIG_SMP |
| + if (test_tsk_thread_flag(current, TIF_USEDFPU)) |
| + regs->psr &= ~PSR_EF; |
| +#else |
| + if (current == last_task_used_math) { |
| + last_task_used_math = NULL; |
| + regs->psr &= ~PSR_EF; |
| + } |
| +#endif |
| + set_used_math(); |
| + clear_tsk_thread_flag(current, TIF_USEDFPU); |
| + |
| + if (!access_ok(VERIFY_READ, fpu, sizeof(*fpu))) |
| + return -EFAULT; |
| + |
| + err = __copy_from_user(¤t->thread.float_regs[0], &fpu->si_float_regs[0], |
| + (sizeof(unsigned long) * 32)); |
| + err |= __get_user(current->thread.fsr, &fpu->si_fsr); |
| + err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth); |
| + if (current->thread.fpqdepth != 0) |
| + err |= __copy_from_user(¤t->thread.fpqueue[0], |
| + &fpu->si_fpqueue[0], |
| + ((sizeof(unsigned long) + |
| + (sizeof(unsigned long *)))*16)); |
| + return err; |
| +} |
| + |
| +int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin) |
| +{ |
| + int i, err = __put_user(wsaved, &rwin->wsaved); |
| + |
| + for (i = 0; i < wsaved; i++) { |
| + struct reg_window32 *rp; |
| + unsigned long fp; |
| + |
| + rp = ¤t_thread_info()->reg_window[i]; |
| + fp = current_thread_info()->rwbuf_stkptrs[i]; |
| + err |= copy_to_user(&rwin->reg_window[i], rp, |
| + sizeof(struct reg_window32)); |
| + err |= __put_user(fp, &rwin->rwbuf_stkptrs[i]); |
| + } |
| + return err; |
| +} |
| + |
| +int restore_rwin_state(__siginfo_rwin_t __user *rp) |
| +{ |
| + struct thread_info *t = current_thread_info(); |
| + int i, wsaved, err; |
| + |
| + __get_user(wsaved, &rp->wsaved); |
| + if (wsaved > NSWINS) |
| + return -EFAULT; |
| + |
| + err = 0; |
| + for (i = 0; i < wsaved; i++) { |
| + err |= copy_from_user(&t->reg_window[i], |
| + &rp->reg_window[i], |
| + sizeof(struct reg_window32)); |
| + err |= __get_user(t->rwbuf_stkptrs[i], |
| + &rp->rwbuf_stkptrs[i]); |
| + } |
| + if (err) |
| + return err; |
| + |
| + t->w_saved = wsaved; |
| + synchronize_user_stack(); |
| + if (t->w_saved) |
| + return -EFAULT; |
| + return 0; |
| + |
| +} |
| --- /dev/null |
| +++ b/arch/sparc/kernel/sigutil_64.c |
| @@ -0,0 +1,93 @@ |
| +#include <linux/kernel.h> |
| +#include <linux/types.h> |
| +#include <linux/thread_info.h> |
| +#include <linux/uaccess.h> |
| + |
| +#include <asm/sigcontext.h> |
| +#include <asm/fpumacro.h> |
| +#include <asm/ptrace.h> |
| + |
| +#include "sigutil.h" |
| + |
| +int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| +{ |
| + unsigned long *fpregs = current_thread_info()->fpregs; |
| + unsigned long fprs; |
| + int err = 0; |
| + |
| + fprs = current_thread_info()->fpsaved[0]; |
| + if (fprs & FPRS_DL) |
| + err |= copy_to_user(&fpu->si_float_regs[0], fpregs, |
| + (sizeof(unsigned int) * 32)); |
| + if (fprs & FPRS_DU) |
| + err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16, |
| + (sizeof(unsigned int) * 32)); |
| + err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr); |
| + err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr); |
| + err |= __put_user(fprs, &fpu->si_fprs); |
| + |
| + return err; |
| +} |
| + |
| +int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
| +{ |
| + unsigned long *fpregs = current_thread_info()->fpregs; |
| + unsigned long fprs; |
| + int err; |
| + |
| + err = __get_user(fprs, &fpu->si_fprs); |
| + fprs_write(0); |
| + regs->tstate &= ~TSTATE_PEF; |
| + if (fprs & FPRS_DL) |
| + err |= copy_from_user(fpregs, &fpu->si_float_regs[0], |
| + (sizeof(unsigned int) * 32)); |
| + if (fprs & FPRS_DU) |
| + err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], |
| + (sizeof(unsigned int) * 32)); |
| + err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr); |
| + err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr); |
| + current_thread_info()->fpsaved[0] |= fprs; |
| + return err; |
| +} |
| + |
| +int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin) |
| +{ |
| + int i, err = __put_user(wsaved, &rwin->wsaved); |
| + |
| + for (i = 0; i < wsaved; i++) { |
| + struct reg_window *rp = ¤t_thread_info()->reg_window[i]; |
| + unsigned long fp = current_thread_info()->rwbuf_stkptrs[i]; |
| + |
| + err |= copy_to_user(&rwin->reg_window[i], rp, |
| + sizeof(struct reg_window)); |
| + err |= __put_user(fp, &rwin->rwbuf_stkptrs[i]); |
| + } |
| + return err; |
| +} |
| + |
| +int restore_rwin_state(__siginfo_rwin_t __user *rp) |
| +{ |
| + struct thread_info *t = current_thread_info(); |
| + int i, wsaved, err; |
| + |
| + __get_user(wsaved, &rp->wsaved); |
| + if (wsaved > NSWINS) |
| + return -EFAULT; |
| + |
| + err = 0; |
| + for (i = 0; i < wsaved; i++) { |
| + err |= copy_from_user(&t->reg_window[i], |
| + &rp->reg_window[i], |
| + sizeof(struct reg_window)); |
| + err |= __get_user(t->rwbuf_stkptrs[i], |
| + &rp->rwbuf_stkptrs[i]); |
| + } |
| + if (err) |
| + return err; |
| + |
| + set_thread_wsaved(wsaved); |
| + synchronize_user_stack(); |
| + if (get_thread_wsaved()) |
| + return -EFAULT; |
| + return 0; |
| +} |