2005-04-16 15:20:36 -07:00
/*
* linux / arch / sh / kernel / signal . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*
* 1997 - 11 - 28 Modified for POSIX .1 b signals by Richard Henderson
*
* SuperH version : Copyright ( C ) 1999 , 2000 Niibe Yutaka & Kaz Kojima
*
*/
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/kernel.h>
# include <linux/signal.h>
# include <linux/errno.h>
# include <linux/wait.h>
# include <linux/ptrace.h>
# include <linux/unistd.h>
# include <linux/stddef.h>
# include <linux/tty.h>
2006-09-27 18:33:49 +09:00
# include <linux/elf.h>
2005-04-16 15:20:36 -07:00
# include <linux/personality.h>
# include <linux/binfmts.h>
2006-12-06 20:34:23 -08:00
# include <linux/freezer.h>
2007-06-01 17:26:13 +09:00
# include <linux/io.h>
2008-07-30 19:55:30 +09:00
# include <linux/tracehook.h>
2007-05-08 14:50:59 +09:00
# include <asm/system.h>
2005-04-16 15:20:36 -07:00
# include <asm/ucontext.h>
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/cacheflush.h>
2008-09-04 18:53:58 +09:00
# include <asm/syscalls.h>
2008-03-26 19:02:47 +09:00
# include <asm/fpu.h>
2005-04-16 15:20:36 -07:00
# define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
2008-05-19 13:40:12 +09:00
struct fdpic_func_descriptor {
unsigned long text ;
unsigned long GOT ;
} ;
2005-04-16 15:20:36 -07:00
/*
* Atomically swap in the new signal mask , and wait for a signal .
*/
asmlinkage int
sys_sigsuspend ( old_sigset_t mask ,
unsigned long r5 , unsigned long r6 , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
mask & = _BLOCKABLE ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
2006-09-27 17:27:00 +09:00
current - > saved_sigmask = current - > blocked ;
2005-04-16 15:20:36 -07:00
siginitset ( & current - > blocked , mask ) ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2006-09-27 17:27:00 +09:00
current - > state = TASK_INTERRUPTIBLE ;
schedule ( ) ;
set_thread_flag ( TIF_RESTORE_SIGMASK ) ;
return - ERESTARTNOHAND ;
2005-04-16 15:20:36 -07:00
}
2006-11-21 11:16:57 +09:00
asmlinkage int
2005-04-16 15:20:36 -07:00
sys_sigaction ( int sig , const struct old_sigaction __user * act ,
struct old_sigaction __user * oact )
{
struct k_sigaction new_ka , old_ka ;
int ret ;
if ( 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_restorer , & act - > sa_restorer ) )
return - EFAULT ;
__get_user ( new_ka . sa . sa_flags , & act - > sa_flags ) ;
__get_user ( mask , & act - > sa_mask ) ;
siginitset ( & new_ka . sa . sa_mask , mask ) ;
}
ret = do_sigaction ( sig , act ? & new_ka : NULL , oact ? & old_ka : NULL ) ;
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_restorer , & oact - > sa_restorer ) )
return - EFAULT ;
__put_user ( old_ka . sa . sa_flags , & oact - > sa_flags ) ;
__put_user ( old_ka . sa . sa_mask . sig [ 0 ] , & oact - > sa_mask ) ;
}
return ret ;
}
asmlinkage int
sys_sigaltstack ( const stack_t __user * uss , stack_t __user * uoss ,
unsigned long r6 , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
2006-11-21 11:16:57 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
return do_sigaltstack ( uss , uoss , regs - > regs [ 15 ] ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Do a signal return ; undo the signal stack .
*/
# define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
2006-12-07 18:07:27 +09:00
# if defined(CONFIG_CPU_SH2)
2006-11-05 15:40:13 +09:00
# define TRAP_NOARG 0xc320 /* Syscall w/no args (NR in R3) */
# else
# define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) */
# endif
2005-04-16 15:20:36 -07:00
# define OR_R0_R0 0x200b /* or r0,r0 (insert to avoid hardware bug) */
struct sigframe
{
struct sigcontext sc ;
unsigned long extramask [ _NSIG_WORDS - 1 ] ;
u16 retcode [ 8 ] ;
} ;
struct rt_sigframe
{
struct siginfo info ;
struct ucontext uc ;
u16 retcode [ 8 ] ;
} ;
# ifdef CONFIG_SH_FPU
static inline int restore_sigcontext_fpu ( struct sigcontext __user * sc )
{
struct task_struct * tsk = current ;
2006-12-25 10:19:56 +09:00
if ( ! ( current_cpu_data . flags & CPU_HAS_FPU ) )
2005-04-16 15:20:36 -07:00
return 0 ;
set_used_math ( ) ;
return __copy_from_user ( & tsk - > thread . fpu . hard , & sc - > sc_fpregs [ 0 ] ,
sizeof ( long ) * ( 16 * 2 + 2 ) ) ;
}
static inline int save_sigcontext_fpu ( struct sigcontext __user * sc ,
struct pt_regs * regs )
{
struct task_struct * tsk = current ;
2006-12-25 10:19:56 +09:00
if ( ! ( current_cpu_data . flags & CPU_HAS_FPU ) )
2005-04-16 15:20:36 -07:00
return 0 ;
if ( ! used_math ( ) ) {
__put_user ( 0 , & sc - > sc_ownedfp ) ;
return 0 ;
}
__put_user ( 1 , & sc - > sc_ownedfp ) ;
/* This will cause a "finit" to be triggered by the next
attempted FPU operation by the ' current ' process .
*/
clear_used_math ( ) ;
unlazy_fpu ( tsk , regs ) ;
return __copy_to_user ( & sc - > sc_fpregs [ 0 ] , & tsk - > thread . fpu . hard ,
sizeof ( long ) * ( 16 * 2 + 2 ) ) ;
}
# endif /* CONFIG_SH_FPU */
static int
restore_sigcontext ( struct pt_regs * regs , struct sigcontext __user * sc , int * r0_p )
{
unsigned int err = 0 ;
# define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
COPY ( regs [ 1 ] ) ;
COPY ( regs [ 2 ] ) ; COPY ( regs [ 3 ] ) ;
COPY ( regs [ 4 ] ) ; COPY ( regs [ 5 ] ) ;
COPY ( regs [ 6 ] ) ; COPY ( regs [ 7 ] ) ;
COPY ( regs [ 8 ] ) ; COPY ( regs [ 9 ] ) ;
COPY ( regs [ 10 ] ) ; COPY ( regs [ 11 ] ) ;
COPY ( regs [ 12 ] ) ; COPY ( regs [ 13 ] ) ;
COPY ( regs [ 14 ] ) ; COPY ( regs [ 15 ] ) ;
COPY ( gbr ) ; COPY ( mach ) ;
COPY ( macl ) ; COPY ( pr ) ;
COPY ( sr ) ; COPY ( pc ) ;
# undef COPY
# ifdef CONFIG_SH_FPU
2006-12-25 10:19:56 +09:00
if ( current_cpu_data . flags & CPU_HAS_FPU ) {
2005-04-16 15:20:36 -07:00
int owned_fp ;
struct task_struct * tsk = current ;
regs - > sr | = SR_FD ; /* Release FPU */
clear_fpu ( tsk , regs ) ;
clear_used_math ( ) ;
__get_user ( owned_fp , & sc - > sc_ownedfp ) ;
if ( owned_fp )
err | = restore_sigcontext_fpu ( sc ) ;
}
# endif
regs - > tra = - 1 ; /* disable syscall checks */
err | = __get_user ( * r0_p , & sc - > sc_regs [ 0 ] ) ;
return err ;
}
asmlinkage int sys_sigreturn ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
2006-11-21 11:16:57 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
struct sigframe __user * frame = ( struct sigframe __user * ) regs - > regs [ 15 ] ;
2005-04-16 15:20:36 -07:00
sigset_t set ;
int r0 ;
2008-09-24 14:37:35 +09:00
/* Always make any pending restarted system calls return -EINTR */
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
2005-04-16 15:20:36 -07:00
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __get_user ( set . sig [ 0 ] , & frame - > sc . oldmask )
| | ( _NSIG_WORDS > 1
& & __copy_from_user ( & set . sig [ 1 ] , & frame - > extramask ,
sizeof ( frame - > extramask ) ) ) )
goto badframe ;
sigdelsetmask ( & set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2006-11-21 11:16:57 +09:00
if ( restore_sigcontext ( regs , & frame - > sc , & r0 ) )
2005-04-16 15:20:36 -07:00
goto badframe ;
return r0 ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
asmlinkage int sys_rt_sigreturn ( unsigned long r4 , unsigned long r5 ,
unsigned long r6 , unsigned long r7 ,
2006-11-21 11:16:57 +09:00
struct pt_regs __regs )
2005-04-16 15:20:36 -07:00
{
2006-11-21 11:16:57 +09:00
struct pt_regs * regs = RELOC_HIDE ( & __regs , 0 ) ;
struct rt_sigframe __user * frame = ( struct rt_sigframe __user * ) regs - > regs [ 15 ] ;
2005-04-16 15:20:36 -07:00
sigset_t set ;
int r0 ;
2008-09-24 14:37:35 +09:00
/* Always make any pending restarted system calls return -EINTR */
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
2005-04-16 15:20:36 -07:00
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set , & frame - > uc . uc_sigmask , sizeof ( set ) ) )
goto badframe ;
sigdelsetmask ( & set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2006-11-21 11:16:57 +09:00
if ( restore_sigcontext ( regs , & frame - > uc . uc_mcontext , & r0 ) )
2005-04-16 15:20:36 -07:00
goto badframe ;
2008-09-04 18:53:58 +09:00
if ( do_sigaltstack ( & frame - > uc . uc_stack , NULL ,
regs - > regs [ 15 ] ) = = - EFAULT )
2005-04-16 15:20:36 -07:00
goto badframe ;
return r0 ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
2007-06-19 12:33:21 +09:00
}
2005-04-16 15:20:36 -07:00
/*
* Set up a signal frame .
*/
static int
setup_sigcontext ( struct sigcontext __user * sc , struct pt_regs * regs ,
unsigned long mask )
{
int err = 0 ;
# define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
COPY ( regs [ 0 ] ) ; COPY ( regs [ 1 ] ) ;
COPY ( regs [ 2 ] ) ; COPY ( regs [ 3 ] ) ;
COPY ( regs [ 4 ] ) ; COPY ( regs [ 5 ] ) ;
COPY ( regs [ 6 ] ) ; COPY ( regs [ 7 ] ) ;
COPY ( regs [ 8 ] ) ; COPY ( regs [ 9 ] ) ;
COPY ( regs [ 10 ] ) ; COPY ( regs [ 11 ] ) ;
COPY ( regs [ 12 ] ) ; COPY ( regs [ 13 ] ) ;
COPY ( regs [ 14 ] ) ; COPY ( regs [ 15 ] ) ;
COPY ( gbr ) ; COPY ( mach ) ;
COPY ( macl ) ; COPY ( pr ) ;
COPY ( sr ) ; COPY ( pc ) ;
# undef COPY
# ifdef CONFIG_SH_FPU
err | = save_sigcontext_fpu ( sc , regs ) ;
# endif
/* non-iBCS2 extensions.. */
err | = __put_user ( mask , & sc - > oldmask ) ;
return err ;
}
/*
* Determine which stack to use . .
*/
static inline void __user *
get_sigframe ( struct k_sigaction * ka , unsigned long sp , size_t frame_size )
{
if ( ka - > sa . sa_flags & SA_ONSTACK ) {
if ( sas_ss_flags ( sp ) = = 0 )
sp = current - > sas_ss_sp + current - > sas_ss_size ;
}
return ( void __user * ) ( ( sp - frame_size ) & - 8ul ) ;
}
2006-09-27 18:33:49 +09:00
/* These symbols are defined with the addresses in the vsyscall page.
See vsyscall - trapa . S . */
extern void __user __kernel_sigreturn ;
extern void __user __kernel_rt_sigreturn ;
2006-09-27 17:27:00 +09:00
static int setup_frame ( int sig , struct k_sigaction * ka ,
2005-04-16 15:20:36 -07:00
sigset_t * set , struct pt_regs * regs )
{
struct sigframe __user * frame ;
int err = 0 ;
int signal ;
frame = get_sigframe ( ka , regs - > regs [ 15 ] , sizeof ( * frame ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
goto give_sigsegv ;
signal = current_thread_info ( ) - > exec_domain
& & current_thread_info ( ) - > exec_domain - > signal_invmap
& & sig < 32
? current_thread_info ( ) - > exec_domain - > signal_invmap [ sig ]
: sig ;
err | = setup_sigcontext ( & frame - > sc , regs , set - > sig [ 0 ] ) ;
2006-09-27 17:27:00 +09:00
if ( _NSIG_WORDS > 1 )
2005-04-16 15:20:36 -07:00
err | = __copy_to_user ( frame - > extramask , & set - > sig [ 1 ] ,
sizeof ( frame - > extramask ) ) ;
/* Set up to return from userspace. If provided, use a stub
already in userspace . */
if ( ka - > sa . sa_flags & SA_RESTORER ) {
regs - > pr = ( unsigned long ) ka - > sa . sa_restorer ;
2006-09-27 18:33:49 +09:00
# ifdef CONFIG_VSYSCALL
} else if ( likely ( current - > mm - > context . vdso ) ) {
regs - > pr = VDSO_SYM ( & __kernel_sigreturn ) ;
# endif
2005-04-16 15:20:36 -07:00
} else {
/* Generate return code (system call to sigreturn) */
err | = __put_user ( MOVW ( 7 ) , & frame - > retcode [ 0 ] ) ;
2006-11-05 15:40:13 +09:00
err | = __put_user ( TRAP_NOARG , & frame - > retcode [ 1 ] ) ;
2005-04-16 15:20:36 -07:00
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 2 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 3 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 4 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 5 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 6 ] ) ;
err | = __put_user ( ( __NR_sigreturn ) , & frame - > retcode [ 7 ] ) ;
regs - > pr = ( unsigned long ) frame - > retcode ;
2008-07-02 17:51:23 +09:00
flush_icache_range ( regs - > pr , regs - > pr + sizeof ( frame - > retcode ) ) ;
2005-04-16 15:20:36 -07:00
}
if ( err )
goto give_sigsegv ;
/* Set up registers for signal handler */
regs - > regs [ 15 ] = ( unsigned long ) frame ;
regs - > regs [ 4 ] = signal ; /* Arg for signal handler */
regs - > regs [ 5 ] = 0 ;
regs - > regs [ 6 ] = ( unsigned long ) & frame - > sc ;
2008-05-19 13:40:12 +09:00
if ( current - > personality & FDPIC_FUNCPTRS ) {
struct fdpic_func_descriptor __user * funcptr =
( struct fdpic_func_descriptor __user * ) ka - > sa . sa_handler ;
__get_user ( regs - > pc , & funcptr - > text ) ;
__get_user ( regs - > regs [ 12 ] , & funcptr - > GOT ) ;
} else
regs - > pc = ( unsigned long ) ka - > sa . sa_handler ;
2005-04-16 15:20:36 -07:00
set_fs ( USER_DS ) ;
2006-09-27 17:27:00 +09:00
pr_debug ( " SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx \n " ,
2007-10-18 23:40:41 -07:00
current - > comm , task_pid_nr ( current ) , frame , regs - > pc , regs - > pr ) ;
2005-04-16 15:20:36 -07:00
2006-09-27 17:27:00 +09:00
return 0 ;
2005-04-16 15:20:36 -07:00
give_sigsegv :
force_sigsegv ( sig , current ) ;
2006-09-27 17:27:00 +09:00
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}
2006-09-27 17:27:00 +09:00
static int setup_rt_frame ( int sig , struct k_sigaction * ka , siginfo_t * info ,
2005-04-16 15:20:36 -07:00
sigset_t * set , struct pt_regs * regs )
{
struct rt_sigframe __user * frame ;
int err = 0 ;
int signal ;
frame = get_sigframe ( ka , regs - > regs [ 15 ] , sizeof ( * frame ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
goto give_sigsegv ;
signal = current_thread_info ( ) - > exec_domain
& & current_thread_info ( ) - > exec_domain - > signal_invmap
& & sig < 32
? current_thread_info ( ) - > exec_domain - > signal_invmap [ sig ]
: sig ;
err | = copy_siginfo_to_user ( & frame - > info , info ) ;
/* Create the ucontext. */
err | = __put_user ( 0 , & frame - > uc . uc_flags ) ;
2008-09-04 18:53:58 +09:00
err | = __put_user ( NULL , & frame - > uc . uc_link ) ;
2005-04-16 15:20:36 -07:00
err | = __put_user ( ( void * ) current - > sas_ss_sp ,
& frame - > uc . uc_stack . ss_sp ) ;
err | = __put_user ( sas_ss_flags ( regs - > regs [ 15 ] ) ,
& 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 | = __copy_to_user ( & frame - > uc . uc_sigmask , set , sizeof ( * set ) ) ;
/* Set up to return from userspace. If provided, use a stub
already in userspace . */
if ( ka - > sa . sa_flags & SA_RESTORER ) {
regs - > pr = ( unsigned long ) ka - > sa . sa_restorer ;
2006-09-27 18:33:49 +09:00
# ifdef CONFIG_VSYSCALL
} else if ( likely ( current - > mm - > context . vdso ) ) {
regs - > pr = VDSO_SYM ( & __kernel_rt_sigreturn ) ;
# endif
2005-04-16 15:20:36 -07:00
} else {
/* Generate return code (system call to rt_sigreturn) */
err | = __put_user ( MOVW ( 7 ) , & frame - > retcode [ 0 ] ) ;
2006-11-05 15:40:13 +09:00
err | = __put_user ( TRAP_NOARG , & frame - > retcode [ 1 ] ) ;
2005-04-16 15:20:36 -07:00
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 2 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 3 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 4 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 5 ] ) ;
err | = __put_user ( OR_R0_R0 , & frame - > retcode [ 6 ] ) ;
err | = __put_user ( ( __NR_rt_sigreturn ) , & frame - > retcode [ 7 ] ) ;
regs - > pr = ( unsigned long ) frame - > retcode ;
}
if ( err )
goto give_sigsegv ;
/* Set up registers for signal handler */
regs - > regs [ 15 ] = ( unsigned long ) frame ;
regs - > regs [ 4 ] = signal ; /* Arg for signal handler */
regs - > regs [ 5 ] = ( unsigned long ) & frame - > info ;
regs - > regs [ 6 ] = ( unsigned long ) & frame - > uc ;
2008-05-19 13:40:12 +09:00
if ( current - > personality & FDPIC_FUNCPTRS ) {
struct fdpic_func_descriptor __user * funcptr =
( struct fdpic_func_descriptor __user * ) ka - > sa . sa_handler ;
__get_user ( regs - > pc , & funcptr - > text ) ;
__get_user ( regs - > regs [ 12 ] , & funcptr - > GOT ) ;
} else
regs - > pc = ( unsigned long ) ka - > sa . sa_handler ;
2005-04-16 15:20:36 -07:00
set_fs ( USER_DS ) ;
2006-09-27 17:27:00 +09:00
pr_debug ( " SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx \n " ,
2007-10-18 23:40:41 -07:00
current - > comm , task_pid_nr ( current ) , frame , regs - > pc , regs - > pr ) ;
2005-04-16 15:20:36 -07:00
2008-07-02 15:17:11 +09:00
flush_icache_range ( regs - > pr , regs - > pr + sizeof ( frame - > retcode ) ) ;
2006-09-27 17:27:00 +09:00
return 0 ;
2005-04-16 15:20:36 -07:00
give_sigsegv :
force_sigsegv ( sig , current ) ;
2006-09-27 17:27:00 +09:00
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}
2008-09-12 22:08:20 +09:00
static inline void
handle_syscall_restart ( unsigned long save_r0 , struct pt_regs * regs ,
struct sigaction * sa )
{
/* If we're not from a syscall, bail out */
if ( regs - > tra < 0 )
return ;
/* check for system call restart.. */
switch ( regs - > regs [ 0 ] ) {
case - ERESTART_RESTARTBLOCK :
case - ERESTARTNOHAND :
no_system_call_restart :
regs - > regs [ 0 ] = - EINTR ;
regs - > sr | = 1 ;
break ;
case - ERESTARTSYS :
if ( ! ( sa - > sa_flags & SA_RESTART ) )
goto no_system_call_restart ;
/* fallthrough */
case - ERESTARTNOINTR :
regs - > regs [ 0 ] = save_r0 ;
regs - > pc - = instruction_size ( ctrl_inw ( regs - > pc - 4 ) ) ;
break ;
}
}
2005-04-16 15:20:36 -07:00
/*
* OK , we ' re invoking a handler
*/
2006-09-27 17:27:00 +09:00
static int
2005-04-16 15:20:36 -07:00
handle_signal ( unsigned long sig , struct k_sigaction * ka , siginfo_t * info ,
2007-06-18 10:08:20 +09:00
sigset_t * oldset , struct pt_regs * regs , unsigned int save_r0 )
2005-04-16 15:20:36 -07:00
{
2006-09-27 17:27:00 +09:00
int ret ;
2005-04-16 15:20:36 -07:00
/* Set up the stack frame */
if ( ka - > sa . sa_flags & SA_SIGINFO )
2006-09-27 17:27:00 +09:00
ret = setup_rt_frame ( sig , ka , info , oldset , regs ) ;
2005-04-16 15:20:36 -07:00
else
2006-09-27 17:27:00 +09:00
ret = setup_frame ( sig , ka , oldset , regs ) ;
2005-04-16 15:20:36 -07:00
if ( ka - > sa . sa_flags & SA_ONESHOT )
ka - > sa . sa_handler = SIG_DFL ;
2006-09-27 17:27:00 +09:00
if ( ret = = 0 ) {
spin_lock_irq ( & current - > sighand - > siglock ) ;
sigorsets ( & current - > blocked , & current - > blocked , & ka - > sa . sa_mask ) ;
if ( ! ( ka - > sa . sa_flags & SA_NODEFER ) )
sigaddset ( & current - > blocked , sig ) ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
}
return ret ;
2005-04-16 15:20:36 -07:00
}
/*
* 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 .
*/
2006-09-27 17:27:00 +09:00
static void do_signal ( struct pt_regs * regs , unsigned int save_r0 )
2005-04-16 15:20:36 -07:00
{
siginfo_t info ;
int signr ;
struct k_sigaction ka ;
2006-09-27 17:27:00 +09:00
sigset_t * oldset ;
2005-04-16 15:20:36 -07:00
/*
* 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 ) )
2006-09-27 17:27:00 +09:00
return ;
2005-04-16 15:20:36 -07:00
2005-07-27 11:43:34 -07:00
if ( try_to_freeze ( ) )
2005-04-16 15:20:36 -07:00
goto no_signal ;
2006-09-27 17:27:00 +09:00
if ( test_thread_flag ( TIF_RESTORE_SIGMASK ) )
oldset = & current - > saved_sigmask ;
else
2005-04-16 15:20:36 -07:00
oldset = & current - > blocked ;
signr = get_signal_to_deliver ( & info , & ka , regs , NULL ) ;
if ( signr > 0 ) {
2008-09-12 22:08:20 +09:00
if ( regs - > sr & 1 )
handle_syscall_restart ( save_r0 , regs , & ka . sa ) ;
2005-04-16 15:20:36 -07:00
/* Whee! Actually deliver the signal. */
2007-06-19 12:33:21 +09:00
if ( handle_signal ( signr , & ka , & info , oldset ,
regs , save_r0 ) = = 0 ) {
2006-09-27 17:27:00 +09:00
/* 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 ) ;
2008-07-30 19:55:30 +09:00
tracehook_signal_handler ( signr , & info , & ka , regs ,
test_thread_flag ( TIF_SINGLESTEP ) ) ;
2006-09-27 17:27:00 +09:00
}
2007-02-23 13:22:56 +09:00
return ;
2005-04-16 15:20:36 -07:00
}
2008-07-30 19:55:30 +09:00
no_signal :
2005-04-16 15:20:36 -07:00
/* Did we come from a system call? */
if ( regs - > tra > = 0 ) {
/* Restart the system call - no handlers present */
if ( regs - > regs [ 0 ] = = - ERESTARTNOHAND | |
regs - > regs [ 0 ] = = - ERESTARTSYS | |
2006-09-27 17:22:49 +09:00
regs - > regs [ 0 ] = = - ERESTARTNOINTR ) {
2007-02-23 13:22:56 +09:00
regs - > regs [ 0 ] = save_r0 ;
2007-05-08 15:31:48 +09:00
regs - > pc - = instruction_size ( ctrl_inw ( regs - > pc - 4 ) ) ;
2006-09-27 17:22:49 +09:00
} else if ( regs - > regs [ 0 ] = = - ERESTART_RESTARTBLOCK ) {
2007-05-08 15:31:48 +09:00
regs - > pc - = instruction_size ( ctrl_inw ( regs - > pc - 4 ) ) ;
2006-09-27 17:22:49 +09:00
regs - > regs [ 3 ] = __NR_restart_syscall ;
2005-04-16 15:20:36 -07:00
}
}
2006-09-27 17:27:00 +09:00
/* 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 , & current - > saved_sigmask , NULL ) ;
}
}
asmlinkage void do_notify_resume ( struct pt_regs * regs , unsigned int save_r0 ,
2008-07-30 19:55:30 +09:00
unsigned long thread_info_flags )
2006-09-27 17:27:00 +09:00
{
/* deal with pending signal delivery */
2008-07-30 19:55:30 +09:00
if ( thread_info_flags & _TIF_SIGPENDING )
2006-09-27 17:27:00 +09:00
do_signal ( regs , save_r0 ) ;
2008-07-30 19:55:30 +09:00
if ( thread_info_flags & _TIF_NOTIFY_RESUME ) {
clear_thread_flag ( TIF_NOTIFY_RESUME ) ;
tracehook_notify_resume ( regs ) ;
}
2005-04-16 15:20:36 -07:00
}