#include <linux/personality.h>
#include <linux/tty.h>
#include <linux/binfmts.h>
-#include <linux/suspend.h>
#include <asm/setup.h>
#include <asm/uaccess.h>
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
/*
* Atomically swap in the new signal mask, and wait for a signal.
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(regs, &saveset))
+ if (do_signal(&saveset, regs))
return -EINTR;
}
}
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(regs, &saveset))
+ if (do_signal(&saveset, regs))
return -EINTR;
}
}
unsigned char retcode[8];
unsigned long extramask[_NSIG_WORDS-1];
struct sigcontext sc;
- int sig;
} __attribute__((aligned(2),packed));
struct rt_sigframe
#endif
long dummy_pc;
char *pretcode;
- struct siginfo *pinfo;
- void *puc;
unsigned char retcode[8];
struct siginfo info;
struct ucontext uc;
- int sig;
} __attribute__((aligned(2),packed));
static inline int
-restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc,
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp,
int *pd0)
{
+ struct sigcontext context;
int err = 0;
- unsigned int ccr;
- unsigned int usp;
- unsigned int er0;
-
- /* Always make any pending restarted system calls return -EINTR */
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
-
-#define COPY(r) err |= __get_user(regs->r, &usc->sc_##r) /* restore passed registers */
- COPY(er1);
- COPY(er2);
- COPY(er3);
- COPY(er5);
- COPY(pc);
- ccr = regs->ccr & 0x10;
- COPY(ccr);
-#undef COPY
- regs->ccr &= 0xef;
- regs->ccr |= ccr;
+
+ /* get previous context */
+ if (copy_from_user(&context, usc, sizeof(context)))
+ goto badframe;
+
+ /* restore passed registers */
+ regs->er1 = context.sc_er1;
+ regs->er2 = context.sc_er2;
+ regs->er3 = context.sc_er3;
+ regs->er5 = context.sc_er5;
+ regs->ccr = (regs->ccr & 0x10)|(context.sc_ccr & 0xef);
+ regs->pc = context.sc_pc;
regs->orig_er0 = -1; /* disable syscall checks */
- err |= __get_user(usp, &usc->sc_usp);
+ wrusp(context.sc_usp);
+
+ *pd0 = context.sc_er0;
+ return err;
+
+badframe:
+ return 1;
+}
+
+static inline int
+rt_restore_ucontext(struct pt_regs *regs, struct ucontext *uc, int *pd0)
+{
+ int temp;
+ greg_t *gregs = uc->uc_mcontext.gregs;
+ unsigned long usp;
+ int err;
+
+ err = __get_user(temp, &uc->uc_mcontext.version);
+ if (temp != MCONTEXT_VERSION)
+ goto badframe;
+ /* restore passed registers */
+ err |= __get_user(regs->er0, &gregs[0]);
+ err |= __get_user(regs->er1, &gregs[1]);
+ err |= __get_user(regs->er2, &gregs[2]);
+ err |= __get_user(regs->er3, &gregs[3]);
+ err |= __get_user(regs->er4, &gregs[4]);
+ err |= __get_user(regs->er5, &gregs[5]);
+ err |= __get_user(regs->er6, &gregs[6]);
+ err |= __get_user(usp, &gregs[7]);
wrusp(usp);
+ err |= __get_user(regs->pc, &gregs[8]);
+ err |= __get_user(temp, &gregs[9]);
+ regs->ccr = (regs->ccr & 0x10) | (temp & 0xef);
+ regs->orig_er0 = -1; /* disable syscall checks */
- err |= __get_user(er0, &usc->sc_er0);
- *pd0 = er0;
+ if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT)
+ goto badframe;
+
+ *pd0 = regs->er0;
return err;
+
+badframe:
+ return 1;
}
asmlinkage int do_sigreturn(unsigned long __unused,...)
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- if (restore_sigcontext(regs, &frame->sc, &er0))
+ if (restore_sigcontext(regs, &frame->sc, frame + 1, &er0))
goto badframe;
return er0;
recalc_sigpending();
spin_lock_irq(¤t->sighand->siglock);
- if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &er0))
+ if (rt_restore_ucontext(regs, &frame->uc, &er0))
goto badframe;
-
- if (do_sigaltstack(&frame->uc.uc_stack, NULL, usp) == -EFAULT)
- goto badframe;
-
return er0;
badframe:
return 0;
}
-static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
+static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
unsigned long mask)
{
- int err = 0;
+ sc->sc_mask = mask;
+ sc->sc_usp = rdusp();
+ sc->sc_er0 = regs->er0;
+ sc->sc_er1 = regs->er1;
+ sc->sc_er2 = regs->er2;
+ sc->sc_er3 = regs->er3;
+ sc->sc_er5 = regs->er5;
+ sc->sc_ccr = regs->ccr;
+ sc->sc_pc = regs->pc;
+}
- err |= __put_user(regs->er0, &sc->sc_er0);
- err |= __put_user(regs->er1, &sc->sc_er1);
- err |= __put_user(regs->er2, &sc->sc_er2);
- err |= __put_user(regs->er3, &sc->sc_er3);
- err |= __put_user(regs->er4, &sc->sc_er4);
- err |= __put_user(regs->er5, &sc->sc_er5);
- err |= __put_user(regs->er6, &sc->sc_er6);
- err |= __put_user(rdusp(), &sc->sc_usp);
- err |= __put_user(regs->pc, &sc->sc_pc);
- err |= __put_user(regs->ccr, &sc->sc_ccr);
- err |= __put_user(mask, &sc->sc_mask);
+static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs)
+{
+ greg_t *gregs = uc->uc_mcontext.gregs;
+ int err = 0;
+ err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version);
+ err |= __put_user(regs->er0, &gregs[0]);
+ err |= __put_user(regs->er1, &gregs[1]);
+ err |= __put_user(regs->er2, &gregs[2]);
+ err |= __put_user(regs->er3, &gregs[3]);
+ err |= __put_user(regs->er4, &gregs[4]);
+ err |= __put_user(regs->er5, &gregs[5]);
+ err |= __put_user(regs->er6, &gregs[6]);
+ err |= __put_user(rdusp(), &gregs[7]);
+ err |= __put_user(regs->pc, &gregs[8]);
+ err |= __put_user(regs->ccr, &gregs[9]);
return err;
}
sigset_t *set, struct pt_regs *regs)
{
struct sigframe *frame;
+ struct sigcontext context;
int err = 0;
- int usig;
- unsigned char *ret;
frame = get_sigframe(ka, regs, sizeof(*frame));
- if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
-
- usig = current_thread_info()->exec_domain
- && current_thread_info()->exec_domain->signal_invmap
- && sig < 32
- ? current_thread_info()->exec_domain->signal_invmap[sig]
- : sig;
-
- err |= __put_user(usig, &frame->sig);
- if (err)
- goto give_sigsegv;
-
- err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
- if (err)
- goto give_sigsegv;
-
- if (_NSIG_WORDS > 1) {
+ if (_NSIG_WORDS > 1)
err |= copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
- if (err)
- goto give_sigsegv;
- }
- ret = frame->retcode;
- if (ka->sa.sa_flags & SA_RESTORER)
- ret = (unsigned char *)(ka->sa.sa_restorer);
- else {
- /* sub.l er0,er0; mov.b #__NR_sigreturn,r0l; trapa #0 */
- err != __put_user(0x1a80f800 + (__NR_sigreturn & 0xff),
- (unsigned long *)(frame->retcode + 0));
- err |= __put_user(0x5700, (unsigned short *)(frame->retcode + 4));
- }
+ setup_sigcontext(&context, regs, set->sig[0]);
+ err |= copy_to_user (&frame->sc, &context, sizeof(context));
/* Set up to return from userspace. */
- err |= __put_user(ret, &frame->pretcode);
+ err |= __put_user(frame->retcode, &frame->pretcode);
+
+ /* sub.l er0,er0; mov.b #__NR_sigreturn,r0l; trapa #0 */
+ err != __put_user(0x1a80f800 + (__NR_sigreturn & 0xff),
+ (unsigned long *)(frame->retcode + 0));
+ err |= __put_user(0x5700, (unsigned short *)(frame->retcode + 4));
+
if (err)
goto give_sigsegv;
{
struct rt_sigframe *frame;
int err = 0;
- int usig;
- unsigned char *ret;
frame = get_sigframe(ka, regs, sizeof(*frame));
- if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
-
- usig = current_thread_info()->exec_domain
- && current_thread_info()->exec_domain->signal_invmap
- && sig < 32
- ? current_thread_info()->exec_domain->signal_invmap[sig]
- : sig;
-
- err |= __put_user(usig, &frame->sig);
- if (err)
- goto give_sigsegv;
-
- err |= __put_user(&frame->info, &frame->pinfo);
- err |= __put_user(&frame->uc, &frame->puc);
err |= copy_siginfo_to_user(&frame->info, info);
- if (err)
- goto give_sigsegv;
/* Create the ucontext. */
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(sas_ss_flags(rdusp()),
&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, set->sig[0]);
+ err |= rt_setup_ucontext(&frame->uc, regs);
err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set));
- if (err)
- goto give_sigsegv;
/* Set up to return from userspace. */
- ret = frame->retcode;
- if (ka->sa.sa_flags & SA_RESTORER)
- ret = (unsigned char *)(ka->sa.sa_restorer);
- else {
- /* sub.l er0,er0; mov.b #__NR_sigreturn,r0l; trapa #0 */
- err != __put_user(0x1a80f800 + (__NR_sigreturn & 0xff),
- (unsigned long *)(frame->retcode + 0));
- err |= __put_user(0x5700, (unsigned short *)(frame->retcode + 4));
- }
- err |= __put_user(ret, &frame->pretcode);
+ err |= __put_user(frame->retcode, &frame->pretcode);
+
+ /* sub.l er0,er0; mov.b #__NR_rt_sigreturn,r0l; trapa #0 */
+ err != __put_user(0x1a80f800 + (__NR_rt_sigreturn & 0xff),
+ (long *)(frame->retcode + 0));
+ err |= __put_user(0x5700, (short *)(frame->retcode + 4));
if (err)
goto give_sigsegv;
force_sigsegv(sig, current);
}
+static inline void
+handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler)
+{
+ switch (regs->er0) {
+ case -ERESTARTNOHAND:
+ if (!has_handler)
+ goto do_restart;
+ regs->er0 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) {
+ regs->er0 = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ do_restart:
+ regs->er0 = regs->orig_er0;
+ regs->pc -= 2;
+ break;
+ }
+}
+
/*
* OK, we're invoking a handler
*/
static void
-handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
- sigset_t *oldset, struct pt_regs * regs)
+handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *oldset, struct pt_regs *regs)
{
/* are we from a system call? */
- if (regs->orig_er0 >= 0) {
- switch (regs->er0) {
- case -ERESTART_RESTARTBLOCK:
- case -ERESTARTNOHAND:
- regs->er0 = -EINTR;
- break;
-
- case -ERESTARTSYS:
- if (!(ka->sa.sa_flags & SA_RESTART)) {
- regs->er0 = -EINTR;
- break;
- }
- /* fallthrough */
- case -ERESTARTNOINTR:
- regs->er0 = regs->orig_er0;
- regs->pc -= 2;
- }
- }
+ if (regs->orig_er0 >= 0)
+ /* If so, check system call restarting.. */
+ handle_restart(regs, ka, 1);
/* set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
else
setup_frame(sig, ka, oldset, regs);
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
if (!(ka->sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals
+ * that the kernel can handle, and then we build all the user-level signal
+ * handling stack-frames in one go after that.
*/
-asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
{
siginfo_t info;
- int signr;
- struct k_sigaction ka;
-
- /*
- * We want the common case to go fast, which
- * is why we may in certain cases get here from
- * kernel mode. Just return without doing anything
- * if so.
- */
- if ((regs->ccr & 0x10))
- return 1;
-
- if (current->flags & PF_FREEZE) {
- refrigerator(0);
- goto no_signal;
- }
+ struct k_sigaction *ka;
current->thread.esp0 = (unsigned long) regs;
if (!oldset)
oldset = ¤t->blocked;
- signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- if (signr > 0) {
+ for (;;) {
+ int signr;
+
+ signr = get_signal_to_deliver(&info, regs, NULL);
+
+ if (!signr)
+ break;
+
+ if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
+ current->exit_code = signr;
+ current->state = TASK_STOPPED;
+
+ /* Did we come from a system call? */
+ if (regs->orig_er0 >= 0) {
+ /* Restart the system call the same way as
+ if the process were not traced. */
+ struct k_sigaction *ka =
+ ¤t->sighand->action[signr-1];
+ int has_handler =
+ (ka->sa.sa_handler != SIG_IGN &&
+ ka->sa.sa_handler != SIG_DFL);
+ handle_restart(regs, ka, has_handler);
+ }
+ notify_parent(current, SIGCHLD);
+ schedule();
+
+ /* We're back. Did the debugger cancel the sig? */
+ if (!(signr = current->exit_code)) {
+ discard_frame:
+ continue;
+ }
+ current->exit_code = 0;
+
+ /* The debugger continued. Ignore SIGSTOP. */
+ if (signr == SIGSTOP)
+ goto discard_frame;
+
+ /* Update the siginfo structure. Is this good? */
+ if (signr != info.si_signo) {
+ info.si_signo = signr;
+ info.si_errno = 0;
+ info.si_code = SI_USER;
+ info.si_pid = current->parent->pid;
+ info.si_uid = current->parent->uid;
+ }
+
+ /* If the (new) signal is now blocked, requeue it. */
+ if (sigismember(¤t->blocked, signr)) {
+ send_sig_info(signr, &info, current);
+ continue;
+ }
+ }
+
+ ka = ¤t->sighand->action[signr-1];
+ if (ka->sa.sa_handler == SIG_IGN) {
+ if (signr != SIGCHLD)
+ continue;
+ /* Check for SIGCHLD: it's special. */
+ while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
+ /* nothing */;
+ continue;
+ }
+
+ if (ka->sa.sa_handler == SIG_DFL) {
+ int exit_code = signr;
+
+ if (current->pid == 1)
+ continue;
+
+ switch (signr) {
+ case SIGCONT: case SIGCHLD:
+ case SIGWINCH: case SIGURG:
+ continue;
+
+ case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ if (is_orphaned_pgrp(process_group(current)))
+ continue;
+ /* FALLTHRU */
+
+ case SIGSTOP: {
+ struct sighand_struct *sig;
+ current->state = TASK_STOPPED;
+ current->exit_code = signr;
+ sig = current->parent->sighand;
+ if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags
+& SA_NOCLDSTOP))
+ notify_parent(current, SIGCHLD);
+ schedule();
+ continue;
+ }
+
+ case SIGQUIT: case SIGILL: case SIGTRAP:
+ case SIGIOT: case SIGFPE: case SIGSEGV:
+ case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
+ if (do_coredump(signr, exit_code, regs))
+ exit_code |= 0x80;
+ /* FALLTHRU */
+
+ default:
+ sigaddset(¤t->pending.signal, signr);
+ recalc_sigpending();
+ current->flags |= PF_SIGNALED;
+ do_exit(exit_code);
+ /* NOTREACHED */
+ }
+ }
+
/* Whee! Actually deliver the signal. */
- handle_signal(signr, &info, &ka, oldset, regs);
+ handle_signal(signr, ka, &info, oldset, regs);
return 1;
}
- no_signal:
+
/* Did we come from a system call? */
- if (regs->orig_er0 >= 0) {
+ if (regs->orig_er0 >= 0)
/* Restart the system call - no handlers present */
- if (regs->er0 == -ERESTARTNOHAND ||
- regs->er0 == -ERESTARTSYS ||
- regs->er0 == -ERESTARTNOINTR) {
- regs->er0 = regs->orig_er0;
- regs->pc -= 2;
- }
- if (regs->er0 == -ERESTART_RESTARTBLOCK){
- regs->er0 = __NR_restart_syscall;
- regs->pc -= 2;
- }
- }
+ handle_restart(regs, NULL, 0);
+
return 0;
}