2007-06-04 09:15:49 +04:00
/*
* Common signal handling code for both 32 and 64 bits
*
* Copyright ( c ) 2007 Benjamin Herrenschmidt , IBM Coproration
* Extracted from signal_32 . c and signal_64 . c
*
* This file is subject to the terms and conditions of the GNU General
* Public License . See the file README . legal in the main directory of
* this archive for more details .
*/
2008-07-27 10:49:50 +04:00
# include <linux/tracehook.h>
2007-06-04 09:15:49 +04:00
# include <linux/signal.h>
2007-06-04 11:22:48 +04:00
# include <asm/uaccess.h>
2007-06-04 09:15:49 +04:00
# include <asm/unistd.h>
2007-06-04 09:15:51 +04:00
# include "signal.h"
2007-10-12 04:20:07 +04:00
/* Log an error when sending an unhandled signal to a process. Controlled
* through debug . exception - trace sysctl .
*/
int show_unhandled_signals = 0 ;
2007-06-04 11:22:48 +04:00
/*
* Allocate space for the signal frame
*/
void __user * get_sigframe ( struct k_sigaction * ka , struct pt_regs * regs ,
size_t frame_size )
{
unsigned long oldsp , newsp ;
/* Default to using normal stack */
oldsp = regs - > gpr [ 1 ] ;
/* Check for alt stack */
if ( ( ka - > sa . sa_flags & SA_ONSTACK ) & &
current - > sas_ss_size & & ! on_sig_stack ( oldsp ) )
oldsp = ( current - > sas_ss_sp + current - > sas_ss_size ) ;
/* Get aligned frame */
newsp = ( oldsp - frame_size ) & ~ 0xFUL ;
/* Check access */
if ( ! access_ok ( VERIFY_WRITE , ( void __user * ) newsp , oldsp - newsp ) )
return NULL ;
return ( void __user * ) newsp ;
}
2007-06-04 09:15:52 +04:00
2007-06-04 09:15:51 +04:00
/*
* Restore the user process ' s signal mask
*/
void restore_sigmask ( sigset_t * set )
{
sigdelsetmask ( set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = * set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
}
2007-06-04 09:15:52 +04:00
static void check_syscall_restart ( struct pt_regs * regs , struct k_sigaction * ka ,
int has_handler )
2007-06-04 09:15:49 +04:00
{
unsigned long ret = regs - > gpr [ 3 ] ;
int restart = 1 ;
/* syscall ? */
if ( TRAP ( regs ) ! = 0x0C00 )
return ;
/* error signalled ? */
if ( ! ( regs - > ccr & 0x10000000 ) )
return ;
switch ( ret ) {
case ERESTART_RESTARTBLOCK :
case ERESTARTNOHAND :
/* ERESTARTNOHAND means that the syscall should only be
* restarted if there was no handler for the signal , and since
* we only get here if there is a handler , we dont restart .
*/
restart = ! has_handler ;
break ;
case ERESTARTSYS :
/* ERESTARTSYS means to restart the syscall if there is no
* handler or the handler was registered with SA_RESTART
*/
restart = ! has_handler | | ( ka - > sa . sa_flags & SA_RESTART ) ! = 0 ;
break ;
case ERESTARTNOINTR :
/* ERESTARTNOINTR means that the syscall should be
* called again after the signal handler returns .
*/
break ;
default :
return ;
}
if ( restart ) {
if ( ret = = ERESTART_RESTARTBLOCK )
regs - > gpr [ 0 ] = __NR_restart_syscall ;
else
regs - > gpr [ 3 ] = regs - > orig_gpr3 ;
regs - > nip - = 4 ;
regs - > result = 0 ;
} else {
regs - > result = - EINTR ;
regs - > gpr [ 3 ] = EINTR ;
regs - > ccr | = 0x10000000 ;
}
}
2007-06-04 09:15:50 +04:00
2007-06-04 09:15:52 +04:00
int do_signal ( sigset_t * oldset , struct pt_regs * regs )
{
siginfo_t info ;
int signr ;
struct k_sigaction ka ;
int ret ;
int is32 = is_32bit_task ( ) ;
2008-04-28 11:30:37 +04:00
if ( current_thread_info ( ) - > local_flags & _TLF_RESTORE_SIGMASK )
2007-06-04 09:15:52 +04:00
oldset = & current - > saved_sigmask ;
else if ( ! oldset )
oldset = & current - > blocked ;
signr = get_signal_to_deliver ( & info , & ka , regs , NULL ) ;
/* Is there any syscall restart business here ? */
check_syscall_restart ( regs , & ka , signr > 0 ) ;
if ( signr < = 0 ) {
2008-04-28 11:30:37 +04:00
struct thread_info * ti = current_thread_info ( ) ;
2007-06-04 09:15:52 +04:00
/* No signal to deliver -- put the saved sigmask back */
2008-04-28 11:30:37 +04:00
if ( ti - > local_flags & _TLF_RESTORE_SIGMASK ) {
ti - > local_flags & = ~ _TLF_RESTORE_SIGMASK ;
2007-06-04 09:15:52 +04:00
sigprocmask ( SIG_SETMASK , & current - > saved_sigmask , NULL ) ;
}
return 0 ; /* no signals delivered */
}
/*
* Reenable the DABR before delivering the signal to
* user space . The DABR will have been cleared if it
* triggered inside the kernel .
*/
2008-07-23 20:10:41 +04:00
if ( current - > thread . dabr ) {
2007-06-04 09:15:52 +04:00
set_dabr ( current - > thread . dabr ) ;
2008-07-25 23:27:33 +04:00
# if defined(CONFIG_BOOKE)
2008-07-23 20:10:41 +04:00
mtspr ( SPRN_DBCR0 , current - > thread . dbcr0 ) ;
# endif
}
2007-06-04 09:15:52 +04:00
if ( is32 ) {
if ( ka . sa . sa_flags & SA_SIGINFO )
ret = handle_rt_signal32 ( signr , & ka , & info , oldset ,
2007-06-04 11:22:48 +04:00
regs ) ;
2007-06-04 09:15:52 +04:00
else
ret = handle_signal32 ( signr , & ka , & info , oldset ,
2007-06-04 11:22:48 +04:00
regs ) ;
2007-06-04 09:15:52 +04:00
} else {
ret = handle_rt_signal64 ( signr , & ka , & info , oldset , regs ) ;
}
if ( ret ) {
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 , signr ) ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
/*
* A signal was successfully delivered ; the saved sigmask is in
2008-04-28 11:30:37 +04:00
* its frame , and we can clear the TLF_RESTORE_SIGMASK flag .
2007-06-04 09:15:52 +04:00
*/
2008-04-28 11:30:37 +04:00
current_thread_info ( ) - > local_flags & = ~ _TLF_RESTORE_SIGMASK ;
2008-07-27 10:49:50 +04:00
/*
* Let tracing know that we ' ve done the handler setup .
*/
tracehook_signal_handler ( signr , & info , & ka , regs ,
test_thread_flag ( TIF_SINGLESTEP ) ) ;
2007-06-04 09:15:52 +04:00
}
return ret ;
}
2007-06-04 09:15:50 +04:00
long sys_sigaltstack ( const stack_t __user * uss , stack_t __user * uoss ,
unsigned long r5 , unsigned long r6 , unsigned long r7 ,
unsigned long r8 , struct pt_regs * regs )
{
return do_sigaltstack ( uss , uoss , regs - > gpr [ 1 ] ) ;
}