]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.0 patches
authorGreg Kroah-Hartman <gregkh@suse.de>
Tue, 30 Aug 2011 21:49:51 +0000 (14:49 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 30 Aug 2011 21:49:51 +0000 (14:49 -0700)
queue-3.0/series
queue-3.0/sparc-allow-handling-signals-when-stack-is-corrupted.patch [new file with mode: 0644]
queue-3.0/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch [new file with mode: 0644]
queue-3.0/sparc32-sun4d-change-ipi-irq-level-to-prevent-collision.patch [new file with mode: 0644]
queue-3.0/sparc32-unbreak-arch_write_unlock.patch [new file with mode: 0644]
queue-3.0/sparc64-remove-unnecessary-macros-from-spinlock_64.h.patch [new file with mode: 0644]
queue-3.0/sparc64-set-have_c_recordmcount.patch [new file with mode: 0644]

index f45ac717b9f127a10e70ba9762e8003aced9e709..4ce67dc17380c12a922bf7600f5038ba3706b645 100644 (file)
@@ -37,3 +37,9 @@ pata_via-disable-atapi-dma-on-averatec-3200.patch
 atm-br2684-fix-oops-due-to-skb-dev-being-null.patch
 rt2x00-fix-crash-in-rt2800usb_write_tx_desc.patch
 rt2x00-fix-crash-in-rt2800usb_get_txwi.patch
+sparc64-remove-unnecessary-macros-from-spinlock_64.h.patch
+sparc32-unbreak-arch_write_unlock.patch
+sparc-allow-handling-signals-when-stack-is-corrupted.patch
+sparc64-set-have_c_recordmcount.patch
+sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch
+sparc32-sun4d-change-ipi-irq-level-to-prevent-collision.patch
diff --git a/queue-3.0/sparc-allow-handling-signals-when-stack-is-corrupted.patch b/queue-3.0/sparc-allow-handling-signals-when-stack-is-corrupted.patch
new file mode 100644 (file)
index 0000000..82908c3
--- /dev/null
@@ -0,0 +1,1157 @@
+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
+@@ -32,6 +32,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 = &current_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 = &current_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(&current->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(&current->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(&current->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(&current->thread.float_regs[0], &current->thread.fsr,
+-                     &current->thread.fpqueue[0], &current->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(&current->thread.float_regs[0], &current->thread.fsr,
+-                     &current->thread.fpqueue[0], &current->thread.fpqdepth);
+-              last_task_used_math = NULL;
+-              regs->psr &= ~(PSR_EF);
+-      }
+-#endif
+-      err |= __copy_to_user(&fpu->si_float_regs[0],
+-                            &current->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],
+-                                    &current->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 = &current_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 = &current_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 = &current_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(&current->thread.float_regs[0], &current->thread.fsr,
++                     &current->thread.fpqueue[0], &current->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(&current->thread.float_regs[0], &current->thread.fsr,
++                     &current->thread.fpqueue[0], &current->thread.fpqdepth);
++              last_task_used_math = NULL;
++              regs->psr &= ~(PSR_EF);
++      }
++#endif
++      err |= __copy_to_user(&fpu->si_float_regs[0],
++                            &current->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],
++                                    &current->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(&current->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(&current->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 = &current_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 = &current_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;
++}
diff --git a/queue-3.0/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch b/queue-3.0/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch
new file mode 100644 (file)
index 0000000..bcabab8
--- /dev/null
@@ -0,0 +1,48 @@
+From 4a0342ca8e8150bd47e7118a76e300692a1b6b7b Mon Sep 17 00:00:00 2001
+From: Ian Campbell <Ian.Campbell@citrix.com>
+Date: Wed, 17 Aug 2011 22:14:57 +0000
+Subject: sparc: fix array bounds error setting up PCIC NMI trap
+
+From: Ian Campbell <Ian.Campbell@citrix.com>
+
+commit 4a0342ca8e8150bd47e7118a76e300692a1b6b7b upstream.
+
+  CC      arch/sparc/kernel/pcic.o
+arch/sparc/kernel/pcic.c: In function 'pcic_probe':
+arch/sparc/kernel/pcic.c:359:33: error: array subscript is above array bounds [-Werror=array-bounds]
+arch/sparc/kernel/pcic.c:359:8: error: array subscript is above array bounds [-Werror=array-bounds]
+arch/sparc/kernel/pcic.c:360:33: error: array subscript is above array bounds [-Werror=array-bounds]
+arch/sparc/kernel/pcic.c:360:8: error: array subscript is above array bounds [-Werror=array-bounds]
+arch/sparc/kernel/pcic.c:361:33: error: array subscript is above array bounds [-Werror=array-bounds]
+arch/sparc/kernel/pcic.c:361:8: error: array subscript is above array bounds [-Werror=array-bounds]
+cc1: all warnings being treated as errors
+
+I'm not particularly familiar with sparc but t_nmi (defined in head_32.S via
+the TRAP_ENTRY macro) and pcic_nmi_trap_patch (defined in entry.S) both appear
+to be 4 instructions long and I presume from the usage that instructions are
+int sized.
+
+Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
+Cc: "David S. Miller" <davem@davemloft.net>
+Cc: sparclinux@vger.kernel.org
+Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/sparc/kernel/pcic.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/sparc/kernel/pcic.c
++++ b/arch/sparc/kernel/pcic.c
+@@ -352,8 +352,8 @@ int __init pcic_probe(void)
+       strcpy(pbm->prom_name, namebuf);
+       {
+-              extern volatile int t_nmi[1];
+-              extern int pcic_nmi_trap_patch[1];
++              extern volatile int t_nmi[4];
++              extern int pcic_nmi_trap_patch[4];
+               t_nmi[0] = pcic_nmi_trap_patch[0];
+               t_nmi[1] = pcic_nmi_trap_patch[1];
diff --git a/queue-3.0/sparc32-sun4d-change-ipi-irq-level-to-prevent-collision.patch b/queue-3.0/sparc32-sun4d-change-ipi-irq-level-to-prevent-collision.patch
new file mode 100644 (file)
index 0000000..e61ccdd
--- /dev/null
@@ -0,0 +1,41 @@
+From 38f7f8f05e8239e9871f7e1c4b0a842080e85315 Mon Sep 17 00:00:00 2001
+From: Kjetil Oftedal <oftedal@gmail.com>
+Date: Mon, 29 Aug 2011 00:16:28 +0200
+Subject: sparc32,sun4d: Change IPI IRQ level to prevent collision
+ between IPI and timer interrupt
+
+From: Kjetil Oftedal <oftedal@gmail.com>
+
+commit 38f7f8f05e8239e9871f7e1c4b0a842080e85315 upstream.
+
+On Sun4d systems running in SMP mode, IRQ 14 is used for timer interrupts
+and has a specialized interrupt handler. IPI is currently set to use IRQ 14
+as well, which causes it to trigger the timer interrupt handler, and not the
+IPI interrupt handler.
+
+The IPI interrupt is therefore changed to IRQ 13, which is the highest
+normally handled interrupt. This IRQ is also used for SBUS interrupts,
+however there is nothing in the IPI/SBUS interrupt handlers that indicate
+that they will not handle sharing the interrupt.
+(IRQ 13 is indicated as audio interrupt, which is unlikely to be found in a
+sun4d system)
+
+Signed-off-by: Kjetil Oftedal <oftedal@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/sparc/kernel/irq.h |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/sparc/kernel/irq.h
++++ b/arch/sparc/kernel/irq.h
+@@ -88,7 +88,7 @@ BTFIXUPDEF_CALL(void, set_irq_udt, int)
+ #define set_irq_udt(cpu) BTFIXUP_CALL(set_irq_udt)(cpu)
+ /* All SUN4D IPIs are sent on this IRQ, may be shared with hard IRQs */
+-#define SUN4D_IPI_IRQ 14
++#define SUN4D_IPI_IRQ 13
+ extern void sun4d_ipi_interrupt(void);
diff --git a/queue-3.0/sparc32-unbreak-arch_write_unlock.patch b/queue-3.0/sparc32-unbreak-arch_write_unlock.patch
new file mode 100644 (file)
index 0000000..7eec1f6
--- /dev/null
@@ -0,0 +1,60 @@
+From 3f6aa0b113846a8628baa649af422cfc6fb1d786 Mon Sep 17 00:00:00 2001
+From: Mikael Pettersson <mikpe@it.uu.se>
+Date: Mon, 15 Aug 2011 10:11:50 +0000
+Subject: sparc32: unbreak arch_write_unlock()
+
+From: Mikael Pettersson <mikpe@it.uu.se>
+
+commit 3f6aa0b113846a8628baa649af422cfc6fb1d786 upstream.
+
+The sparc32 version of arch_write_unlock() is just a plain assignment.
+Unfortunately this allows the compiler to schedule side-effects in a
+protected region to occur after the HW-level unlock, which is broken.
+E.g., the following trivial test case gets miscompiled:
+
+       #include <linux/spinlock.h>
+       rwlock_t lock;
+       int counter;
+       void foo(void) { write_lock(&lock); ++counter; write_unlock(&lock); }
+
+Fixed by adding a compiler memory barrier to arch_write_unlock().  The
+sparc64 version combines the barrier and assignment into a single asm(),
+and implements the operation as a static inline, so that's what I did too.
+
+Compile-tested with sparc32_defconfig + CONFIG_SMP=y.
+
+Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/sparc/include/asm/spinlock_32.h |   11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/arch/sparc/include/asm/spinlock_32.h
++++ b/arch/sparc/include/asm/spinlock_32.h
+@@ -131,6 +131,15 @@ static inline void arch_write_lock(arch_
+       *(volatile __u32 *)&lp->lock = ~0U;
+ }
++static void inline arch_write_unlock(arch_rwlock_t *lock)
++{
++      __asm__ __volatile__(
++"     st              %%g0, [%0]"
++      : /* no outputs */
++      : "r" (lock)
++      : "memory");
++}
++
+ static inline int arch_write_trylock(arch_rwlock_t *rw)
+ {
+       unsigned int val;
+@@ -175,8 +184,6 @@ static inline int __arch_read_trylock(ar
+       res; \
+ })
+-#define arch_write_unlock(rw) do { (rw)->lock = 0; } while(0)
+-
+ #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
+ #define arch_read_lock_flags(rw, flags)   arch_read_lock(rw)
+ #define arch_write_lock_flags(rw, flags)  arch_write_lock(rw)
diff --git a/queue-3.0/sparc64-remove-unnecessary-macros-from-spinlock_64.h.patch b/queue-3.0/sparc64-remove-unnecessary-macros-from-spinlock_64.h.patch
new file mode 100644 (file)
index 0000000..ed3d3bf
--- /dev/null
@@ -0,0 +1,45 @@
+From a0fba3eb059e73fed2d376a901f8117734c12f1f Mon Sep 17 00:00:00 2001
+From: Mikael Pettersson <mikpe@it.uu.se>
+Date: Mon, 15 Aug 2011 10:10:31 +0000
+Subject: sparc64: remove unnecessary macros from spinlock_64.h
+
+From: Mikael Pettersson <mikpe@it.uu.se>
+
+commit a0fba3eb059e73fed2d376a901f8117734c12f1f upstream.
+
+The sparc64 spinlock_64.h contains a number of operations defined
+first as static inline functions, and then as macros with the same
+names and parameters as the functions.  Maybe this was needed at
+some point in the past, but now nothing seems to depend on these
+macros (checked with a recursive grep looking for ifdefs on these
+names).  Other archs don't define these identity-macros.
+
+So this patch deletes these unnecessary macros.
+
+Compile-tested with sparc64_defconfig.
+
+Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/sparc/include/asm/spinlock_64.h |    6 ------
+ 1 file changed, 6 deletions(-)
+
+--- a/arch/sparc/include/asm/spinlock_64.h
++++ b/arch/sparc/include/asm/spinlock_64.h
+@@ -210,14 +210,8 @@ static int inline arch_write_trylock(arc
+       return result;
+ }
+-#define arch_read_lock(p)     arch_read_lock(p)
+ #define arch_read_lock_flags(p, f) arch_read_lock(p)
+-#define arch_read_trylock(p)  arch_read_trylock(p)
+-#define arch_read_unlock(p)   arch_read_unlock(p)
+-#define arch_write_lock(p)    arch_write_lock(p)
+ #define arch_write_lock_flags(p, f) arch_write_lock(p)
+-#define arch_write_unlock(p)  arch_write_unlock(p)
+-#define arch_write_trylock(p) arch_write_trylock(p)
+ #define arch_read_can_lock(rw)                (!((rw)->lock & 0x80000000UL))
+ #define arch_write_can_lock(rw)       (!(rw)->lock)
diff --git a/queue-3.0/sparc64-set-have_c_recordmcount.patch b/queue-3.0/sparc64-set-have_c_recordmcount.patch
new file mode 100644 (file)
index 0000000..c4946e1
--- /dev/null
@@ -0,0 +1,25 @@
+From e5fa8bd53dbe1c3ebf4da2297b1fde3ed3a5e428 Mon Sep 17 00:00:00 2001
+From: "David S. Miller" <davem@davemloft.net>
+Date: Mon, 15 Aug 2011 14:45:17 -0700
+Subject: sparc64: Set HAVE_C_RECORDMCOUNT
+
+From: "David S. Miller" <davem@davemloft.net>
+
+[ Upstream commit 178a29600340bef5b13cd4157053679debe35351 ]
+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ arch/sparc/Kconfig |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/sparc/Kconfig
++++ b/arch/sparc/Kconfig
+@@ -53,6 +53,7 @@ config SPARC64
+       select HAVE_PERF_EVENTS
+       select PERF_USE_VMALLOC
+       select IRQ_PREFLOW_FASTEOI
++      select HAVE_C_RECORDMCOUNT
+ config ARCH_DEFCONFIG
+       string