Please add the following Sparc bug fixes to 3.0.x-stable, thanks!
>From 9dd4bfdc9361a326d7fc42141743e878e3e422ac Mon Sep 17 00:00:00 2001
From: Mikael Pettersson <mi...@it.uu.se>
Date: Mon, 15 Aug 2011 10:10:31 +0000
Subject: [PATCH 1/7] sparc64: remove unnecessary macros from spinlock_64.h

[ Upstream commit a0fba3eb059e73fed2d376a901f8117734c12f1f ]

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 <mi...@it.uu.se>
Signed-off-by: David S. Miller <da...@davemloft.net>
---
 arch/sparc/include/asm/spinlock_64.h |    6 ------
 1 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/arch/sparc/include/asm/spinlock_64.h 
b/arch/sparc/include/asm/spinlock_64.h
index 073936a..9689176 100644
--- 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(arch_rwlock_t *lock)
        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)
-- 
1.7.6


>From 653fddbf28baa5d8fc7a32bd5bf212312043e606 Mon Sep 17 00:00:00 2001
From: Mikael Pettersson <mi...@it.uu.se>
Date: Mon, 15 Aug 2011 10:11:50 +0000
Subject: [PATCH 2/7] sparc32: unbreak arch_write_unlock()

[ Upstream commit 3f6aa0b113846a8628baa649af422cfc6fb1d786 ]

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 <mi...@it.uu.se>
Signed-off-by: David S. Miller <da...@davemloft.net>
---
 arch/sparc/include/asm/spinlock_32.h |   11 +++++++++--
 1 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/sparc/include/asm/spinlock_32.h 
b/arch/sparc/include/asm/spinlock_32.h
index 5f5b8bf..bcc98fc 100644
--- 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_rwlock_t *rw)
        *(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(arch_rwlock_t *rw)
        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)
-- 
1.7.6


>From 24b779a7dc79da7b80722db5755bf55961b4f43e Mon Sep 17 00:00:00 2001
From: "David S. Miller" <da...@davemloft.net>
Date: Sat, 20 Aug 2011 17:14:54 -0700
Subject: [PATCH 3/7] sparc: Allow handling signals when stack is corrupted.

[ Upstream commit 5598473a5b40c47a8c5349dd2c2630797169cf1a ]

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 <da...@davemloft.net>
---
 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(-)
 create mode 100644 arch/sparc/kernel/sigutil.h
 create mode 100644 arch/sparc/kernel/sigutil_32.c
 create mode 100644 arch/sparc/kernel/sigutil_64.c

diff --git a/arch/sparc/include/asm/sigcontext.h 
b/arch/sparc/include/asm/sigcontext.h
index a1607d1..69914d7 100644
--- 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
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index b90b4a1..cb85458 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_SPARC32)   += sun4m_irq.o sun4c_irq.o sun4d_irq.o
 
 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
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index 75fad42..1ba95af 100644
--- 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 *to, 
compat_siginfo_t __user *from)
        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 *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 |= 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 pt_regs *regs)
        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 sigaction *sa, 
struct pt_regs *regs, uns
        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_sigaction *ka, struct 
pt_regs *regs,
                         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_sigaction *ka, struct 
pt_regs *regs,
        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_sigaction *ka, struct 
pt_regs *regs,
                          &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_sigaction *ka, struct 
pt_regs *regs,
        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_sigaction *ka, struct 
pt_regs *regs,
                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_sigaction *ka, 
struct pt_regs *regs,
                            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_sigaction *ka, struct 
pt_regs *regs,
        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_sigaction *ka, 
struct pt_regs *regs,
                          &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_sigaction *ka, struct 
pt_regs *regs,
        }
        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;
        
diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c
index 5e5c5fd..04ede8f 100644
--- 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_t set)
        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_regs *regs)
        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 pt_regs *regs)
        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 pt_regs *regs)
        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 pt_regs *regs)
        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(struct sigaction 
*sa, struct pt_regs *re
        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_sigaction *ka, struct 
pt_regs *regs,
        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_sigaction *ka, struct 
pt_regs *regs,
        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_sigaction *ka, struct 
pt_regs *regs,
                          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_sigaction *ka, struct 
pt_regs *regs,
        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_sigaction *ka, struct 
pt_regs *regs,
        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);
 
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index 006fe45..47509df 100644
--- 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_sigset_t set)
        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 *regs)
        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 *regs)
        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, struct pt_regs 
*regs,
               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, struct pt_regs 
*regs,
 
        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 {
diff --git a/arch/sparc/kernel/sigutil.h b/arch/sparc/kernel/sigutil.h
new file mode 100644
index 0000000..d223aa4
--- /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 */
diff --git a/arch/sparc/kernel/sigutil_32.c b/arch/sparc/kernel/sigutil_32.c
new file mode 100644
index 0000000..35c7897
--- /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;
+
+}
diff --git a/arch/sparc/kernel/sigutil_64.c b/arch/sparc/kernel/sigutil_64.c
new file mode 100644
index 0000000..e7dc508
--- /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;
+}
-- 
1.7.6


>From e5fa8bd53dbe1c3ebf4da2297b1fde3ed3a5e428 Mon Sep 17 00:00:00 2001
From: "David S. Miller" <da...@davemloft.net>
Date: Mon, 15 Aug 2011 14:45:17 -0700
Subject: [PATCH 4/7] sparc64: Set HAVE_C_RECORDMCOUNT

[ Upstream commit 178a29600340bef5b13cd4157053679debe35351 ]

Signed-off-by: David S. Miller <da...@davemloft.net>
---
 arch/sparc/Kconfig |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 253986b..2e79419 100644
--- 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
-- 
1.7.6


>From e38c9b46f771fbc36bb546f53380720c45ea8b5b Mon Sep 17 00:00:00 2001
From: Ian Campbell <ian.campb...@citrix.com>
Date: Wed, 17 Aug 2011 22:14:57 +0000
Subject: [PATCH 5/7] sparc: fix array bounds error setting up PCIC NMI trap

[ Upstream commit 4a0342ca8e8150bd47e7118a76e300692a1b6b7b ]

  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.campb...@citrix.com>
Cc: "David S. Miller" <da...@davemloft.net>
Cc: sparcli...@vger.kernel.org
Reviewed-by: Sam Ravnborg <s...@ravnborg.org>
Signed-off-by: David S. Miller <da...@davemloft.net>
---
 arch/sparc/kernel/pcic.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
index 948601a..6418ba6 100644
--- 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];
-- 
1.7.6


>From 52fdd7593dea7dee749e6d29d1af5c4b799de80f Mon Sep 17 00:00:00 2001
From: Kjetil Oftedal <ofte...@gmail.com>
Date: Mon, 29 Aug 2011 00:16:28 +0200
Subject: [PATCH 6/7] sparc32,sun4d: Change IPI IRQ level to prevent collision
 between IPI and timer interrupt

[ Upstream commit 38f7f8f05e8239e9871f7e1c4b0a842080e85315 ]

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 <ofte...@gmail.com>
Signed-off-by: David S. Miller <da...@davemloft.net>
---
 arch/sparc/kernel/irq.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h
index 100b9c2..4285112 100644
--- 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);
 
-- 
1.7.6


>From 29180da13540a565aff93a11eaf79dccae334797 Mon Sep 17 00:00:00 2001
From: "David S. Miller" <da...@davemloft.net>
Date: Mon, 29 Aug 2011 21:14:29 -0700
Subject: [PATCH 7/7] sparc64: Only Panther cheetah+ chips have POPC.

[ Upstream commit 1a8e0da5937a6c87807083baa318cf8f98dac9aa ]

Signed-off-by: David S. Miller <da...@davemloft.net>
---
 arch/sparc/kernel/setup_64.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
index 3e9daea..3c5bb78 100644
--- a/arch/sparc/kernel/setup_64.c
+++ b/arch/sparc/kernel/setup_64.c
@@ -440,8 +440,14 @@ static void __init init_sparc64_elf_hwcap(void)
                        cap |= AV_SPARC_VIS;
                if (tlb_type == cheetah || tlb_type == cheetah_plus)
                        cap |= AV_SPARC_VIS | AV_SPARC_VIS2;
-               if (tlb_type == cheetah_plus)
-                       cap |= AV_SPARC_POPC;
+               if (tlb_type == cheetah_plus) {
+                       unsigned long impl, ver;
+
+                       __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver));
+                       impl = ((ver >> 32) & 0xffff);
+                       if (impl == PANTHER_IMPL)
+                               cap |= AV_SPARC_POPC;
+               }
                if (tlb_type == hypervisor) {
                        if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1)
                                cap |= AV_SPARC_ASI_BLK_INIT;
-- 
1.7.6

_______________________________________________
stable mailing list
stable@linux.kernel.org
http://linux.kernel.org/mailman/listinfo/stable

Reply via email to