Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
Browse files Browse the repository at this point in the history
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc:
  sparc: Allow handling signals when stack is corrupted.
  • Loading branch information
torvalds committed Aug 24, 2011
2 parents 2da2c66 + 5598473 commit 2983573
Show file tree
Hide file tree
Showing 8 changed files with 468 additions and 233 deletions.
14 changes: 14 additions & 0 deletions arch/sparc/include/asm/sigcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -73,6 +86,7 @@ struct sigcontext {
unsigned long ss_size;
} sigc_stack;
unsigned long sigc_mask;
__siginfo_rwin_t * sigc_rwin_save;
};

#else
Expand Down
1 change: 1 addition & 0 deletions arch/sparc/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ obj-$(CONFIG_SPARC32) += sun4m_irq.o sun4c_irq.o sun4d_irq.o

obj-y += process_$(BITS).o
obj-y += signal_$(BITS).o
obj-y += sigutil_$(BITS).o
obj-$(CONFIG_SPARC32) += ioport.o
obj-y += setup_$(BITS).o
obj-y += idprom.o
Expand Down
184 changes: 105 additions & 79 deletions arch/sparc/kernel/signal32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -192,30 +190,13 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
return 0;
}

static int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
{
unsigned long *fpregs = current_thread_info()->fpregs;
unsigned long fprs;
int err;

err = __get_user(fprs, &fpu->si_fprs);
fprs_write(0);
regs->tstate &= ~TSTATE_PEF;
if (fprs & FPRS_DL)
err |= copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32));
if (fprs & FPRS_DU)
err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32));
err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr);
err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr);
current_thread_info()->fpsaved[0] |= fprs;
return err;
}

void do_sigreturn32(struct pt_regs *regs)
{
struct signal_frame32 __user *sf;
compat_uptr_t fpu_save;
compat_uptr_t rwin_save;
unsigned int psr;
unsigned pc, npc, fpu_save;
unsigned pc, npc;
sigset_t set;
unsigned seta[_COMPAT_NSIG_WORDS];
int err, i;
Expand Down Expand Up @@ -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));
Expand All @@ -300,7 +286,9 @@ void do_sigreturn32(struct pt_regs *regs)
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;
Expand Down Expand Up @@ -359,8 +347,8 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
pt_regs_clear_syscall(regs);

err |= __get_user(fpu_save, &sf->fpu_save);
if (fpu_save)
err |= restore_fpu_state32(regs, &sf->fpu_state);
if (!err && fpu_save)
err |= restore_fpu_state(regs, compat_ptr(fpu_save));
err |= copy_from_user(&seta, &sf->mask, sizeof(compat_sigset_t));
err |= __get_user(u_ss_sp, &sf->stack.ss_sp);
st.ss_sp = compat_ptr(u_ss_sp);
Expand All @@ -376,6 +364,12 @@ asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
do_sigaltstack((stack_t __user *) &st, NULL, (unsigned long)sf);
set_fs(old_fs);

err |= __get_user(rwin_save, &sf->rwin_save);
if (!err && rwin_save) {
if (restore_rwin_state(compat_ptr(rwin_save)))
goto segv;
}

switch (_NSIG_WORDS) {
case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32);
case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32);
Expand Down Expand Up @@ -433,26 +427,6 @@ static void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, uns
return (void __user *) sp;
}

static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
{
unsigned long *fpregs = current_thread_info()->fpregs;
unsigned long fprs;
int err = 0;

fprs = current_thread_info()->fpsaved[0];
if (fprs & FPRS_DL)
err |= copy_to_user(&fpu->si_float_regs[0], fpregs,
(sizeof(unsigned int) * 32));
if (fprs & FPRS_DU)
err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16,
(sizeof(unsigned int) * 32));
err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr);
err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr);
err |= __put_user(fprs, &fpu->si_fprs);

return err;
}

/* The I-cache flush instruction only works in the primary ASI, which
* right now is the nucleus, aka. kernel space.
*
Expand Down Expand Up @@ -515,27 +489,31 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,
int signo, sigset_t *oldset)
{
struct signal_frame32 __user *sf;
int i, err, wsaved;
void __user *tail;
int sigframe_size;
u32 psr;
int i, err;
unsigned int seta[_COMPAT_NSIG_WORDS];

/* 1. Make sure everything is clean */
synchronize_user_stack();
save_and_clear_fpu();

sigframe_size = SF_ALIGNEDSZ;
if (!(current_thread_info()->fpsaved[0] & FPRS_FEF))
sigframe_size -= sizeof(__siginfo_fpu_t);
wsaved = get_thread_wsaved();

sigframe_size = sizeof(*sf);
if (current_thread_info()->fpsaved[0] & FPRS_FEF)
sigframe_size += sizeof(__siginfo_fpu_t);
if (wsaved)
sigframe_size += sizeof(__siginfo_rwin_t);

sf = (struct signal_frame32 __user *)
get_sigframe(&ka->sa, regs, sigframe_size);

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)) {
Expand All @@ -560,11 +538,22 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,
&sf->v8plus.asi);

if (psr & PSR_EF) {
err |= save_fpu_state32(regs, &sf->fpu_state);
err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save);
__siginfo_fpu_t __user *fp = tail;
tail += sizeof(*fp);
err |= save_fpu_state(regs, fp);
err |= __put_user((u64)fp, &sf->fpu_save);
} else {
err |= __put_user(0, &sf->fpu_save);
}
if (wsaved) {
__siginfo_rwin_t __user *rwp = tail;
tail += sizeof(*rwp);
err |= save_rwin_state(wsaved, rwp);
err |= __put_user((u64)rwp, &sf->rwin_save);
set_thread_wsaved(0);
} else {
err |= __put_user(0, &sf->rwin_save);
}

switch (_NSIG_WORDS) {
case 4: seta[7] = (oldset->sig[3] >> 32);
Expand All @@ -580,10 +569,21 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,
err |= __copy_to_user(sf->extramask, seta + 1,
(_COMPAT_NSIG_WORDS - 1) * sizeof(unsigned int));

err |= copy_in_user((u32 __user *)sf,
(u32 __user *)(regs->u_regs[UREG_FP]),
sizeof(struct reg_window32));

if (!wsaved) {
err |= copy_in_user((u32 __user *)sf,
(u32 __user *)(regs->u_regs[UREG_FP]),
sizeof(struct reg_window32));
} else {
struct reg_window *rp;

rp = &current_thread_info()->reg_window[wsaved - 1];
for (i = 0; i < 8; i++)
err |= __put_user(rp->locals[i], &sf->ss.locals[i]);
for (i = 0; i < 6; i++)
err |= __put_user(rp->ins[i], &sf->ss.ins[i]);
err |= __put_user(rp->ins[6], &sf->ss.fp);
err |= __put_user(rp->ins[7], &sf->ss.callers_pc);
}
if (err)
goto sigsegv;

Expand Down Expand Up @@ -613,7 +613,6 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs,
err |= __put_user(0x91d02010, &sf->insns[1]); /*t 0x10*/
if (err)
goto sigsegv;

flush_signal_insns(address);
}
return 0;
Expand All @@ -632,27 +631,31 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,
siginfo_t *info)
{
struct rt_signal_frame32 __user *sf;
int i, err, wsaved;
void __user *tail;
int sigframe_size;
u32 psr;
int i, err;
compat_sigset_t seta;

/* 1. Make sure everything is clean */
synchronize_user_stack();
save_and_clear_fpu();

sigframe_size = RT_ALIGNEDSZ;
if (!(current_thread_info()->fpsaved[0] & FPRS_FEF))
sigframe_size -= sizeof(__siginfo_fpu_t);
wsaved = get_thread_wsaved();

sigframe_size = sizeof(*sf);
if (current_thread_info()->fpsaved[0] & FPRS_FEF)
sigframe_size += sizeof(__siginfo_fpu_t);
if (wsaved)
sigframe_size += sizeof(__siginfo_rwin_t);

sf = (struct rt_signal_frame32 __user *)
get_sigframe(&ka->sa, regs, sigframe_size);

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)) {
Expand All @@ -677,11 +680,22 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,
&sf->v8plus.asi);

if (psr & PSR_EF) {
err |= save_fpu_state32(regs, &sf->fpu_state);
err |= __put_user((u64)&sf->fpu_state, &sf->fpu_save);
__siginfo_fpu_t __user *fp = tail;
tail += sizeof(*fp);
err |= save_fpu_state(regs, fp);
err |= __put_user((u64)fp, &sf->fpu_save);
} else {
err |= __put_user(0, &sf->fpu_save);
}
if (wsaved) {
__siginfo_rwin_t __user *rwp = tail;
tail += sizeof(*rwp);
err |= save_rwin_state(wsaved, rwp);
err |= __put_user((u64)rwp, &sf->rwin_save);
set_thread_wsaved(0);
} else {
err |= __put_user(0, &sf->rwin_save);
}

/* Update the siginfo structure. */
err |= copy_siginfo_to_user32(&sf->info, info);
Expand All @@ -703,9 +717,21 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs,
}
err |= __copy_to_user(&sf->mask, &seta, sizeof(compat_sigset_t));

err |= copy_in_user((u32 __user *)sf,
(u32 __user *)(regs->u_regs[UREG_FP]),
sizeof(struct reg_window32));
if (!wsaved) {
err |= copy_in_user((u32 __user *)sf,
(u32 __user *)(regs->u_regs[UREG_FP]),
sizeof(struct reg_window32));
} else {
struct reg_window *rp;

rp = &current_thread_info()->reg_window[wsaved - 1];
for (i = 0; i < 8; i++)
err |= __put_user(rp->locals[i], &sf->ss.locals[i]);
for (i = 0; i < 6; i++)
err |= __put_user(rp->ins[i], &sf->ss.ins[i]);
err |= __put_user(rp->ins[6], &sf->ss.fp);
err |= __put_user(rp->ins[7], &sf->ss.callers_pc);
}
if (err)
goto sigsegv;

Expand Down
Loading

0 comments on commit 2983573

Please sign in to comment.