* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
-#include <linux/config.h>
#ifdef CONFIG_SPARC32_COMPAT
#include <linux/compat.h> /* for compat_old_sigset_t */
#endif
#include <linux/tty.h>
#include <linux/smp_lock.h>
#include <linux/binfmts.h>
+#include <linux/bitops.h>
#include <asm/uaccess.h>
-#include <asm/bitops.h>
#include <asm/ptrace.h>
#include <asm/svr4.h>
#include <asm/pgtable.h>
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-static int do_signal(sigset_t *oldset, struct pt_regs * regs,
- unsigned long orig_o0, int ret_from_syscall);
-
/* {set, get}context() needed for 64-bit SparcLinux userland. */
asmlinkage void sparc64_set_context(struct pt_regs *regs)
{
flush_user_windows();
if (get_thread_wsaved() ||
(((unsigned long)ucp) & (sizeof(unsigned long)-1)) ||
- (!__access_ok((unsigned long)ucp, sizeof(*ucp))))
+ (!__access_ok(ucp, sizeof(*ucp))))
goto do_sigsegv;
grp = &ucp->uc_mcontext.mc_gregs;
err = __get_user(pc, &((*grp)[MC_PC]));
regs->tnpc = npc;
err |= __get_user(regs->y, &((*grp)[MC_Y]));
err |= __get_user(tstate, &((*grp)[MC_TSTATE]));
- regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
- regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC));
+ regs->tstate &= ~(TSTATE_ASI | TSTATE_ICC | TSTATE_XCC);
+ regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC));
err |= __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1]));
err |= __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2]));
err |= __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3]));
return;
do_sigsegv:
- do_exit(SIGSEGV);
+ force_sig(SIGSEGV, current);
}
asmlinkage void sparc64_get_context(struct pt_regs *regs)
return;
do_sigsegv:
- do_exit(SIGSEGV);
+ force_sig(SIGSEGV, current);
}
struct rt_signal_frame {
/* Align macros */
#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7)))
-/*
- * atomically swap in the new signal mask, and wait for a signal.
- * This is really tricky on the Sparc, watch out...
- */
-asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs)
+static long _sigpause_common(old_sigset_t set)
{
- sigset_t saveset;
-
-#ifdef CONFIG_SPARC32_COMPAT
- if (test_thread_flag(TIF_32BIT)) {
- extern asmlinkage void _sigpause32_common(compat_old_sigset_t,
- struct pt_regs *);
- _sigpause32_common(set, regs);
- return;
- }
-#endif
set &= _BLOCKABLE;
spin_lock_irq(¤t->sighand->siglock);
- saveset = current->blocked;
+ current->saved_sigmask = current->blocked;
siginitset(¤t->blocked, set);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc = (regs->tnpc & 0xffffffff);
- regs->tnpc = (regs->tnpc + 4) & 0xffffffff;
- } else {
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- }
-
- /* Condition codes and return value where set here for sigpause,
- * and so got used by setup_frame, which again causes sigreturn()
- * to return -EINTR.
- */
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- /*
- * Return -EINTR and set condition code here,
- * so the interrupted system call actually returns
- * these.
- */
- regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
- regs->u_regs[UREG_I0] = EINTR;
- if (do_signal(&saveset, regs, 0, 0))
- return;
- }
-}
-asmlinkage void do_sigpause(unsigned int set, struct pt_regs *regs)
-{
- _sigpause_common(set, regs);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ set_thread_flag(TIF_RESTORE_SIGMASK);
+ return -ERESTARTNOHAND;
}
-asmlinkage void do_sigsuspend(struct pt_regs *regs)
+asmlinkage long sys_sigpause(unsigned int set)
{
- _sigpause_common(regs->u_regs[UREG_I0], regs);
+ return _sigpause_common(set);
}
-asmlinkage void do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize, struct pt_regs *regs)
+asmlinkage long sys_sigsuspend(old_sigset_t set)
{
- sigset_t oldset, set;
-
- /* XXX: Don't preclude handling different sized sigset_t's. */
- if (sigsetsize != sizeof(sigset_t)) {
- regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
- regs->u_regs[UREG_I0] = EINVAL;
- return;
- }
- if (copy_from_user(&set, uset, sizeof(set))) {
- regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
- regs->u_regs[UREG_I0] = EFAULT;
- return;
- }
-
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sighand->siglock);
- oldset = current->blocked;
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc = (regs->tnpc & 0xffffffff);
- regs->tnpc = (regs->tnpc + 4) & 0xffffffff;
- } else {
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- }
-
- /* Condition codes and return value where set here for sigpause,
- * and so got used by setup_frame, which again causes sigreturn()
- * to return -EINTR.
- */
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- /*
- * Return -EINTR and set condition code here,
- * so the interrupted system call actually returns
- * these.
- */
- regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY);
- regs->u_regs[UREG_I0] = EINTR;
- if (do_signal(&oldset, regs, 0, 0))
- return;
- }
+ return _sigpause_common(set);
}
static inline int
err |= __get_user(tstate, &sf->regs.tstate);
err |= copy_from_user(regs->u_regs, sf->regs.u_regs, sizeof(regs->u_regs));
- /* User can only change condition codes in %tstate. */
- regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
- regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC));
+ /* User can only change condition codes and %asi in %tstate. */
+ regs->tstate &= ~(TSTATE_ASI | TSTATE_ICC | TSTATE_XCC);
+ regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC));
err |= __get_user(fpu_save, &sf->fpu_save);
if (fpu_save)
spin_unlock_irq(¤t->sighand->siglock);
return;
segv:
- send_sig(SIGSEGV, current, 1);
+ force_sig(SIGSEGV, current);
}
/* Checks if the fp is valid */
regs->u_regs[UREG_I0] = signo;
regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+ /* The sigcontext is passed in this way because of how it
+ * is defined in GLIBC's /usr/include/bits/sigcontext.h
+ * for sparc64. It includes the 128 bytes of siginfo_t.
+ */
+ regs->u_regs[UREG_I2] = (unsigned long) &sf->info;
+
/* 5. signal handler */
regs->tpc = (unsigned long) ka->sa.sa_handler;
regs->tnpc = (regs->tpc + 4);
sigill:
do_exit(SIGILL);
sigsegv:
- do_exit(SIGSEGV);
+ force_sigsegv(signo, current);
}
static inline void handle_signal(unsigned long signr, struct k_sigaction *ka,
{
setup_rt_frame(ka, regs, signr, oldset,
(ka->sa.sa_flags & SA_SIGINFO) ? info : NULL);
- if (ka->sa.sa_flags & SA_ONESHOT)
- ka->sa.sa_handler = SIG_DFL;
- if (!(ka->sa.sa_flags & SA_NOMASK)) {
- spin_lock_irq(¤t->sighand->siglock);
- sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
+ spin_lock_irq(¤t->sighand->siglock);
+ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NOMASK))
sigaddset(¤t->blocked,signr);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- }
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
}
static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
-static int do_signal(sigset_t *oldset, struct pt_regs * regs,
- unsigned long orig_i0, int restart_syscall)
+static void do_signal(struct pt_regs *regs, unsigned long orig_i0, int restart_syscall)
{
siginfo_t info;
struct signal_deliver_cookie cookie;
+ struct k_sigaction ka;
int signr;
+ sigset_t *oldset;
cookie.restart_syscall = restart_syscall;
cookie.orig_i0 = orig_i0;
- if (!oldset)
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = ¤t->saved_sigmask;
+ else
oldset = ¤t->blocked;
#ifdef CONFIG_SPARC32_COMPAT
if (test_thread_flag(TIF_32BIT)) {
- extern int do_signal32(sigset_t *, struct pt_regs *,
- unsigned long, int);
- return do_signal32(oldset, regs, orig_i0,
- cookie.restart_syscall);
+ extern void do_signal32(sigset_t *, struct pt_regs *,
+ unsigned long, int);
+ do_signal32(oldset, regs, orig_i0,
+ cookie.restart_syscall);
+ return;
}
#endif
- signr = get_signal_to_deliver(&info, regs, &cookie);
+ signr = get_signal_to_deliver(&info, &ka, regs, &cookie);
if (signr > 0) {
- struct k_sigaction *ka;
-
- ka = ¤t->sighand->action[signr-1];
-
if (cookie.restart_syscall)
- syscall_restart(orig_i0, regs, &ka->sa);
- handle_signal(signr, ka, &info, oldset, regs);
- return 1;
+ syscall_restart(orig_i0, regs, &ka.sa);
+ handle_signal(signr, &ka, &info, oldset, regs);
+
+ /* a signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TIF_RESTORE_SIGMASK flag.
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ return;
}
if (cookie.restart_syscall &&
(regs->u_regs[UREG_I0] == ERESTARTNOHAND ||
regs->tpc -= 4;
regs->tnpc -= 4;
}
- return 0;
+
+ /* if there's no signal to deliver, we just put the saved sigmask
+ * back
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
+ }
}
-void do_notify_resume(sigset_t *oldset, struct pt_regs *regs,
- unsigned long orig_i0, int restart_syscall,
+void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, int restart_syscall,
unsigned long thread_info_flags)
{
- if (thread_info_flags & _TIF_SIGPENDING)
- do_signal(oldset, regs, orig_i0, restart_syscall);
+ if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+ do_signal(regs, orig_i0, restart_syscall);
}
void ptrace_signal_deliver(struct pt_regs *regs, void *cookie)