/* $Id: signal.c,v 1.110 2002/02/08 03:57:14 davem Exp $
* linux/arch/sparc/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/binfmts.h> /* do_coredum */
#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/ptrace.h>
#include <asm/svr4.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h> /* flush_sig_insns */
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
void *fpqueue, unsigned long *fpqdepth);
extern void fpload(unsigned long *fpregs, unsigned long *fsr);
/* Signal frames: the original one (compatible with SunOS):
*
* Set up a signal frame... Make the stack look the way SunOS
* expects it to look which is basically:
*
* ---------------------------------- <-- %sp at signal time
* Struct sigcontext
* Signal address
* Ptr to sigcontext area above
* Signal code
* The signal number itself
* One register window
* ---------------------------------- <-- New %sp
*/
struct signal_sframe {
struct reg_window sig_window;
int sig_num;
int sig_code;
struct sigcontext __user *sig_scptr;
int sig_address;
struct sigcontext sig_context;
unsigned int extramask[_NSIG_WORDS - 1];
};
/*
* And the new one, intended to be used for Linux applications only
* (we have enough in there to work with clone).
* All the interesting bits are in the info field.
*/
struct new_signal_frame {
struct sparc_stackf ss;
__siginfo_t info;
__siginfo_fpu_t __user *fpu_save;
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;
};
struct rt_signal_frame {
struct sparc_stackf ss;
siginfo_t info;
struct pt_regs regs;
sigset_t mask;
__siginfo_fpu_t __user *fpu_save;
unsigned int insns[2];
stack_t stack;
unsigned int extra_size; /* Should be 0 */
__siginfo_fpu_t fpu_state;
};
/* Align macros */
#define SF_ALIGNEDSZ (((sizeof(struct signal_sframe) + 7) & (~7)))
#define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7)))
#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7)))
static int _sigpause_common(old_sigset_t set)
{
set &= _BLOCKABLE;
spin_lock_irq(¤t->sighand->siglock);
current->saved_sigmask = current->blocked;
siginitset(¤t->blocked, set);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
current->state = TASK_INTERRUPTIBLE;
schedule();
set_thread_flag(TIF_RESTORE_SIGMASK);
return -ERESTARTNOHAND;
}
asmlinkage int sys_sigpause(unsigned int set)
{
return _sigpause_common(set);
}
asmlinkage int sys_sigsuspend(old_sigset_t set)
{
return _sigpause_common(set);
}
static inline int
restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
{
int err;
#ifdef CONFIG_SMP
if (test_tsk_thread_flag(current, TIF_USEDFPU))
regs->psr &= ~PSR_EF;
#else
if (current == last_task_used_math) {
last_task_used_math = NULL;
regs->psr &= ~PSR_EF;
}
#endif
set_used_math();
clear_tsk_thread_flag(current, TIF_USEDFPU);
if (!access_ok(VERIFY_READ, fpu, sizeof(*fpu)))
return -EFAULT;
err = __copy_from_user(¤t->thread.float_regs[0], &fpu->si_float_regs[0],
(sizeof(unsigned long) * 32));