2005-04-17 02:20:36 +04:00
/*
* arch / s390 / kernel / signal . c
*
2006-02-01 14:06:38 +03:00
* Copyright ( C ) IBM Corp . 1999 , 2006
2005-04-17 02:20:36 +04:00
* Author ( s ) : Denis Joseph Barrow ( djbarrow @ de . ibm . com , barrow_dj @ yahoo . com )
*
* Based on Intel version
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*
* 1997 - 11 - 28 Modified for POSIX .1 b signals by Richard Henderson
*/
# 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>
# include <linux/personality.h>
# include <linux/binfmts.h>
# include <asm/ucontext.h>
# include <asm/uaccess.h>
# include <asm/lowcore.h>
2008-04-17 09:46:26 +04:00
# include "entry.h"
2005-04-17 02:20:36 +04:00
# define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
typedef struct
{
__u8 callee_used_stack [ __SIGNAL_FRAMESIZE ] ;
struct sigcontext sc ;
_sigregs sregs ;
int signo ;
__u8 retcode [ S390_SYSCALL_SIZE ] ;
} sigframe ;
typedef struct
{
__u8 callee_used_stack [ __SIGNAL_FRAMESIZE ] ;
__u8 retcode [ S390_SYSCALL_SIZE ] ;
struct siginfo info ;
struct ucontext uc ;
} rt_sigframe ;
/*
* Atomically swap in the new signal mask , and wait for a signal .
*/
asmlinkage int
2006-02-01 14:06:38 +03:00
sys_sigsuspend ( int history0 , int history1 , old_sigset_t mask )
2005-04-17 02:20:36 +04:00
{
mask & = _BLOCKABLE ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
2006-02-01 14:06:38 +03:00
current - > saved_sigmask = current - > blocked ;
2005-04-17 02:20:36 +04:00
siginitset ( & current - > blocked , mask ) ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2006-02-01 14:06:38 +03:00
current - > state = TASK_INTERRUPTIBLE ;
schedule ( ) ;
set_thread_flag ( TIF_RESTORE_SIGMASK ) ;
2005-04-17 02:20:36 +04:00
2006-02-01 14:06:38 +03:00
return - ERESTARTNOHAND ;
2005-04-17 02:20:36 +04:00
}
asmlinkage long
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 ) | |
2006-10-27 14:39:22 +04:00
__get_user ( new_ka . sa . sa_restorer , & act - > sa_restorer ) | |
__get_user ( new_ka . sa . sa_flags , & act - > sa_flags ) | |
__get_user ( mask , & act - > sa_mask ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
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 ) | |
2006-10-27 14:39:22 +04:00
__put_user ( old_ka . sa . sa_restorer , & oact - > sa_restorer ) | |
__put_user ( old_ka . sa . sa_flags , & oact - > sa_flags ) | |
__put_user ( old_ka . sa . sa_mask . sig [ 0 ] , & oact - > sa_mask ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
return ret ;
}
asmlinkage long
2007-04-27 18:01:40 +04:00
sys_sigaltstack ( const stack_t __user * uss , stack_t __user * uoss )
2005-04-17 02:20:36 +04:00
{
2007-04-27 18:01:40 +04:00
struct pt_regs * regs = task_pt_regs ( current ) ;
2005-04-17 02:20:36 +04:00
return do_sigaltstack ( uss , uoss , regs - > gprs [ 15 ] ) ;
}
/* Returns non-zero on fault. */
static int save_sigregs ( struct pt_regs * regs , _sigregs __user * sregs )
{
2006-09-20 17:59:39 +04:00
_sigregs user_sregs ;
2005-04-17 02:20:36 +04:00
save_access_regs ( current - > thread . acrs ) ;
/* Copy a 'clean' PSW mask to the user to avoid leaking
information about whether PER is currently on . */
2007-02-05 23:18:17 +03:00
user_sregs . regs . psw . mask = PSW_MASK_MERGE ( psw_user_bits , regs - > psw . mask ) ;
2006-10-04 22:01:58 +04:00
user_sregs . regs . psw . addr = regs - > psw . addr ;
memcpy ( & user_sregs . regs . gprs , & regs - > gprs , sizeof ( sregs - > regs . gprs ) ) ;
2006-09-20 17:59:39 +04:00
memcpy ( & user_sregs . regs . acrs , current - > thread . acrs ,
sizeof ( sregs - > regs . acrs ) ) ;
2005-04-17 02:20:36 +04:00
/*
* We have to store the fp registers to current - > thread . fp_regs
* to merge them with the emulated registers .
*/
save_fp_regs ( & current - > thread . fp_regs ) ;
2006-09-20 17:59:39 +04:00
memcpy ( & user_sregs . fpregs , & current - > thread . fp_regs ,
sizeof ( s390_fp_regs ) ) ;
return __copy_to_user ( sregs , & user_sregs , sizeof ( _sigregs ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Returns positive number on error */
static int restore_sigregs ( struct pt_regs * regs , _sigregs __user * sregs )
{
int err ;
2006-09-20 17:59:39 +04:00
_sigregs user_sregs ;
2005-04-17 02:20:36 +04:00
/* Alwys make any pending restarted system call return -EINTR */
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
2006-09-20 17:59:39 +04:00
err = __copy_from_user ( & user_sregs , sregs , sizeof ( _sigregs ) ) ;
2005-04-17 02:20:36 +04:00
if ( err )
return err ;
2006-10-04 22:01:58 +04:00
regs - > psw . mask = PSW_MASK_MERGE ( regs - > psw . mask ,
user_sregs . regs . psw . mask ) ;
regs - > psw . addr = PSW_ADDR_AMODE | user_sregs . regs . psw . addr ;
memcpy ( & regs - > gprs , & user_sregs . regs . gprs , sizeof ( sregs - > regs . gprs ) ) ;
2006-09-20 17:59:39 +04:00
memcpy ( & current - > thread . acrs , & user_sregs . regs . acrs ,
sizeof ( sregs - > regs . acrs ) ) ;
2005-04-17 02:20:36 +04:00
restore_access_regs ( current - > thread . acrs ) ;
2006-09-20 17:59:39 +04:00
memcpy ( & current - > thread . fp_regs , & user_sregs . fpregs ,
sizeof ( s390_fp_regs ) ) ;
2005-04-17 02:20:36 +04:00
current - > thread . fp_regs . fpc & = FPC_VALID_MASK ;
restore_fp_regs ( & current - > thread . fp_regs ) ;
regs - > trap = - 1 ; /* disable syscall checks */
return 0 ;
}
2007-04-27 18:01:40 +04:00
asmlinkage long sys_sigreturn ( void )
2005-04-17 02:20:36 +04:00
{
2007-04-27 18:01:40 +04:00
struct pt_regs * regs = task_pt_regs ( current ) ;
2005-04-17 02:20:36 +04:00
sigframe __user * frame = ( sigframe __user * ) regs - > gprs [ 15 ] ;
sigset_t set ;
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set . sig , & frame - > sc . oldmask , _SIGMASK_COPY_SIZE ) )
goto badframe ;
sigdelsetmask ( & set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
if ( restore_sigregs ( regs , & frame - > sregs ) )
goto badframe ;
return regs - > gprs [ 2 ] ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
2007-04-27 18:01:40 +04:00
asmlinkage long sys_rt_sigreturn ( void )
2005-04-17 02:20:36 +04:00
{
2007-04-27 18:01:40 +04:00
struct pt_regs * regs = task_pt_regs ( current ) ;
2005-04-17 02:20:36 +04:00
rt_sigframe __user * frame = ( rt_sigframe __user * ) regs - > gprs [ 15 ] ;
sigset_t set ;
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set . sig , & 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 ) ;
if ( restore_sigregs ( regs , & frame - > uc . uc_mcontext ) )
goto badframe ;
2006-01-06 11:19:10 +03:00
if ( do_sigaltstack ( & frame - > uc . uc_stack , NULL ,
regs - > gprs [ 15 ] ) = = - EFAULT )
goto badframe ;
2005-04-17 02:20:36 +04:00
return regs - > gprs [ 2 ] ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
/*
* Set up a signal frame .
*/
/*
* Determine which stack to use . .
*/
static inline void __user *
get_sigframe ( struct k_sigaction * ka , struct pt_regs * regs , size_t frame_size )
{
unsigned long sp ;
/* Default to using normal stack */
sp = regs - > gprs [ 15 ] ;
2008-04-17 09:45:57 +04:00
/* Overflow on alternate signal stack gives SIGSEGV. */
if ( on_sig_stack ( sp ) & & ! on_sig_stack ( ( sp - frame_size ) & - 8UL ) )
return ( void __user * ) - 1UL ;
2005-04-17 02:20:36 +04:00
/* This is the X/Open sanctioned signal stack switching. */
if ( ka - > sa . sa_flags & SA_ONSTACK ) {
if ( ! sas_ss_flags ( sp ) )
sp = current - > sas_ss_sp + current - > sas_ss_size ;
}
/* This is the legacy signal stack switching. */
else if ( ! user_mode ( regs ) & &
! ( ka - > sa . sa_flags & SA_RESTORER ) & &
ka - > sa . sa_restorer ) {
sp = ( unsigned long ) ka - > sa . sa_restorer ;
}
return ( void __user * ) ( ( sp - frame_size ) & - 8ul ) ;
}
static inline int map_signal ( int sig )
{
if ( current_thread_info ( ) - > exec_domain
& & current_thread_info ( ) - > exec_domain - > signal_invmap
& & sig < 32 )
return current_thread_info ( ) - > exec_domain - > signal_invmap [ sig ] ;
else
return sig ;
}
2006-02-01 14:06:38 +03:00
static int setup_frame ( int sig , struct k_sigaction * ka ,
sigset_t * set , struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
sigframe __user * frame ;
frame = get_sigframe ( ka , regs , sizeof ( sigframe ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( sigframe ) ) )
goto give_sigsegv ;
2008-04-17 09:45:57 +04:00
if ( frame = = ( void __user * ) - 1UL )
goto give_sigsegv ;
2005-04-17 02:20:36 +04:00
if ( __copy_to_user ( & frame - > sc . oldmask , & set - > sig , _SIGMASK_COPY_SIZE ) )
goto give_sigsegv ;
if ( save_sigregs ( regs , & frame - > sregs ) )
goto give_sigsegv ;
if ( __put_user ( & frame - > sregs , & frame - > sc . sregs ) )
goto give_sigsegv ;
/* Set up to return from userspace. If provided, use a stub
already in userspace . */
if ( ka - > sa . sa_flags & SA_RESTORER ) {
regs - > gprs [ 14 ] = ( unsigned long )
ka - > sa . sa_restorer | PSW_ADDR_AMODE ;
} else {
regs - > gprs [ 14 ] = ( unsigned long )
frame - > retcode | PSW_ADDR_AMODE ;
if ( __put_user ( S390_SYSCALL_OPCODE | __NR_sigreturn ,
( u16 __user * ) ( frame - > retcode ) ) )
goto give_sigsegv ;
}
/* Set up backchain. */
if ( __put_user ( regs - > gprs [ 15 ] , ( addr_t __user * ) frame ) )
goto give_sigsegv ;
/* Set up registers for signal handler */
regs - > gprs [ 15 ] = ( unsigned long ) frame ;
regs - > psw . addr = ( unsigned long ) ka - > sa . sa_handler | PSW_ADDR_AMODE ;
regs - > gprs [ 2 ] = map_signal ( sig ) ;
regs - > gprs [ 3 ] = ( unsigned long ) & frame - > sc ;
/* We forgot to include these in the sigcontext.
To avoid breaking binary compatibility , they are passed as args . */
regs - > gprs [ 4 ] = current - > thread . trap_no ;
regs - > gprs [ 5 ] = current - > thread . prot_addr ;
/* Place signal number on stack to allow backtrace from handler. */
if ( __put_user ( regs - > gprs [ 2 ] , ( int __user * ) & frame - > signo ) )
goto give_sigsegv ;
2006-02-01 14:06:38 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
give_sigsegv :
force_sigsegv ( sig , current ) ;
2006-02-01 14:06:38 +03:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
2006-02-01 14:06:38 +03:00
static int setup_rt_frame ( int sig , struct k_sigaction * ka , siginfo_t * info ,
2005-04-17 02:20:36 +04:00
sigset_t * set , struct pt_regs * regs )
{
int err = 0 ;
rt_sigframe __user * frame ;
frame = get_sigframe ( ka , regs , sizeof ( rt_sigframe ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( rt_sigframe ) ) )
goto give_sigsegv ;
2008-04-17 09:45:57 +04:00
if ( frame = = ( void __user * ) - 1UL )
goto give_sigsegv ;
2005-04-17 02:20:36 +04:00
if ( copy_siginfo_to_user ( & frame - > info , info ) )
goto give_sigsegv ;
/* Create the ucontext. */
err | = __put_user ( 0 , & frame - > uc . uc_flags ) ;
2005-09-29 03:16:02 +04:00
err | = __put_user ( NULL , & frame - > uc . uc_link ) ;
err | = __put_user ( ( void __user * ) current - > sas_ss_sp , & frame - > uc . uc_stack . ss_sp ) ;
2005-04-17 02:20:36 +04:00
err | = __put_user ( sas_ss_flags ( regs - > gprs [ 15 ] ) ,
& frame - > uc . uc_stack . ss_flags ) ;
err | = __put_user ( current - > sas_ss_size , & frame - > uc . uc_stack . ss_size ) ;
err | = save_sigregs ( regs , & frame - > uc . uc_mcontext ) ;
err | = __copy_to_user ( & frame - > uc . uc_sigmask , set , sizeof ( * set ) ) ;
if ( err )
goto give_sigsegv ;
/* Set up to return from userspace. If provided, use a stub
already in userspace . */
if ( ka - > sa . sa_flags & SA_RESTORER ) {
regs - > gprs [ 14 ] = ( unsigned long )
ka - > sa . sa_restorer | PSW_ADDR_AMODE ;
} else {
regs - > gprs [ 14 ] = ( unsigned long )
frame - > retcode | PSW_ADDR_AMODE ;
2006-05-01 23:16:15 +04:00
if ( __put_user ( S390_SYSCALL_OPCODE | __NR_rt_sigreturn ,
( u16 __user * ) ( frame - > retcode ) ) )
goto give_sigsegv ;
2005-04-17 02:20:36 +04:00
}
/* Set up backchain. */
if ( __put_user ( regs - > gprs [ 15 ] , ( addr_t __user * ) frame ) )
goto give_sigsegv ;
/* Set up registers for signal handler */
regs - > gprs [ 15 ] = ( unsigned long ) frame ;
regs - > psw . addr = ( unsigned long ) ka - > sa . sa_handler | PSW_ADDR_AMODE ;
regs - > gprs [ 2 ] = map_signal ( sig ) ;
regs - > gprs [ 3 ] = ( unsigned long ) & frame - > info ;
regs - > gprs [ 4 ] = ( unsigned long ) & frame - > uc ;
2006-02-01 14:06:38 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
give_sigsegv :
force_sigsegv ( sig , current ) ;
2006-02-01 14:06:38 +03:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
/*
* OK , we ' re invoking a handler
*/
2006-02-01 14:06:38 +03:00
static int
2005-04-17 02:20:36 +04:00
handle_signal ( unsigned long sig , struct k_sigaction * ka ,
siginfo_t * info , sigset_t * oldset , struct pt_regs * regs )
{
2006-02-01 14:06:38 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
/* Set up the stack frame */
if ( ka - > sa . sa_flags & SA_SIGINFO )
2006-02-01 14:06:38 +03:00
ret = setup_rt_frame ( sig , ka , info , oldset , regs ) ;
2005-04-17 02:20:36 +04:00
else
2006-02-01 14:06:38 +03:00
ret = setup_frame ( sig , ka , oldset , regs ) ;
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 ) ;
}
2005-04-17 02:20:36 +04:00
2006-02-01 14:06:38 +03:00
return ret ;
2005-04-17 02:20:36 +04: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-02-01 14:06:38 +03:00
void do_signal ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
unsigned long retval = 0 , continue_addr = 0 , restart_addr = 0 ;
siginfo_t info ;
int signr ;
struct k_sigaction ka ;
2006-02-01 14:06:38 +03:00
sigset_t * oldset ;
2005-04-17 02:20:36 +04: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-02-01 14:06:38 +03:00
return ;
2005-04-17 02:20:36 +04:00
2006-02-01 14:06:38 +03:00
if ( test_thread_flag ( TIF_RESTORE_SIGMASK ) )
oldset = & current - > saved_sigmask ;
else
2005-04-17 02:20:36 +04:00
oldset = & current - > blocked ;
/* Are we from a system call? */
if ( regs - > trap = = __LC_SVC_OLD_PSW ) {
continue_addr = regs - > psw . addr ;
restart_addr = continue_addr - regs - > ilc ;
retval = regs - > gprs [ 2 ] ;
/* Prepare for system call restart. We do this here so that a
debugger will see the already changed PSW . */
2006-02-01 14:06:38 +03:00
switch ( retval ) {
case - ERESTARTNOHAND :
case - ERESTARTSYS :
case - ERESTARTNOINTR :
2005-04-17 02:20:36 +04:00
regs - > gprs [ 2 ] = regs - > orig_gpr2 ;
regs - > psw . addr = restart_addr ;
2006-02-01 14:06:38 +03:00
break ;
case - ERESTART_RESTARTBLOCK :
2005-04-17 02:20:36 +04:00
regs - > gprs [ 2 ] = - EINTR ;
}
2006-09-20 17:58:54 +04:00
regs - > trap = - 1 ; /* Don't deal with this again. */
2005-04-17 02:20:36 +04:00
}
/* Get 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 . */
if ( signr > 0 & & regs - > psw . addr = = restart_addr ) {
if ( retval = = - ERESTARTNOHAND
| | ( retval = = - ERESTARTSYS
& & ! ( current - > sighand - > action [ signr - 1 ] . sa . sa_flags
& SA_RESTART ) ) ) {
regs - > gprs [ 2 ] = - EINTR ;
regs - > psw . addr = continue_addr ;
}
}
if ( signr > 0 ) {
/* Whee! Actually deliver the signal. */
2008-01-26 16:11:22 +03:00
int ret ;
2006-01-06 11:19:28 +03:00
# ifdef CONFIG_COMPAT
2005-04-17 02:20:36 +04:00
if ( test_thread_flag ( TIF_31BIT ) ) {
2008-01-26 16:11:22 +03:00
ret = handle_signal32 ( signr , & ka , & info , oldset , regs ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:11:22 +03:00
else
2005-04-17 02:20:36 +04:00
# endif
2008-01-26 16:11:22 +03:00
ret = handle_signal ( signr , & ka , & info , oldset , regs ) ;
if ( ! ret ) {
2006-02-01 14:06:38 +03: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-01-26 16:11:22 +03:00
/*
* If we would have taken a single - step trap
* for a normal instruction , act like we took
* one for the handler setup .
*/
if ( current - > thread . per_info . single_step )
set_thread_flag ( TIF_SINGLE_STEP ) ;
2006-02-01 14:06:38 +03:00
}
return ;
}
/*
* 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 ) ;
2005-04-17 02:20:36 +04:00
}
/* Restart a different system call. */
if ( retval = = - ERESTART_RESTARTBLOCK
& & regs - > psw . addr = = continue_addr ) {
regs - > gprs [ 2 ] = __NR_restart_syscall ;
set_thread_flag ( TIF_RESTART_SVC ) ;
}
}