summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-08-30 15:03:07 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-30 15:03:07 -0700
commit2edff1c16ec4396f87e40e8161fb91e6fcc92150 (patch)
tree0074e678c2bf993231913396cb69dc66cc987d3c
parent7b25235cec5b8b019850d9a57192c7b5e0c0467f (diff)
downloadlongterm-queue-2.6.32-2edff1c16ec4396f87e40e8161fb91e6fcc92150.tar.gz
.32 patches
-rw-r--r--queue-2.6.32/fix-broken-backport-for-ipv6-tunnels.patch49
-rw-r--r--queue-2.6.32/ipv6-add-gso-support-on-forwarding-path.patch42
-rw-r--r--queue-2.6.32/net-fix-ipv6-gso-type-checks-in-intel-ethernet-drivers.patch73
-rw-r--r--queue-2.6.32/series5
-rw-r--r--queue-2.6.32/sparc-allow-handling-signals-when-stack-is-corrupted.patch1157
-rw-r--r--queue-2.6.32/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch48
6 files changed, 1374 insertions, 0 deletions
diff --git a/queue-2.6.32/fix-broken-backport-for-ipv6-tunnels.patch b/queue-2.6.32/fix-broken-backport-for-ipv6-tunnels.patch
new file mode 100644
index 0000000..9ce3adb
--- /dev/null
+++ b/queue-2.6.32/fix-broken-backport-for-ipv6-tunnels.patch
@@ -0,0 +1,49 @@
+From psomas@gentoo.org Tue Aug 30 14:52:07 2011
+From: Stratos Psomadakis <psomas@gentoo.org>
+Date: Tue, 30 Aug 2011 17:48:08 +0300
+Subject: Fix broken backport for IPv6 tunnels
+To: stable@kernel.org
+Cc: Stratos Psomadakis <psomas@gentoo.org>
+Message-ID: <1314715688-26919-1-git-send-email-psomas@gentoo.org>
+
+From: Stratos Psomadakis <psomas@gentoo.org>
+
+Fix broken backport for IPv6 tunnels in 2.6.32-longterm kernels.
+
+upstream commit d5aa407f59f5b83d2c50ec88f5bf56d40f1f8978 ("tunnels: fix
+netns vs proto registration ordering") , which was included in
+2.6.32.44-longterm, was not backported correctly, and results in a NULL
+pointer dereference in ip6_tunnel.c for longterm kernels >=2.6.32.44
+
+Use [un]register_pernet_gen_device() instead of
+[un]register_pernet_device() to fix it.
+
+Signed-off-by: Stratos Psomadakis <psomas@gentoo.org>
+Cc: Wolfgang Walter <wolfgang.walter@stwm.de>
+Cc: Tim Gardner <tim.gardner@canonical.com>
+Cc: Andy Whitcroft <apw@canonical.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ net/ipv6/ip6_tunnel.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -1466,7 +1466,7 @@ static int __init ip6_tunnel_init(void)
+ {
+ int err;
+
+- err = register_pernet_device(&ip6_tnl_net_ops);
++ err = register_pernet_gen_device(&ip6_tnl_net_id, &ip6_tnl_net_ops);
+ if (err < 0)
+ goto out_pernet;
+
+@@ -1487,7 +1487,7 @@ static int __init ip6_tunnel_init(void)
+ out_ip6ip6:
+ xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET);
+ out_ip4ip6:
+- unregister_pernet_device(&ip6_tnl_net_ops);
++ unregister_pernet_gen_device(ip6_tnl_net_id, &ip6_tnl_net_ops);
+ out_pernet:
+ return err;
+ }
diff --git a/queue-2.6.32/ipv6-add-gso-support-on-forwarding-path.patch b/queue-2.6.32/ipv6-add-gso-support-on-forwarding-path.patch
new file mode 100644
index 0000000..18f9e22
--- /dev/null
+++ b/queue-2.6.32/ipv6-add-gso-support-on-forwarding-path.patch
@@ -0,0 +1,42 @@
+From 0aa68271510ae2b221d4b60892103837be63afe4 Mon Sep 17 00:00:00 2001
+From: Herbert Xu <herbert@gondor.apana.org.au>
+Date: Thu, 27 May 2010 16:14:30 -0700
+Subject: ipv6: Add GSO support on forwarding path
+
+From: Herbert Xu <herbert@gondor.apana.org.au>
+
+commit 0aa68271510ae2b221d4b60892103837be63afe4 upstream.
+
+Currently we disallow GSO packets on the IPv6 forward path.
+This patch fixes this.
+
+Note that I discovered that our existing GSO MTU checks (e.g.,
+IPv4 forwarding) are buggy in that they skip the check altogether,
+when they really should be checking gso_size + header instead.
+
+I have also been lazy here in that I haven't bothered to segment
+the GSO packet by hand before generating an ICMP message. Someone
+should add that to be 100% correct.
+
+Reported-by: Ralf Baechle <ralf@linux-mips.org>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Apollon Oikonomopoulos <apoikos@gmail.com>
+Signed-off-by: Faidon Liambotis <paravoid@debian.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/ipv6/ip6_output.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -510,7 +510,7 @@ int ip6_forward(struct sk_buff *skb)
+ }
+ }
+
+- if (skb->len > dst_mtu(dst)) {
++ if (skb->len > dst_mtu(dst) && !skb_is_gso(skb)) {
+ /* Again, force OUTPUT device used as source address */
+ skb->dev = dst->dev;
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst_mtu(dst), skb->dev);
diff --git a/queue-2.6.32/net-fix-ipv6-gso-type-checks-in-intel-ethernet-drivers.patch b/queue-2.6.32/net-fix-ipv6-gso-type-checks-in-intel-ethernet-drivers.patch
new file mode 100644
index 0000000..daf5bf3
--- /dev/null
+++ b/queue-2.6.32/net-fix-ipv6-gso-type-checks-in-intel-ethernet-drivers.patch
@@ -0,0 +1,73 @@
+From 8e1e8a4779cb23c1d9f51e9223795e07ec54d77a Mon Sep 17 00:00:00 2001
+From: Sridhar Samudrala <sri@us.ibm.com>
+Date: Sat, 23 Jan 2010 02:02:21 -0800
+Subject: net: Fix IPv6 GSO type checks in Intel ethernet drivers
+
+From: Sridhar Samudrala <sri@us.ibm.com>
+
+commit 8e1e8a4779cb23c1d9f51e9223795e07ec54d77a upstream.
+
+Found this problem when testing IPv6 from a KVM guest to a remote
+host via e1000e device on the host.
+The following patch fixes the check for IPv6 GSO packet in Intel
+ethernet drivers to use skb_is_gso_v6(). SKB_GSO_DODGY is also set
+when packets are forwarded from a guest.
+
+Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
+Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Faidon Liambotis <paravoid@debian.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/net/e1000e/netdev.c | 2 +-
+ drivers/net/igb/igb_main.c | 2 +-
+ drivers/net/igbvf/netdev.c | 2 +-
+ drivers/net/ixgbe/ixgbe_main.c | 2 +-
+ 4 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/e1000e/netdev.c
++++ b/drivers/net/e1000e/netdev.c
+@@ -3807,7 +3807,7 @@ static int e1000_tso(struct e1000_adapte
+ 0);
+ cmd_length = E1000_TXD_CMD_IP;
+ ipcse = skb_transport_offset(skb) - 1;
+- } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
++ } else if (skb_is_gso_v6(skb)) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+--- a/drivers/net/igb/igb_main.c
++++ b/drivers/net/igb/igb_main.c
+@@ -3032,7 +3032,7 @@ static inline int igb_tso_adv(struct igb
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+- } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
++ } else if (skb_is_gso_v6(skb)) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+--- a/drivers/net/igbvf/netdev.c
++++ b/drivers/net/igbvf/netdev.c
+@@ -1953,7 +1953,7 @@ static int igbvf_tso(struct igbvf_adapte
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+- } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
++ } else if (skb_is_gso_v6(skb)) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+--- a/drivers/net/ixgbe/ixgbe_main.c
++++ b/drivers/net/ixgbe/ixgbe_main.c
+@@ -4881,7 +4881,7 @@ static int ixgbe_tso(struct ixgbe_adapte
+ IPPROTO_TCP,
+ 0);
+ adapter->hw_tso_ctxt++;
+- } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
++ } else if (skb_is_gso_v6(skb)) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
diff --git a/queue-2.6.32/series b/queue-2.6.32/series
index e8ffeb2..045e85e 100644
--- a/queue-2.6.32/series
+++ b/queue-2.6.32/series
@@ -2,3 +2,8 @@ usb-ftdi_sio-add-calao-reference-board-support.patch
usb-ehci-do-not-rely-on-port_suspend-to-stop-usb-resuming-in-ehci_bus_resume.patch
rt2x00-do-not-drop-usb-dev-reference-counter-on-suspend.patch
atm-br2684-fix-oops-due-to-skb-dev-being-null.patch
+sparc-allow-handling-signals-when-stack-is-corrupted.patch
+sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch
+fix-broken-backport-for-ipv6-tunnels.patch
+net-fix-ipv6-gso-type-checks-in-intel-ethernet-drivers.patch
+ipv6-add-gso-support-on-forwarding-path.patch
diff --git a/queue-2.6.32/sparc-allow-handling-signals-when-stack-is-corrupted.patch b/queue-2.6.32/sparc-allow-handling-signals-when-stack-is-corrupted.patch
new file mode 100644
index 0000000..5bdc380
--- /dev/null
+++ b/queue-2.6.32/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
+@@ -24,6 +24,7 @@ obj-$(CONFIG_SPARC32) += sun4m_irq.o s
+
+ obj-y += process_$(BITS).o
+ obj-y += signal_$(BITS).o
++obj-y += sigutil_$(BITS).o
+ obj-$(CONFIG_SPARC32) += ioport.o
+ obj-y += setup_$(BITS).o
+ obj-y += idprom.o
+--- a/arch/sparc/kernel/signal32.c
++++ b/arch/sparc/kernel/signal32.c
+@@ -29,6 +29,8 @@
+ #include <asm/visasm.h>
+ #include <asm/compat_signal.h>
+
++#include "sigutil.h"
++
+ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+ /* This magic should be in g_upper[0] for all upper parts
+@@ -44,14 +46,14 @@ typedef struct {
+ struct signal_frame32 {
+ struct sparc_stackf32 ss;
+ __siginfo32_t info;
+- /* __siginfo_fpu32_t * */ u32 fpu_save;
++ /* __siginfo_fpu_t * */ u32 fpu_save;
+ unsigned int insns[2];
+ unsigned int extramask[_COMPAT_NSIG_WORDS - 1];
+ unsigned int extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */
+ /* Only valid if (info.si_regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */
+ siginfo_extra_v8plus_t v8plus;
+- __siginfo_fpu_t fpu_state;
+-};
++ /* __siginfo_rwin_t * */u32 rwin_save;
++} __attribute__((aligned(8)));
+
+ typedef struct compat_siginfo{
+ int si_signo;
+@@ -110,18 +112,14 @@ struct rt_signal_frame32 {
+ compat_siginfo_t info;
+ struct pt_regs32 regs;
+ compat_sigset_t mask;
+- /* __siginfo_fpu32_t * */ u32 fpu_save;
++ /* __siginfo_fpu_t * */ u32 fpu_save;
+ unsigned int insns[2];
+ stack_t32 stack;
+ unsigned int extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */
+ /* Only valid if (regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */
+ siginfo_extra_v8plus_t v8plus;
+- __siginfo_fpu_t fpu_state;
+-};
+-
+-/* Align macros */
+-#define SF_ALIGNEDSZ (((sizeof(struct signal_frame32) + 15) & (~15)))
+-#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame32) + 15) & (~15)))
++ /* __siginfo_rwin_t * */u32 rwin_save;
++} __attribute__((aligned(8)));
+
+ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
+ {
+@@ -192,30 +190,13 @@ int copy_siginfo_from_user32(siginfo_t *
+ return 0;
+ }
+
+-static int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
+-{
+- unsigned long *fpregs = current_thread_info()->fpregs;
+- unsigned long fprs;
+- int err;
+-
+- err = __get_user(fprs, &fpu->si_fprs);
+- fprs_write(0);
+- regs->tstate &= ~TSTATE_PEF;
+- if (fprs & FPRS_DL)
+- err |= copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32));
+- if (fprs & FPRS_DU)
+- err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32));
+- err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr);
+- err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr);
+- current_thread_info()->fpsaved[0] |= fprs;
+- return err;
+-}
+-
+ void do_sigreturn32(struct pt_regs *regs)
+ {
+ struct signal_frame32 __user *sf;
++ compat_uptr_t fpu_save;
++ compat_uptr_t rwin_save;
+ unsigned int psr;
+- unsigned pc, npc, fpu_save;
++ unsigned pc, npc;
+ sigset_t set;
+ unsigned seta[_COMPAT_NSIG_WORDS];
+ int err, i;
+@@ -273,8 +254,13 @@ void do_sigreturn32(struct pt_regs *regs
+ pt_regs_clear_syscall(regs);
+
+ err |= __get_user(fpu_save, &sf->fpu_save);
+- if (fpu_save)
+- err |= restore_fpu_state32(regs, &sf->fpu_state);
++ if (!err && fpu_save)
++ err |= restore_fpu_state(regs, compat_ptr(fpu_save));
++ err |= __get_user(rwin_save, &sf->rwin_save);
++ if (!err && rwin_save) {
++ if (restore_rwin_state(compat_ptr(rwin_save)))
++ goto segv;
++ }
+ err |= __get_user(seta[0], &sf->info.si_mask);
+ err |= copy_from_user(seta+1, &sf->extramask,
+ (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int));
+@@ -300,7 +286,9 @@ segv:
+ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
+ {
+ struct rt_signal_frame32 __user *sf;
+- unsigned int psr, pc, npc, fpu_save, u_ss_sp;
++ unsigned int psr, pc, npc, u_ss_sp;
++ compat_uptr_t fpu_save;
++ compat_uptr_t rwin_save;
+ mm_segment_t old_fs;
+ sigset_t set;
+ compat_sigset_t seta;
+@@ -359,8 +347,8 @@ asmlinkage void do_rt_sigreturn32(struct
+ pt_regs_clear_syscall(regs);
+
+ err |= __get_user(fpu_save, &sf->fpu_save);
+- if (fpu_save)
+- err |= restore_fpu_state32(regs, &sf->fpu_state);
++ if (!err && fpu_save)
++ err |= restore_fpu_state(regs, compat_ptr(fpu_save));
+ err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t));
+ err |= __get_user(u_ss_sp, &sf->stack.ss_sp);
+ st.ss_sp = compat_ptr(u_ss_sp);
+@@ -376,6 +364,12 @@ asmlinkage void do_rt_sigreturn32(struct
+ do_sigaltstack((stack_t __user *) &st, NULL, (unsigned long)sf);
+ set_fs(old_fs);
+
++ err |= __get_user(rwin_save, &sf->rwin_save);
++ if (!err && rwin_save) {
++ if (restore_rwin_state(compat_ptr(rwin_save)))
++ goto segv;
++ }
++
+ switch (_NSIG_WORDS) {
+ case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32);
+ case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32);
+@@ -433,26 +427,6 @@ static void __user *get_sigframe(struct
+ return (void __user *) sp;
+ }
+
+-static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
+-{
+- unsigned long *fpregs = current_thread_info()->fpregs;
+- unsigned long fprs;
+- int err = 0;
+-
+- fprs = current_thread_info()->fpsaved[0];
+- if (fprs & FPRS_DL)
+- err |= copy_to_user(&fpu->si_float_regs[0], fpregs,
+- (sizeof(unsigned int) * 32));
+- if (fprs & FPRS_DU)
+- err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16,
+- (sizeof(unsigned int) * 32));
+- err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr);
+- err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr);
+- err |= __put_user(fprs, &fpu->si_fprs);
+-
+- return err;
+-}
+-
+ /* The I-cache flush instruction only works in the primary ASI, which
+ * right now is the nucleus, aka. kernel space.
+ *
+@@ -515,18 +489,23 @@ static int setup_frame32(struct k_sigact
+ int signo, sigset_t *oldset)
+ {
+ struct signal_frame32 __user *sf;
++ int i, err, wsaved;
++ void __user *tail;
+ int sigframe_size;
+ u32 psr;
+- int i, err;
+ unsigned int seta[_COMPAT_NSIG_WORDS];
+
+ /* 1. Make sure everything is clean */
+ synchronize_user_stack();
+ save_and_clear_fpu();
+
+- sigframe_size = SF_ALIGNEDSZ;
+- if (!(current_thread_info()->fpsaved[0] & FPRS_FEF))
+- sigframe_size -= sizeof(__siginfo_fpu_t);
++ wsaved = get_thread_wsaved();
++
++ sigframe_size = sizeof(*sf);
++ if (current_thread_info()->fpsaved[0] & FPRS_FEF)
++ sigframe_size += sizeof(__siginfo_fpu_t);
++ if (wsaved)
++ sigframe_size += sizeof(__siginfo_rwin_t);
+
+ sf = (struct signal_frame32 __user *)
+ get_sigframe(&ka->sa, regs, sigframe_size);
+@@ -534,8 +513,7 @@ static int setup_frame32(struct k_sigact
+ if (invalid_frame_pointer(sf, sigframe_size))
+ goto sigill;
+
+- if (get_thread_wsaved() != 0)
+- goto sigill;
++ tail = (sf + 1);
+
+ /* 2. Save the current process state */
+ if (test_thread_flag(TIF_32BIT)) {
+@@ -560,11 +538,22 @@ static int setup_frame32(struct k_sigact
+ &sf->v8plus.asi);
+
+ if (psr & PSR_EF) {
+- err |= save_fpu_state32(regs, &sf->fpu_state);
+- err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save);
++ __siginfo_fpu_t __user *fp = tail;
++ tail += sizeof(*fp);
++ err |= save_fpu_state(regs, fp);
++ err |= __put_user((u64)fp, &sf->fpu_save);
+ } else {
+ err |= __put_user(0, &sf->fpu_save);
+ }
++ if (wsaved) {
++ __siginfo_rwin_t __user *rwp = tail;
++ tail += sizeof(*rwp);
++ err |= save_rwin_state(wsaved, rwp);
++ err |= __put_user((u64)rwp, &sf->rwin_save);
++ set_thread_wsaved(0);
++ } else {
++ err |= __put_user(0, &sf->rwin_save);
++ }
+
+ switch (_NSIG_WORDS) {
+ case 4: seta[7] = (oldset->sig[3] >> 32);
+@@ -580,10 +569,21 @@ static int setup_frame32(struct k_sigact
+ err |= __copy_to_user(sf->extramask, seta + 1,
+ (_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int));
+
+- err |= copy_in_user((u32 __user *)sf,
+- (u32 __user *)(regs->u_regs[UREG_FP]),
+- sizeof(struct reg_window32));
+-
++ if (!wsaved) {
++ err |= copy_in_user((u32 __user *)sf,
++ (u32 __user *)(regs->u_regs[UREG_FP]),
++ sizeof(struct reg_window32));
++ } else {
++ struct reg_window *rp;
++
++ rp = &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-2.6.32/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch b/queue-2.6.32/sparc-fix-array-bounds-error-setting-up-pcic-nmi-trap.patch
new file mode 100644
index 0000000..3bf846d
--- /dev/null
+++ b/queue-2.6.32/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
+@@ -350,8 +350,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];