Commit 04a24ae4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-3.14' of git://openrisc.net/~jonas/linux

Pull OpenRISC updates from Jonas Bonn:
 "The interesting change here is a rework of the OpenRISC signal
  handling to make it more like other architectures in the hopes that
  this makes it easier for others to comment on and understand.  This
  rework fixes some real bugs, like the fact that syscall restart did
  not work reliably"

* tag 'for-3.14' of git://openrisc.net/~jonas/linux:
  openrisc: Use get_signal() signal_setup_done()
  openrisc: Rework signal handling
parents 4bcec913 548dafe8
...@@ -853,37 +853,44 @@ UNHANDLED_EXCEPTION(_vector_0x1f00,0x1f00) ...@@ -853,37 +853,44 @@ UNHANDLED_EXCEPTION(_vector_0x1f00,0x1f00)
/* ========================================================[ return ] === */ /* ========================================================[ return ] === */
_work_pending: _resume_userspace:
/* DISABLE_INTERRUPTS(r3,r4)
* if (current_thread_info->flags & _TIF_NEED_RESCHED) l.lwz r4,TI_FLAGS(r10)
* schedule(); l.andi r13,r4,_TIF_WORK_MASK
*/ l.sfeqi r13,0
l.lwz r5,TI_FLAGS(r10) l.bf _restore_all
l.andi r3,r5,_TIF_NEED_RESCHED
l.sfnei r3,0
l.bnf _work_notifysig
l.nop
l.jal schedule
l.nop
l.j _resume_userspace
l.nop l.nop
/* Handle pending signals and notify-resume requests. _work_pending:
* do_notify_resume must be passed the latest pushed pt_regs, not l.lwz r5,PT_ORIG_GPR11(r1)
* necessarily the "userspace" ones. Also, pt_regs->syscallno l.sfltsi r5,0
* must be set so that the syscall restart functionality works. l.bnf 1f
*/ l.nop
_work_notifysig: l.andi r5,r5,0
l.jal do_notify_resume 1:
l.jal do_work_pending
l.ori r3,r1,0 /* pt_regs */ l.ori r3,r1,0 /* pt_regs */
_resume_userspace: l.sfeqi r11,0
DISABLE_INTERRUPTS(r3,r4) l.bf _restore_all
l.lwz r3,TI_FLAGS(r10) l.nop
l.andi r3,r3,_TIF_WORK_MASK l.sfltsi r11,0
l.sfnei r3,0 l.bnf 1f
l.bf _work_pending l.nop
l.and r11,r11,r0
l.ori r11,r11,__NR_restart_syscall
l.j _syscall_check_trace_enter
l.nop l.nop
1:
l.lwz r11,PT_ORIG_GPR11(r1)
/* Restore arg registers */
l.lwz r3,PT_GPR3(r1)
l.lwz r4,PT_GPR4(r1)
l.lwz r5,PT_GPR5(r1)
l.lwz r6,PT_GPR6(r1)
l.lwz r7,PT_GPR7(r1)
l.j _syscall_check_trace_enter
l.lwz r8,PT_GPR8(r1)
_restore_all: _restore_all:
RESTORE_ALL RESTORE_ALL
......
...@@ -28,24 +28,24 @@ ...@@ -28,24 +28,24 @@
#include <linux/tracehook.h> #include <linux/tracehook.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/syscall.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#define DEBUG_SIG 0 #define DEBUG_SIG 0
struct rt_sigframe { struct rt_sigframe {
struct siginfo *pinfo;
void *puc;
struct siginfo info; struct siginfo info;
struct ucontext uc; struct ucontext uc;
unsigned char retcode[16]; /* trampoline code */ unsigned char retcode[16]; /* trampoline code */
}; };
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) static int restore_sigcontext(struct pt_regs *regs,
struct sigcontext __user *sc)
{ {
unsigned int err = 0; int err = 0;
/* Alwys make any pending restarted system call return -EINTR */ /* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall; current_thread_info()->restart_block.fn = do_no_restart_syscall;
/* /*
...@@ -53,25 +53,21 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) ...@@ -53,25 +53,21 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
* (sc is already checked for VERIFY_READ since the sigframe was * (sc is already checked for VERIFY_READ since the sigframe was
* checked in sys_sigreturn previously) * checked in sys_sigreturn previously)
*/ */
if (__copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long))) err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long));
goto badframe; err |= __copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long));
if (__copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long))) err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
goto badframe;
if (__copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long)))
goto badframe;
/* make sure the SM-bit is cleared so user-mode cannot fool us */ /* make sure the SM-bit is cleared so user-mode cannot fool us */
regs->sr &= ~SPR_SR_SM; 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 /* 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 * after this completes, but we don't use that mechanism. maybe we can
* use it now ? * use it now ?
*/ */
return err; return err;
badframe:
return 1;
} }
asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs) asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
...@@ -111,21 +107,18 @@ asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs) ...@@ -111,21 +107,18 @@ asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
* Set up a signal frame. * Set up a signal frame.
*/ */
static int setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
unsigned long mask)
{ {
int err = 0; int err = 0;
/* copy the regs */ /* 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.gpr, regs, 32 * sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long)); err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long));
err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long)); err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long));
/* then some other stuff */
err |= __put_user(mask, &sc->oldmask);
return err; return err;
} }
...@@ -173,55 +166,53 @@ static inline void __user *get_sigframe(struct k_sigaction *ka, ...@@ -173,55 +166,53 @@ static inline void __user *get_sigframe(struct k_sigaction *ka,
* trampoline which performs the syscall sigreturn, or a provided * trampoline which performs the syscall sigreturn, or a provided
* user-mode trampoline. * user-mode trampoline.
*/ */
static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
sigset_t *set, struct pt_regs *regs) struct pt_regs *regs)
{ {
struct rt_sigframe *frame; struct rt_sigframe *frame;
unsigned long return_ip; unsigned long return_ip;
int err = 0; int err = 0;
frame = get_sigframe(ka, regs, sizeof(*frame)); frame = get_sigframe(&ksig->ka, regs, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv; return -EFAULT;
err |= __put_user(&frame->info, &frame->pinfo);
err |= __put_user(&frame->uc, &frame->puc);
if (ka->sa.sa_flags & SA_SIGINFO) /* Create siginfo. */
err |= copy_siginfo_to_user(&frame->info, info); if (ksig->ka.sa.sa_flags & SA_SIGINFO)
if (err) err |= copy_siginfo_to_user(&frame->info, &ksig->info);
goto give_sigsegv;
/* Clear all the bits of the ucontext we don't use. */ /* Create the ucontext. */
err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(NULL, &frame->uc.uc_link); err |= __put_user(NULL, &frame->uc.uc_link);
err |= __save_altstack(&frame->uc.uc_stack, regs->sp); err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); err |= setup_sigcontext(regs, &frame->uc.uc_mcontext);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err) if (err)
goto give_sigsegv; return -EFAULT;
/* trampoline - the desired return ip is the retcode itself */ /* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode; return_ip = (unsigned long)&frame->retcode;
/* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ /* This is:
l.ori r11,r0,__NR_sigreturn
l.sys 1
*/
err |= __put_user(0xa960, (short *)(frame->retcode + 0)); err |= __put_user(0xa960, (short *)(frame->retcode + 0));
err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2)); err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2));
err |= __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); err |= __put_user(0x20000001, (unsigned long *)(frame->retcode + 4));
err |= __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); err |= __put_user(0x15000000, (unsigned long *)(frame->retcode + 8));
if (err) if (err)
goto give_sigsegv; return -EFAULT;
/* TODO what is the current->exec_domain stuff and invmap ? */ /* TODO what is the current->exec_domain stuff and invmap ? */
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->pc = (unsigned long)ka->sa.sa_handler; /* what we enter NOW */ regs->pc = (unsigned long)ksig->ka.sa.sa_handler; /* what we enter NOW */
regs->gpr[9] = (unsigned long)return_ip; /* what we enter LATER */ regs->gpr[9] = (unsigned long)return_ip; /* what we enter LATER */
regs->gpr[3] = (unsigned long)sig; /* arg 1: signo */ regs->gpr[3] = (unsigned long)ksig->sig; /* arg 1: signo */
regs->gpr[4] = (unsigned long)&frame->info; /* arg 2: (siginfo_t*) */ regs->gpr[4] = (unsigned long)&frame->info; /* arg 2: (siginfo_t*) */
regs->gpr[5] = (unsigned long)&frame->uc; /* arg 3: ucontext */ regs->gpr[5] = (unsigned long)&frame->uc; /* arg 3: ucontext */
...@@ -229,25 +220,16 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -229,25 +220,16 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->sp = (unsigned long)frame; regs->sp = (unsigned long)frame;
return 0; return 0;
give_sigsegv:
force_sigsegv(sig, current);
return -EFAULT;
} }
static inline void static inline void
handle_signal(unsigned long sig, handle_signal(struct ksignal *ksig, struct pt_regs *regs)
siginfo_t *info, struct k_sigaction *ka,
struct pt_regs *regs)
{ {
int ret; int ret;
ret = setup_rt_frame(sig, ka, info, sigmask_to_save(), regs); ret = setup_rt_frame(ksig, sigmask_to_save(), regs);
if (ret)
return;
signal_delivered(sig, info, ka, regs, signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP));
test_thread_flag(TIF_SINGLESTEP));
} }
/* /*
...@@ -262,82 +244,99 @@ handle_signal(unsigned long sig, ...@@ -262,82 +244,99 @@ handle_signal(unsigned long sig,
* mode below. * mode below.
*/ */
void do_signal(struct pt_regs *regs) int do_signal(struct pt_regs *regs, int syscall)
{ {
siginfo_t info; struct ksignal ksig;
int signr; unsigned long continue_addr = 0;
struct k_sigaction ka; 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];
/* /*
* We want the common case to go fast, which * Setup syscall restart here so that a debugger will
* is why we may in certain cases get here from * see the already changed PC.
* 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) { switch (retval) {
int restart = 0;
switch (regs->gpr[11]) {
case -ERESTART_RESTARTBLOCK: case -ERESTART_RESTARTBLOCK:
restart = -2;
/* Fall through */
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
/* Restart if there is no signal handler */
restart = (signr <= 0);
break;
case -ERESTARTSYS: 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: case -ERESTARTNOINTR:
/* Always restart */ restart++;
restart = 1; regs->gpr[11] = regs->orig_gpr11;
regs->pc = restart_addr;
break; break;
} }
}
if (restart) { /*
if (regs->gpr[11] == -ERESTART_RESTARTBLOCK) * Get the signal to deliver. During the call to get_signal the
regs->gpr[11] = __NR_restart_syscall; * debugger may change all our registers so we may need to revert
else * the decision to restart the syscall; specifically, if the PC is
regs->gpr[11] = regs->orig_gpr11; * changed, don't restart the syscall.
regs->pc -= 4; */
} else { if (get_signal(&ksig)) {
if (unlikely(restart) && regs->pc == restart_addr) {
if (retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK
|| (retval == -ERESTARTSYS
&& !(ksig.ka.sa.sa_flags & SA_RESTART))) {
/* No automatic restart */
regs->gpr[11] = -EINTR; regs->gpr[11] = -EINTR;
regs->pc = continue_addr;
} }
} }
handle_signal(&ksig, regs);
if (signr <= 0) { } else {
/* no signal to deliver so we just put the saved sigmask /* no handler */
* back */
restore_saved_sigmask(); restore_saved_sigmask();
} else { /* signr > 0 */ /*
/* Whee! Actually deliver the signal. */ * Restore pt_regs PC as syscall restart will be handled by
handle_signal(signr, &info, &ka, regs); * kernel without return to userspace
*/
if (unlikely(restart) && regs->pc == restart_addr) {
regs->pc = continue_addr;
return restart;
}
} }
return; return 0;
} }
asmlinkage void do_notify_resume(struct pt_regs *regs) asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{ {
if (current_thread_info()->flags & _TIF_SIGPENDING) do {
do_signal(regs); if (likely(thread_flags & _TIF_NEED_RESCHED)) {
schedule();
if (current_thread_info()->flags & _TIF_NOTIFY_RESUME) { } 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); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); tracehook_notify_resume(regs);
} }
}
local_irq_disable();
thread_flags = current_thread_info()->flags;
} while (thread_flags & _TIF_WORK_MASK);
return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment