summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-08-30 14:49:51 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-30 14:49:51 -0700
commitaa860198da73375bbdfa0be997b5d77352e098b6 (patch)
treef50c87b3682b96886a21da0bac8f61edf93987ca
parent42c4d8d501c0c167dab8d441366d59f2ff6104da (diff)
downloadstable-queue-aa860198da73375bbdfa0be997b5d77352e098b6.tar.gz
3.0 patches
-rw-r--r--queue-3.0/series6
-rw-r--r--queue-3.0/sparc-allow-handling-signals-when-stack-is-corrupted.patch1157
-rw-r--r--queue-3.0/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch48
-rw-r--r--queue-3.0/sparc32-sun4d-change-ipi-irq-level-to-prevent-collision.patch41
-rw-r--r--queue-3.0/sparc32-unbreak-arch_write_unlock.patch60
-rw-r--r--queue-3.0/sparc64-remove-unnecessary-macros-from-spinlock_64.h.patch45
-rw-r--r--queue-3.0/sparc64-set-have_c_recordmcount.patch25
7 files changed, 1382 insertions, 0 deletions
diff --git a/queue-3.0/series b/queue-3.0/series
index f45ac717b9..4ce67dc173 100644
--- a/queue-3.0/series
+++ b/queue-3.0/series
@@ -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
index 0000000000..82908c39ab
--- /dev/null
+++ b/queue-3.0/sparc-allow-handling-signals-when-stack-is-corrupted.patch
@@ -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
index 0000000000..bcabab844a
--- /dev/null
+++ b/queue-3.0/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch
@@ -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
index 0000000000..e61ccddfc6
--- /dev/null
+++ b/queue-3.0/sparc32-sun4d-change-ipi-irq-level-to-prevent-collision.patch
@@ -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
index 0000000000..7eec1f6579
--- /dev/null
+++ b/queue-3.0/sparc32-unbreak-arch_write_unlock.patch
@@ -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
index 0000000000..ed3d3bf6bf
--- /dev/null
+++ b/queue-3.0/sparc64-remove-unnecessary-macros-from-spinlock_64.h.patch
@@ -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
index 0000000000..c4946e1c76
--- /dev/null
+++ b/queue-3.0/sparc64-set-have_c_recordmcount.patch
@@ -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