#include <linux/tracehook.h>
#include <asm/processor.h>
-#include <asm/syscall.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#define DEBUG_SIG 0
struct rt_sigframe {
+ struct siginfo *pinfo;
+ void *puc;
struct siginfo info;
struct ucontext uc;
unsigned char retcode[16]; /* trampoline code */
};
-static int restore_sigcontext(struct pt_regs *regs,
- struct sigcontext __user *sc)
+static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
{
- int err = 0;
+ unsigned int err = 0;
- /* Always make any pending restarted system calls return -EINTR */
+ /* Alwys make any pending restarted system call return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
/*
* (sc is already checked for VERIFY_READ since the sigframe was
* checked in sys_sigreturn previously)
*/
- err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long));
- err |= __copy_from_user(®s->pc, &sc->regs.pc, sizeof(unsigned long));
- err |= __copy_from_user(®s->sr, &sc->regs.sr, sizeof(unsigned long));
+ if (__copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long)))
+ goto badframe;
+ if (__copy_from_user(®s->pc, &sc->regs.pc, sizeof(unsigned long)))
+ goto badframe;
+ if (__copy_from_user(®s->sr, &sc->regs.sr, sizeof(unsigned long)))
+ goto badframe;
/* make sure the SM-bit is cleared so user-mode cannot fool us */
regs->sr &= ~SPR_SR_SM;
- regs->orig_gpr11 = -1; /* Avoid syscall restart checks */
-
/* TODO: the other ports use regs->orig_XX to disable syscall checks
* after this completes, but we don't use that mechanism. maybe we can
* use it now ?
*/
return err;
+
+badframe:
+ return 1;
}
asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
* Set up a signal frame.
*/
-static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+static int setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
+ unsigned long mask)
{
int err = 0;
/* copy the regs */
- /* There should be no need to save callee-saved registers here...
- * ...but we save them anyway. Revisit this
- */
+
err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.pc, ®s->pc, sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.sr, ®s->sr, sizeof(unsigned long));
+ /* then some other stuff */
+
+ err |= __put_user(mask, &sc->oldmask);
+
return err;
}
int err = 0;
frame = get_sigframe(ka, regs, sizeof(*frame));
+
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
- /* Create siginfo. */
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+
if (ka->sa.sa_flags & SA_SIGINFO)
err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ goto give_sigsegv;
- /* Create the ucontext. */
+ /* Clear all the bits of the ucontext we don't use. */
+ err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(NULL, &frame->uc.uc_link);
err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
- err |= setup_sigcontext(regs, &frame->uc.uc_mcontext);
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
/* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode;
- /* This is:
- l.ori r11,r0,__NR_sigreturn
- l.sys 1
- */
- err |= __put_user(0xa960, (short *)(frame->retcode + 0));
- err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2));
+ /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */
+ err |= __put_user(0xa960, (short *)(frame->retcode + 0));
+ err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2));
err |= __put_user(0x20000001, (unsigned long *)(frame->retcode + 4));
err |= __put_user(0x15000000, (unsigned long *)(frame->retcode + 8));
* mode below.
*/
-int do_signal(struct pt_regs *regs, int syscall)
+void do_signal(struct pt_regs *regs)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
- unsigned long continue_addr = 0;
- unsigned long restart_addr = 0;
- unsigned long retval = 0;
- int restart = 0;
-
- if (syscall) {
- continue_addr = regs->pc;
- restart_addr = continue_addr - 4;
- retval = regs->gpr[11];
-
- /*
- * Setup syscall restart here so that a debugger will
- * see the already changed PC.
- */
- switch (retval) {
+
+ /*
+ * 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 (!user_mode(regs))
+ return;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+
+ /* If we are coming out of a syscall then we need
+ * to check if the syscall was interrupted and wants to be
+ * restarted after handling the signal. If so, the original
+ * syscall number is put back into r11 and the PC rewound to
+ * point at the l.sys instruction that resulted in the
+ * original syscall. Syscall results other than the four
+ * below mean that the syscall executed to completion and no
+ * restart is necessary.
+ */
+ if (regs->orig_gpr11) {
+ int restart = 0;
+
+ switch (regs->gpr[11]) {
case -ERESTART_RESTARTBLOCK:
- restart = -2;
- /* Fall through */
case -ERESTARTNOHAND:
+ /* Restart if there is no signal handler */
+ restart = (signr <= 0);
+ break;
case -ERESTARTSYS:
+ /* Restart if there no signal handler or
+ * SA_RESTART flag is set */
+ restart = (signr <= 0 || (ka.sa.sa_flags & SA_RESTART));
+ break;
case -ERESTARTNOINTR:
- restart++;
- regs->gpr[11] = regs->orig_gpr11;
- regs->pc = restart_addr;
+ /* Always restart */
+ restart = 1;
break;
}
- }
- /*
- * Get the signal to deliver. When running under ptrace, at this
- * point the debugger may change all our registers ...
- */
- signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- /*
- * Depending on the signal settings we may need to revert the
- * decision to restart the system call. But skip this if a
- * debugger has chosen to restart at a different PC.
- */
- if (signr > 0) {
- if (unlikely(restart) && regs->pc == restart_addr) {
- if (retval == -ERESTARTNOHAND ||
- retval == -ERESTART_RESTARTBLOCK
- || (retval == -ERESTARTSYS
- && !(ka.sa.sa_flags & SA_RESTART))) {
- /* No automatic restart */
- regs->gpr[11] = -EINTR;
- regs->pc = continue_addr;
- }
+ if (restart) {
+ if (regs->gpr[11] == -ERESTART_RESTARTBLOCK)
+ regs->gpr[11] = __NR_restart_syscall;
+ else
+ regs->gpr[11] = regs->orig_gpr11;
+ regs->pc -= 4;
+ } else {
+ regs->gpr[11] = -EINTR;
}
+ }
- handle_signal(signr, &info, &ka, regs);
- } else {
- /* no handler */
+ if (signr <= 0) {
+ /* no signal to deliver so we just put the saved sigmask
+ * back */
restore_saved_sigmask();
- /*
- * Restore pt_regs PC as syscall restart will be handled by
- * kernel without return to userspace
- */
- if (unlikely(restart) && regs->pc == restart_addr) {
- regs->pc = continue_addr;
- return restart;
- }
+ } else { /* signr > 0 */
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, &info, &ka, regs);
}
- return 0;
+ return;
}
-asmlinkage int
-do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
+asmlinkage void do_notify_resume(struct pt_regs *regs)
{
- do {
- if (likely(thread_flags & _TIF_NEED_RESCHED)) {
- schedule();
- } else {
- if (unlikely(!user_mode(regs)))
- return 0;
- local_irq_enable();
- if (thread_flags & _TIF_SIGPENDING) {
- int restart = do_signal(regs, syscall);
- if (unlikely(restart)) {
- /*
- * Restart without handlers.
- * Deal with it without leaving
- * the kernel space.
- */
- return restart;
- }
- syscall = 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);
- return 0;
+ if (current_thread_info()->flags & _TIF_SIGPENDING)
+ do_signal(regs);
+
+ if (current_thread_info()->flags & _TIF_NOTIFY_RESUME) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
}