diff options
Diffstat (limited to 'arch/alpha/kernel/signal.c')
| -rw-r--r-- | arch/alpha/kernel/signal.c | 358 |
1 files changed, 119 insertions, 239 deletions
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 2e45e8604e3..6cec2881acb 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -15,11 +15,12 @@ #include <linux/unistd.h> #include <linux/mm.h> #include <linux/smp.h> -#include <linux/smp_lock.h> #include <linux/stddef.h> #include <linux/tty.h> #include <linux/binfmts.h> #include <linux/bitops.h> +#include <linux/syscalls.h> +#include <linux/tracehook.h> #include <asm/uaccess.h> #include <asm/sigcontext.h> @@ -33,58 +34,29 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage void ret_from_sys_call(void); -static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, - unsigned long, unsigned long); - /* * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. - * - * how: - * 1 - SIG_BLOCK - * 2 - SIG_UNBLOCK - * 3 - SIG_SETMASK - * - * We change the range to -1 .. 1 in order to let gcc easily - * use the conditional move instructions. - * - * Note that we don't need to acquire the kernel lock for SMP - * operation, as all of this is local to this thread. */ -asmlinkage unsigned long -do_osf_sigprocmask(int how, unsigned long newmask, struct pt_regs *regs) +SYSCALL_DEFINE2(osf_sigprocmask, int, how, unsigned long, newmask) { - unsigned long oldmask = -EINVAL; - - if ((unsigned long)how-1 <= 2) { - long sign = how-2; /* -1 .. 1 */ - unsigned long block, unblock; - - newmask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - oldmask = current->blocked.sig[0]; - - unblock = oldmask & ~newmask; - block = oldmask | newmask; - if (!sign) - block = unblock; - if (sign <= 0) - newmask = block; - if (_NSIG_WORDS > 1 && sign > 0) - sigemptyset(¤t->blocked); - current->blocked.sig[0] = newmask; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->r0 = 0; /* special no error return */ + sigset_t oldmask; + sigset_t mask; + unsigned long res; + + siginitset(&mask, newmask & _BLOCKABLE); + res = sigprocmask(how, &mask, &oldmask); + if (!res) { + force_successful_syscall_return(); + res = oldmask.sig[0]; } - return oldmask; + return res; } -asmlinkage int -osf_sigaction(int sig, const struct osf_sigaction __user *act, - struct osf_sigaction __user *oact) +SYSCALL_DEFINE3(osf_sigaction, int, sig, + const struct osf_sigaction __user *, act, + struct osf_sigaction __user *, oact) { struct k_sigaction new_ka, old_ka; int ret; @@ -93,9 +65,9 @@ osf_sigaction(int sig, const struct osf_sigaction __user *act, old_sigset_t mask; if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_flags, &act->sa_flags)) + __get_user(new_ka.sa.sa_flags, &act->sa_flags) || + __get_user(mask, &act->sa_mask)) return -EFAULT; - __get_user(mask, &act->sa_mask); siginitset(&new_ka.sa.sa_mask, mask); new_ka.ka_restorer = NULL; } @@ -105,18 +77,17 @@ osf_sigaction(int sig, const struct osf_sigaction __user *act, if (!ret && oact) { if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_flags, &oact->sa_flags)) + __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) return -EFAULT; - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); } return ret; } -asmlinkage long -sys_rt_sigaction(int sig, const struct sigaction __user *act, - struct sigaction __user *oact, - size_t sigsetsize, void __user *restorer) +SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act, + struct sigaction __user *, oact, + size_t, sigsetsize, void __user *, restorer) { struct k_sigaction new_ka, old_ka; int ret; @@ -142,72 +113,6 @@ sys_rt_sigaction(int sig, const struct sigaction __user *act, } /* - * Atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage int -do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw) -{ - sigset_t oldset; - - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - oldset = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - /* Indicate EINTR on return from any possible signal handler, - which will not come back through here, but via sigreturn. */ - regs->r0 = EINTR; - regs->r19 = 1; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&oldset, regs, sw, 0, 0)) - return -EINTR; - } -} - -asmlinkage int -do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize, - struct pt_regs *regs, struct switch_stack *sw) -{ - sigset_t oldset, set; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - if (copy_from_user(&set, uset, sizeof(set))) - return -EFAULT; - - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - oldset = current->blocked; - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - /* Indicate EINTR on return from any possible signal handler, - which will not come back through here, but via sigreturn. */ - regs->r0 = EINTR; - regs->r19 = 1; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&oldset, regs, sw, 0, 0)) - return -EINTR; - } -} - -asmlinkage int -sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) -{ - return do_sigaltstack(uss, uoss, rdusp()); -} - -/* * Do a signal return; undo the signal stack. */ @@ -239,12 +144,14 @@ extern char compile_time_assert #define INSN_CALLSYS 0x00000083 static long -restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, - struct switch_stack *sw) +restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs) { unsigned long usp; + struct switch_stack *sw = (struct switch_stack *)regs - 1; long i, err = __get_user(regs->pc, &sc->sc_pc); + current_thread_info()->restart_block.fn = do_no_restart_syscall; + sw->r26 = (unsigned long) ret_from_sys_call; err |= __get_user(regs->r0, sc->sc_regs+0); @@ -292,9 +199,9 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, registers and transfer control from userland. */ asmlinkage void -do_sigreturn(struct sigcontext __user *sc, struct pt_regs *regs, - struct switch_stack *sw) +do_sigreturn(struct sigcontext __user *sc) { + struct pt_regs *regs = current_pt_regs(); sigset_t set; /* Verify that it's a good sigcontext before using it */ @@ -303,13 +210,9 @@ do_sigreturn(struct sigcontext __user *sc, struct pt_regs *regs, if (__get_user(set.sig[0], &sc->sc_mask)) goto give_sigsegv; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); - if (restore_sigcontext(sc, regs, sw)) + if (restore_sigcontext(sc, regs)) goto give_sigsegv; /* Send SIGTRAP if we're single-stepping: */ @@ -330,9 +233,9 @@ give_sigsegv: } asmlinkage void -do_rt_sigreturn(struct rt_sigframe __user *frame, struct pt_regs *regs, - struct switch_stack *sw) +do_rt_sigreturn(struct rt_sigframe __user *frame) { + struct pt_regs *regs = current_pt_regs(); sigset_t set; /* Verify that it's a good ucontext_t before using it */ @@ -341,13 +244,9 @@ do_rt_sigreturn(struct rt_sigframe __user *frame, struct pt_regs *regs, if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto give_sigsegv; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(&set); - if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw)) + if (restore_sigcontext(&frame->uc.uc_mcontext, regs)) goto give_sigsegv; /* Send SIGTRAP if we're single-stepping: */ @@ -373,18 +272,16 @@ give_sigsegv: */ static inline void __user * -get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +get_sigframe(struct ksignal *ksig, unsigned long sp, size_t frame_size) { - if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) - sp = current->sas_ss_sp + current->sas_ss_size; - - return (void __user *)((sp - frame_size) & -32ul); + return (void __user *)((sigsp(sp, ksig) - frame_size) & -32ul); } static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, - struct switch_stack *sw, unsigned long mask, unsigned long sp) + unsigned long mask, unsigned long sp) { + struct switch_stack *sw = (struct switch_stack *)regs - 1; long i, err = 0; err |= __put_user(on_sig_stack((unsigned long)sc), &sc->sc_onstack); @@ -437,27 +334,25 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, return err; } -static void -setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, - struct pt_regs *regs, struct switch_stack * sw) +static int +setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { unsigned long oldsp, r26, err = 0; struct sigframe __user *frame; oldsp = rdusp(); - frame = get_sigframe(ka, oldsp, sizeof(*frame)); + frame = get_sigframe(ksig, oldsp, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - err |= setup_sigcontext(&frame->sc, regs, sw, set->sig[0], oldsp); + err |= setup_sigcontext(&frame->sc, regs, set->sig[0], oldsp); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ - if (ka->ka_restorer) { - r26 = (unsigned long) ka->ka_restorer; - } else { + r26 = (unsigned long) ksig->ka.ka_restorer; + if (!r26) { err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); err |= __put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1); err |= __put_user(INSN_CALLSYS, frame->retcode+2); @@ -467,12 +362,12 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* Check that everything was written properly. */ if (err) - goto give_sigsegv; + return err; /* "Return" to the handler */ regs->r26 = r26; - regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; - regs->r16 = sig; /* a0: signal number */ + regs->r27 = regs->pc = (unsigned long) ksig->ka.sa.sa_handler; + regs->r16 = ksig->sig; /* a0: signal number */ regs->r17 = 0; /* a1: exception code */ regs->r18 = (unsigned long) &frame->sc; /* a2: sigcontext pointer */ wrusp((unsigned long) frame); @@ -481,45 +376,37 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", current->comm, current->pid, frame, regs->pc, regs->r26); #endif - - return; - -give_sigsegv: - force_sigsegv(sig, current); + return 0; } -static void -setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs, struct switch_stack * sw) +static int +setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { unsigned long oldsp, r26, err = 0; struct rt_sigframe __user *frame; oldsp = rdusp(); - frame = get_sigframe(ka, oldsp, sizeof(*frame)); + frame = get_sigframe(ksig, oldsp, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - err |= copy_siginfo_to_user(&frame->info, info); + err |= copy_siginfo_to_user(&frame->info, &ksig->info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(set->sig[0], &frame->uc.uc_osf_sigmask); - err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(oldsp), &frame->uc.uc_stack.ss_flags); - err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, + err |= __save_altstack(&frame->uc.uc_stack, oldsp); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0], oldsp); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ - if (ka->ka_restorer) { - r26 = (unsigned long) ka->ka_restorer; - } else { + r26 = (unsigned long) ksig->ka.ka_restorer; + if (!r26) { err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); err |= __put_user(INSN_LDI_R0+__NR_rt_sigreturn, frame->retcode+1); @@ -529,12 +416,12 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, } if (err) - goto give_sigsegv; + return -EFAULT; /* "Return" to the handler */ regs->r26 = r26; - regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; - regs->r16 = sig; /* a0: signal number */ + regs->r27 = regs->pc = (unsigned long) ksig->ka.sa.sa_handler; + regs->r16 = ksig->sig; /* a0: signal number */ regs->r17 = (unsigned long) &frame->info; /* a1: siginfo pointer */ regs->r18 = (unsigned long) &frame->uc; /* a2: ucontext pointer */ wrusp((unsigned long) frame); @@ -544,10 +431,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, current->comm, current->pid, frame, regs->pc, regs->r26); #endif - return; - -give_sigsegv: - force_sigsegv(sig, current); + return 0; } @@ -555,23 +439,17 @@ give_sigsegv: * OK, we're invoking a handler. */ static inline void -handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *oldset, struct pt_regs * regs, struct switch_stack *sw) +handle_signal(struct ksignal *ksig, struct pt_regs *regs) { - if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs, sw); - else - setup_frame(sig, ka, oldset, regs, sw); + sigset_t *oldset = sigmask_to_save(); + int ret; - if (ka->sa.sa_flags & SA_RESETHAND) - ka->sa.sa_handler = SIG_DFL; + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(ksig, oldset, regs); + else + ret = setup_frame(ksig, oldset, regs); - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + signal_setup_done(ret, ksig, 0); } static inline void @@ -592,7 +470,6 @@ syscall_restart(unsigned long r0, unsigned long r19, regs->pc -= 4; break; case ERESTART_RESTARTBLOCK: - current_thread_info()->restart_block.fn = do_no_restart_syscall; regs->r0 = EINTR; break; } @@ -612,60 +489,63 @@ syscall_restart(unsigned long r0, unsigned long r19, * restart. "r0" is also used as an indicator whether we can restart at * all (if we get here from anything but a syscall return, it will be 0) */ -static int -do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw, - unsigned long r0, unsigned long r19) +static void +do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19) { - siginfo_t info; - int signr; unsigned long single_stepping = ptrace_cancel_bpt(current); - struct k_sigaction ka; - - if (!oldset) - oldset = ¤t->blocked; + struct ksignal ksig; /* This lets the debugger run, ... */ - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - /* ... so re-check the single stepping. */ - single_stepping |= ptrace_cancel_bpt(current); - - if (signr > 0) { + if (get_signal(&ksig)) { + /* ... so re-check the single stepping. */ + single_stepping |= ptrace_cancel_bpt(current); /* Whee! Actually deliver the signal. */ - if (r0) syscall_restart(r0, r19, regs, &ka); - handle_signal(signr, &ka, &info, oldset, regs, sw); - if (single_stepping) - ptrace_set_bpt(current); /* re-set bpt */ - return 1; - } - - if (r0) { - switch (regs->r0) { - case ERESTARTNOHAND: - case ERESTARTSYS: - case ERESTARTNOINTR: - /* Reset v0 and a3 and replay syscall. */ - regs->r0 = r0; - regs->r19 = r19; - regs->pc -= 4; - break; - case ERESTART_RESTARTBLOCK: - /* Force v0 to the restart syscall and reply. */ - regs->r0 = __NR_restart_syscall; - regs->pc -= 4; - break; + if (r0) + syscall_restart(r0, r19, regs, &ksig.ka); + handle_signal(&ksig, regs); + } else { + single_stepping |= ptrace_cancel_bpt(current); + if (r0) { + switch (regs->r0) { + case ERESTARTNOHAND: + case ERESTARTSYS: + case ERESTARTNOINTR: + /* Reset v0 and a3 and replay syscall. */ + regs->r0 = r0; + regs->r19 = r19; + regs->pc -= 4; + break; + case ERESTART_RESTARTBLOCK: + /* Set v0 to the restart_syscall and replay */ + regs->r0 = __NR_restart_syscall; + regs->pc -= 4; + break; + } } + restore_saved_sigmask(); } if (single_stepping) ptrace_set_bpt(current); /* re-set breakpoint */ - - return 0; } void -do_notify_resume(sigset_t *oldset, struct pt_regs *regs, - struct switch_stack *sw, unsigned long r0, - unsigned long r19, unsigned long thread_info_flags) +do_work_pending(struct pt_regs *regs, unsigned long thread_flags, + unsigned long r0, unsigned long r19) { - if (thread_info_flags & _TIF_SIGPENDING) - do_signal(oldset, regs, sw, r0, r19); + do { + if (thread_flags & _TIF_NEED_RESCHED) { + schedule(); + } else { + local_irq_enable(); + if (thread_flags & _TIF_SIGPENDING) { + do_signal(regs, r0, r19); + r0 = 0; + } else { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(regs); + } + } + local_irq_disable(); + thread_flags = current_thread_info()->flags; + } while (thread_flags & _TIF_WORK_MASK); } |
