2005-04-17 02:20:36 +04:00
/*
* linux / arch / parisc / kernel / signal . c : Architecture - specific signal
* handling support .
*
* Copyright ( C ) 2000 David Huggins - Daines < dhd @ debian . org >
* Copyright ( C ) 2000 Linuxcare , Inc .
*
* Based on the ia64 , i386 , and alpha versions .
*
* Like the IA - 64 , we are a recent enough port ( we are * starting *
* with glibc2 .2 ) that we do not need to support the old non - realtime
* Linux signals . Therefore we don ' t . HP / UX signals will go in
* arch / parisc / hpux / signal . c when we figure out how to do them .
*/
# 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>
2009-04-27 08:29:53 +04:00
# include <linux/tracehook.h>
2005-04-17 02:20:36 +04:00
# include <linux/unistd.h>
# include <linux/stddef.h>
# include <linux/compat.h>
# include <linux/elf.h>
# include <asm/ucontext.h>
# include <asm/rt_sigframe.h>
# include <asm/uaccess.h>
# include <asm/pgalloc.h>
# include <asm/cacheflush.h>
2005-09-09 22:57:26 +04:00
# include <asm/asm-offsets.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_COMPAT
# include "signal32.h"
# endif
# define DEBUG_SIG 0
# define DEBUG_SIG_LEVEL 2
# if DEBUG_SIG
# define DBG(LEVEL, ...) \
( ( DEBUG_SIG_LEVEL > = LEVEL ) \
? printk ( __VA_ARGS__ ) : ( void ) 0 )
# else
# define DBG(LEVEL, ...)
# endif
/* gcc will complain if a pointer is cast to an integer of different
* size . If you really need to do this ( and we do for an ELF32 user
* application in an ELF64 kernel ) then you have to do a cast to an
* integer of the same size first . The A ( ) macro accomplishes
* this . */
# define A(__x) ((unsigned long)(__x))
/*
* Atomically swap in the new signal mask , and wait for a signal .
*/
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
# include "sys32.h"
# endif
/*
* Do a signal return - restore sigcontext .
*/
/* Trampoline for calling rt_sigreturn() */
# define INSN_LDI_R25_0 0x34190000 /* ldi 0,%r25 (in_syscall=0) */
# define INSN_LDI_R25_1 0x34190002 /* ldi 1,%r25 (in_syscall=1) */
# define INSN_LDI_R20 0x3414015a /* ldi __NR_rt_sigreturn,%r20 */
# define INSN_BLE_SR2_R0 0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */
# define INSN_NOP 0x08000240 /* nop */
/* For debugging */
# define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */
static long
restore_sigcontext ( struct sigcontext __user * sc , struct pt_regs * regs )
{
long err = 0 ;
err | = __copy_from_user ( regs - > gr , sc - > sc_gr , sizeof ( regs - > gr ) ) ;
err | = __copy_from_user ( regs - > fr , sc - > sc_fr , sizeof ( regs - > fr ) ) ;
err | = __copy_from_user ( regs - > iaoq , sc - > sc_iaoq , sizeof ( regs - > iaoq ) ) ;
err | = __copy_from_user ( regs - > iasq , sc - > sc_iasq , sizeof ( regs - > iasq ) ) ;
err | = __get_user ( regs - > sar , & sc - > sc_sar ) ;
DBG ( 2 , " restore_sigcontext: iaoq is 0x%#lx / 0x%#lx \n " ,
regs - > iaoq [ 0 ] , regs - > iaoq [ 1 ] ) ;
DBG ( 2 , " restore_sigcontext: r28 is %ld \n " , regs - > gr [ 28 ] ) ;
return err ;
}
void
sys_rt_sigreturn ( struct pt_regs * regs , int in_syscall )
{
struct rt_sigframe __user * frame ;
sigset_t set ;
unsigned long usp = ( regs - > gr [ 30 ] & ~ ( 0x01UL ) ) ;
unsigned long sigframe_size = PARISC_RT_SIGFRAME_SIZE ;
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
compat_sigset_t compat_set ;
struct compat_rt_sigframe __user * compat_frame ;
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) )
2005-04-17 02:20:36 +04:00
sigframe_size = PARISC_RT_SIGFRAME_SIZE32 ;
# endif
2012-04-22 11:15:40 +04:00
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
2005-04-17 02:20:36 +04:00
/* Unwind the user stack to get the rt_sigframe structure. */
frame = ( struct rt_sigframe __user * )
( usp - sigframe_size ) ;
DBG ( 2 , " sys_rt_sigreturn: frame is %p \n " , frame ) ;
2012-05-19 08:29:22 +04:00
regs - > orig_r28 = 1 ; /* no restarts for sigreturn */
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
compat_frame = ( struct compat_rt_sigframe __user * ) frame ;
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) ) {
2005-04-17 02:20:36 +04:00
DBG ( 2 , " sys_rt_sigreturn: ELF32 process. \n " ) ;
if ( __copy_from_user ( & compat_set , & compat_frame - > uc . uc_sigmask , sizeof ( compat_set ) ) )
goto give_sigsegv ;
sigset_32to64 ( & set , & compat_set ) ;
} else
# endif
{
if ( __copy_from_user ( & set , & frame - > uc . uc_sigmask , sizeof ( set ) ) )
goto give_sigsegv ;
}
2012-05-11 04:58:58 +04:00
set_current_blocked ( & set ) ;
2005-04-17 02:20:36 +04:00
/* Good thing we saved the old gr[30], eh? */
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) ) {
2005-04-17 02:20:36 +04:00
DBG ( 1 , " sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p \n " ,
& compat_frame - > uc . uc_mcontext ) ;
// FIXME: Load upper half from register file
if ( restore_sigcontext32 ( & compat_frame - > uc . uc_mcontext ,
& compat_frame - > regs , regs ) )
goto give_sigsegv ;
DBG ( 1 , " sys_rt_sigreturn: usp %#08lx stack 0x%p \n " ,
usp , & compat_frame - > uc . uc_stack ) ;
if ( do_sigaltstack32 ( & compat_frame - > uc . uc_stack , NULL , usp ) = = - EFAULT )
goto give_sigsegv ;
} else
# endif
{
DBG ( 1 , " sys_rt_sigreturn: frame->uc.uc_mcontext 0x%p \n " ,
& frame - > uc . uc_mcontext ) ;
if ( restore_sigcontext ( & frame - > uc . uc_mcontext , regs ) )
goto give_sigsegv ;
DBG ( 1 , " sys_rt_sigreturn: usp %#08lx stack 0x%p \n " ,
usp , & frame - > uc . uc_stack ) ;
if ( do_sigaltstack ( & frame - > uc . uc_stack , NULL , usp ) = = - EFAULT )
goto give_sigsegv ;
}
/* If we are on the syscall path IAOQ will not be restored, and
* if we are on the interrupt path we must not corrupt gr31 .
*/
if ( in_syscall )
regs - > gr [ 31 ] = regs - > iaoq [ 0 ] ;
# if DEBUG_SIG
DBG ( 1 , " sys_rt_sigreturn: returning to %#lx, DUMPING REGS: \n " , regs - > iaoq [ 0 ] ) ;
show_regs ( regs ) ;
# endif
return ;
give_sigsegv :
DBG ( 1 , " sys_rt_sigreturn: Sending SIGSEGV \n " ) ;
2010-11-30 04:34:38 +03:00
force_sig ( SIGSEGV , current ) ;
2005-04-17 02:20:36 +04:00
return ;
}
/*
* Set up a signal frame .
*/
static inline void __user *
get_sigframe ( struct k_sigaction * ka , unsigned long sp , size_t frame_size )
{
/*FIXME: ELF32 vs. ELF64 has different frame_size, but since we
don ' t use the parameter it doesn ' t matter */
DBG ( 1 , " get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx \n " ,
( unsigned long ) ka , sp , frame_size ) ;
2006-06-23 13:05:36 +04:00
if ( ( ka - > sa . sa_flags & SA_ONSTACK ) ! = 0 & & ! sas_ss_flags ( sp ) )
2005-04-17 02:20:36 +04:00
sp = current - > sas_ss_sp ; /* Stacks grow up! */
DBG ( 1 , " get_sigframe: Returning sp = %#lx \n " , ( unsigned long ) sp ) ;
return ( void __user * ) sp ; /* Stacks grow up. Fun. */
}
static long
setup_sigcontext ( struct sigcontext __user * sc , struct pt_regs * regs , int in_syscall )
{
unsigned long flags = 0 ;
long err = 0 ;
if ( on_sig_stack ( ( unsigned long ) sc ) )
flags | = PARISC_SC_FLAG_ONSTACK ;
if ( in_syscall ) {
flags | = PARISC_SC_FLAG_IN_SYSCALL ;
/* regs->iaoq is undefined in the syscall return path */
err | = __put_user ( regs - > gr [ 31 ] , & sc - > sc_iaoq [ 0 ] ) ;
err | = __put_user ( regs - > gr [ 31 ] + 4 , & sc - > sc_iaoq [ 1 ] ) ;
err | = __put_user ( regs - > sr [ 3 ] , & sc - > sc_iasq [ 0 ] ) ;
err | = __put_user ( regs - > sr [ 3 ] , & sc - > sc_iasq [ 1 ] ) ;
DBG ( 1 , " setup_sigcontext: iaoq %#lx / %#lx (in syscall) \n " ,
regs - > gr [ 31 ] , regs - > gr [ 31 ] + 4 ) ;
} else {
err | = __copy_to_user ( sc - > sc_iaoq , regs - > iaoq , sizeof ( regs - > iaoq ) ) ;
err | = __copy_to_user ( sc - > sc_iasq , regs - > iasq , sizeof ( regs - > iasq ) ) ;
DBG ( 1 , " setup_sigcontext: iaoq %#lx / %#lx (not in syscall) \n " ,
regs - > iaoq [ 0 ] , regs - > iaoq [ 1 ] ) ;
}
err | = __put_user ( flags , & sc - > sc_flags ) ;
err | = __copy_to_user ( sc - > sc_gr , regs - > gr , sizeof ( regs - > gr ) ) ;
err | = __copy_to_user ( sc - > sc_fr , regs - > fr , sizeof ( regs - > fr ) ) ;
err | = __put_user ( regs - > sar , & sc - > sc_sar ) ;
DBG ( 1 , " setup_sigcontext: r28 is %ld \n " , regs - > gr [ 28 ] ) ;
return err ;
}
static long
setup_rt_frame ( int sig , struct k_sigaction * ka , siginfo_t * info ,
sigset_t * set , struct pt_regs * regs , int in_syscall )
{
struct rt_sigframe __user * frame ;
unsigned long rp , usp ;
unsigned long haddr , sigframe_size ;
int err = 0 ;
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
compat_int_t compat_val ;
struct compat_rt_sigframe __user * compat_frame ;
compat_sigset_t compat_set ;
# endif
usp = ( regs - > gr [ 30 ] & ~ ( 0x01UL ) ) ;
/*FIXME: frame_size parameter is unused, remove it. */
frame = get_sigframe ( ka , usp , sizeof ( * frame ) ) ;
DBG ( 1 , " SETUP_RT_FRAME: START \n " ) ;
DBG ( 1 , " setup_rt_frame: frame %p info %p \n " , frame , info ) ;
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
compat_frame = ( struct compat_rt_sigframe __user * ) frame ;
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) ) {
2005-04-17 02:20:36 +04:00
DBG ( 1 , " setup_rt_frame: frame->info = 0x%p \n " , & compat_frame - > info ) ;
2006-01-15 22:10:29 +03:00
err | = copy_siginfo_to_user32 ( & compat_frame - > info , info ) ;
2005-04-17 02:20:36 +04:00
DBG ( 1 , " SETUP_RT_FRAME: 1 \n " ) ;
compat_val = ( compat_int_t ) current - > sas_ss_sp ;
err | = __put_user ( compat_val , & compat_frame - > uc . uc_stack . ss_sp ) ;
DBG ( 1 , " SETUP_RT_FRAME: 2 \n " ) ;
compat_val = ( compat_int_t ) current - > sas_ss_size ;
err | = __put_user ( compat_val , & compat_frame - > uc . uc_stack . ss_size ) ;
DBG ( 1 , " SETUP_RT_FRAME: 3 \n " ) ;
compat_val = sas_ss_flags ( regs - > gr [ 30 ] ) ;
err | = __put_user ( compat_val , & compat_frame - > uc . uc_stack . ss_flags ) ;
DBG ( 1 , " setup_rt_frame: frame->uc = 0x%p \n " , & compat_frame - > uc ) ;
DBG ( 1 , " setup_rt_frame: frame->uc.uc_mcontext = 0x%p \n " , & compat_frame - > uc . uc_mcontext ) ;
err | = setup_sigcontext32 ( & compat_frame - > uc . uc_mcontext ,
& compat_frame - > regs , regs , in_syscall ) ;
sigset_64to32 ( & compat_set , set ) ;
err | = __copy_to_user ( & compat_frame - > uc . uc_sigmask , & compat_set , sizeof ( compat_set ) ) ;
} else
# endif
{
DBG ( 1 , " setup_rt_frame: frame->info = 0x%p \n " , & frame - > info ) ;
err | = copy_siginfo_to_user ( & frame - > info , info ) ;
err | = __put_user ( current - > sas_ss_sp , & frame - > uc . uc_stack . ss_sp ) ;
err | = __put_user ( current - > sas_ss_size , & frame - > uc . uc_stack . ss_size ) ;
err | = __put_user ( sas_ss_flags ( regs - > gr [ 30 ] ) ,
& frame - > uc . uc_stack . ss_flags ) ;
DBG ( 1 , " setup_rt_frame: frame->uc = 0x%p \n " , & frame - > uc ) ;
DBG ( 1 , " setup_rt_frame: frame->uc.uc_mcontext = 0x%p \n " , & frame - > uc . uc_mcontext ) ;
err | = setup_sigcontext ( & frame - > uc . uc_mcontext , regs , in_syscall ) ;
2011-03-31 05:57:33 +04:00
/* FIXME: Should probably be converted as well for the compat case */
2005-04-17 02:20:36 +04:00
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 . The first words of tramp are used to
save the previous sigrestartblock trampoline that might be
on the stack . We start the sigreturn trampoline at
SIGRESTARTBLOCK_TRAMP + X . */
err | = __put_user ( in_syscall ? INSN_LDI_R25_1 : INSN_LDI_R25_0 ,
& frame - > tramp [ SIGRESTARTBLOCK_TRAMP + 0 ] ) ;
err | = __put_user ( INSN_LDI_R20 ,
& frame - > tramp [ SIGRESTARTBLOCK_TRAMP + 1 ] ) ;
err | = __put_user ( INSN_BLE_SR2_R0 ,
& frame - > tramp [ SIGRESTARTBLOCK_TRAMP + 2 ] ) ;
err | = __put_user ( INSN_NOP , & frame - > tramp [ SIGRESTARTBLOCK_TRAMP + 3 ] ) ;
# if DEBUG_SIG
/* Assert that we're flushing in the correct space... */
{
int sid ;
asm ( " mfsp %%sr3,%0 " : " =r " ( sid ) ) ;
DBG ( 1 , " setup_rt_frame: Flushing 64 bytes at space %#x offset %p \n " ,
sid , frame - > tramp ) ;
}
# endif
flush_user_dcache_range ( ( unsigned long ) & frame - > tramp [ 0 ] ,
( unsigned long ) & frame - > tramp [ TRAMP_SIZE ] ) ;
flush_user_icache_range ( ( unsigned long ) & frame - > tramp [ 0 ] ,
( unsigned long ) & frame - > tramp [ TRAMP_SIZE ] ) ;
2008-02-03 16:42:53 +03:00
/* TRAMP Words 0-4, Length 5 = SIGRESTARTBLOCK_TRAMP
2005-04-17 02:20:36 +04:00
* TRAMP Words 5 - 9 , Length 4 = SIGRETURN_TRAMP
* So the SIGRETURN_TRAMP is at the end of SIGRESTARTBLOCK_TRAMP
*/
rp = ( unsigned long ) & frame - > tramp [ SIGRESTARTBLOCK_TRAMP ] ;
if ( err )
goto give_sigsegv ;
haddr = A ( ka - > sa . sa_handler ) ;
/* The sa_handler may be a pointer to a function descriptor */
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) ) {
2005-04-17 02:20:36 +04:00
# endif
if ( haddr & PA_PLABEL_FDESC ) {
Elf32_Fdesc fdesc ;
Elf32_Fdesc __user * ufdesc = ( Elf32_Fdesc __user * ) A ( haddr & ~ 3 ) ;
err = __copy_from_user ( & fdesc , ufdesc , sizeof ( fdesc ) ) ;
if ( err )
goto give_sigsegv ;
haddr = fdesc . addr ;
regs - > gr [ 19 ] = fdesc . gp ;
}
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
} else {
Elf64_Fdesc fdesc ;
Elf64_Fdesc __user * ufdesc = ( Elf64_Fdesc __user * ) A ( haddr & ~ 3 ) ;
err = __copy_from_user ( & fdesc , ufdesc , sizeof ( fdesc ) ) ;
if ( err )
goto give_sigsegv ;
haddr = fdesc . addr ;
regs - > gr [ 19 ] = fdesc . gp ;
DBG ( 1 , " setup_rt_frame: 64 bit signal, exe=%#lx, r19=%#lx, in_syscall=%d \n " ,
haddr , regs - > gr [ 19 ] , in_syscall ) ;
}
# endif
/* The syscall return path will create IAOQ values from r31.
*/
sigframe_size = PARISC_RT_SIGFRAME_SIZE ;
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) )
2005-04-17 02:20:36 +04:00
sigframe_size = PARISC_RT_SIGFRAME_SIZE32 ;
# endif
if ( in_syscall ) {
regs - > gr [ 31 ] = haddr ;
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2006-08-27 19:04:26 +04:00
if ( ! test_thread_flag ( TIF_32BIT ) )
2005-04-17 02:20:36 +04:00
sigframe_size | = 1 ;
# endif
} else {
unsigned long psw = USER_PSW ;
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2006-08-27 19:04:26 +04:00
if ( ! test_thread_flag ( TIF_32BIT ) )
2005-04-17 02:20:36 +04:00
psw | = PSW_W ;
# endif
/* If we are singlestepping, arrange a trap to be delivered
when we return to userspace . Note the semantics - - we
should trap before the first insn in the handler is
executed . Ref :
http : //sources.redhat.com/ml/gdb/2004-11/msg00245.html
*/
if ( pa_psw ( current ) - > r ) {
pa_psw ( current ) - > r = 0 ;
psw | = PSW_R ;
mtctl ( - 1 , 0 ) ;
}
regs - > gr [ 0 ] = psw ;
regs - > iaoq [ 0 ] = haddr | 3 ;
regs - > iaoq [ 1 ] = regs - > iaoq [ 0 ] + 4 ;
}
regs - > gr [ 2 ] = rp ; /* userland return pointer */
regs - > gr [ 26 ] = sig ; /* signal number */
2007-01-28 16:58:52 +03:00
# ifdef CONFIG_64BIT
2006-06-16 23:10:02 +04:00
if ( is_compat_task ( ) ) {
2005-04-17 02:20:36 +04:00
regs - > gr [ 25 ] = A ( & compat_frame - > info ) ; /* siginfo pointer */
regs - > gr [ 24 ] = A ( & compat_frame - > uc ) ; /* ucontext pointer */
} else
# endif
{
regs - > gr [ 25 ] = A ( & frame - > info ) ; /* siginfo pointer */
regs - > gr [ 24 ] = A ( & frame - > uc ) ; /* ucontext pointer */
}
DBG ( 1 , " setup_rt_frame: making sigreturn frame: %#lx + %#lx = %#lx \n " ,
regs - > gr [ 30 ] , sigframe_size ,
regs - > gr [ 30 ] + sigframe_size ) ;
/* Raise the user stack pointer to make a proper call frame. */
regs - > gr [ 30 ] = ( A ( frame ) + sigframe_size ) ;
DBG ( 1 , " setup_rt_frame: sig deliver (%s,%d) frame=0x%p sp=%#lx iaoq=%#lx/%#lx rp=%#lx \n " ,
current - > comm , current - > pid , frame , regs - > gr [ 30 ] ,
regs - > iaoq [ 0 ] , regs - > iaoq [ 1 ] , rp ) ;
return 1 ;
give_sigsegv :
DBG ( 1 , " setup_rt_frame: sending SIGSEGV \n " ) ;
2005-10-22 06:49:47 +04:00
force_sigsegv ( sig , current ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* OK , we ' re invoking a handler .
*/
2012-05-19 09:13:01 +04:00
static void
2005-04-17 02:20:36 +04:00
handle_signal ( unsigned long sig , siginfo_t * info , struct k_sigaction * ka ,
2012-05-02 17:59:21 +04:00
struct pt_regs * regs , int in_syscall )
2005-04-17 02:20:36 +04:00
{
2012-05-02 17:59:21 +04:00
sigset_t * oldset = sigmask_to_save ( ) ;
2005-04-17 02:20:36 +04:00
DBG ( 1 , " handle_signal: sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p \n " ,
sig , ka , info , oldset , regs ) ;
/* Set up the stack frame */
if ( ! setup_rt_frame ( sig , ka , info , oldset , regs , in_syscall ) )
2012-05-19 09:13:01 +04:00
return ;
2005-04-17 02:20:36 +04:00
2012-04-28 10:04:15 +04:00
signal_delivered ( sig , info , ka , regs ,
2010-02-12 18:53:08 +03:00
test_thread_flag ( TIF_SINGLESTEP ) | |
test_thread_flag ( TIF_BLOCKSTEP ) ) ;
2009-04-27 08:29:53 +04:00
2012-05-22 07:42:15 +04:00
DBG ( 1 , KERN_DEBUG " do_signal: Exit (success), regs->gr[28] = %ld \n " ,
regs - > gr [ 28 ] ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-15 08:36:26 +03:00
static inline void
syscall_restart ( struct pt_regs * regs , struct k_sigaction * ka )
{
2012-05-19 08:29:22 +04:00
if ( regs - > orig_r28 )
return ;
regs - > orig_r28 = 1 ; /* no more restarts */
2007-01-15 08:36:26 +03:00
/* Check the return code */
switch ( regs - > gr [ 28 ] ) {
case - ERESTART_RESTARTBLOCK :
case - ERESTARTNOHAND :
DBG ( 1 , " ERESTARTNOHAND: returning -EINTR \n " ) ;
regs - > gr [ 28 ] = - EINTR ;
break ;
case - ERESTARTSYS :
if ( ! ( ka - > sa . sa_flags & SA_RESTART ) ) {
DBG ( 1 , " ERESTARTSYS: putting -EINTR \n " ) ;
regs - > gr [ 28 ] = - EINTR ;
break ;
}
/* fallthrough */
case - ERESTARTNOINTR :
/* A syscall is just a branch, so all
* we have to do is fiddle the return pointer .
*/
regs - > gr [ 31 ] - = 8 ; /* delayed branching */
break ;
}
}
static inline void
insert_restart_trampoline ( struct pt_regs * regs )
{
2012-05-19 08:29:22 +04:00
if ( regs - > orig_r28 )
return ;
regs - > orig_r28 = 1 ; /* no more restarts */
2007-01-15 08:36:26 +03:00
switch ( regs - > gr [ 28 ] ) {
case - ERESTART_RESTARTBLOCK : {
/* Restart the system call - no handlers present */
unsigned int * usp = ( unsigned int * ) regs - > gr [ 30 ] ;
/* Setup a trampoline to restart the syscall
* with __NR_restart_syscall
*
* 0 : < return address ( orig r31 ) >
* 4 : < 2 nd half for 64 - bit >
* 8 : ldw 0 ( % sp ) , % r31
* 12 : be 0x100 ( % sr2 , % r0 )
* 16 : ldi __NR_restart_syscall , % r20
*/
# ifdef CONFIG_64BIT
put_user ( regs - > gr [ 31 ] > > 32 , & usp [ 0 ] ) ;
put_user ( regs - > gr [ 31 ] & 0xffffffff , & usp [ 1 ] ) ;
put_user ( 0x0fc010df , & usp [ 2 ] ) ;
# else
put_user ( regs - > gr [ 31 ] , & usp [ 0 ] ) ;
put_user ( 0x0fc0109f , & usp [ 2 ] ) ;
# endif
put_user ( 0xe0008200 , & usp [ 3 ] ) ;
put_user ( 0x34140000 , & usp [ 4 ] ) ;
/* Stack is 64-byte aligned, and we only need
* to flush 1 cache line .
* Flushing one cacheline is cheap .
* " sync " on bigger ( > 4 way ) boxes is not .
*/
2008-04-16 02:36:38 +04:00
flush_user_dcache_range ( regs - > gr [ 30 ] , regs - > gr [ 30 ] + 4 ) ;
flush_user_icache_range ( regs - > gr [ 30 ] , regs - > gr [ 30 ] + 4 ) ;
2007-01-15 08:36:26 +03:00
regs - > gr [ 31 ] = regs - > gr [ 30 ] + 8 ;
return ;
}
case - ERESTARTNOHAND :
case - ERESTARTSYS :
case - ERESTARTNOINTR : {
/* Hooray for delayed branching. We don't
* have to restore % r20 ( the system call
* number ) because it gets loaded in the delay
* slot of the branch external instruction .
*/
regs - > gr [ 31 ] - = 8 ;
return ;
}
default :
break ;
}
}
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 .
*
* We need to be able to restore the syscall arguments ( r21 - r26 ) to
* restart syscalls . Thus , the syscall path should save them in the
* pt_regs structure ( it ' s okay to do so since they are caller - save
* registers ) . As noted below , the syscall number gets restored for
* us due to the magic of delayed branching .
*/
2007-01-09 00:28:06 +03:00
asmlinkage void
do_signal ( struct pt_regs * regs , long in_syscall )
2005-04-17 02:20:36 +04:00
{
siginfo_t info ;
struct k_sigaction ka ;
int signr ;
2012-05-02 17:59:21 +04:00
DBG ( 1 , " \n do_signal: regs=0x%p, sr7 %#lx, in_syscall=%d \n " ,
regs , regs - > sr [ 7 ] , in_syscall ) ;
2005-04-17 02:20:36 +04:00
2012-05-19 09:13:01 +04:00
signr = get_signal_to_deliver ( & info , & ka , regs , NULL ) ;
DBG ( 3 , " do_signal: signr = %d, regs->gr[28] = %ld \n " , signr , regs - > gr [ 28 ] ) ;
2005-04-17 02:20:36 +04:00
2012-05-19 09:13:01 +04:00
if ( signr > 0 ) {
2005-04-17 02:20:36 +04:00
/* Restart a system call if necessary. */
2007-01-15 08:36:26 +03:00
if ( in_syscall )
syscall_restart ( regs , & ka ) ;
2012-05-19 09:13:01 +04:00
handle_signal ( signr , & info , & ka , regs , in_syscall ) ;
return ;
2005-04-17 02:20:36 +04:00
}
/* Did we come from a system call? */
2007-01-15 08:36:26 +03:00
if ( in_syscall )
insert_restart_trampoline ( regs ) ;
2005-04-17 02:20:36 +04:00
DBG ( 1 , " do_signal: Exit (not delivered), regs->gr[28] = %ld \n " ,
regs - > gr [ 28 ] ) ;
2012-05-22 07:33:55 +04:00
restore_saved_sigmask ( ) ;
2007-01-09 00:28:06 +03:00
}
void do_notify_resume ( struct pt_regs * regs , long in_syscall )
{
2012-05-23 23:28:58 +04:00
if ( test_thread_flag ( TIF_SIGPENDING ) )
2007-01-09 00:28:06 +03:00
do_signal ( regs , in_syscall ) ;
2009-09-02 12:14:16 +04:00
if ( test_thread_flag ( TIF_NOTIFY_RESUME ) ) {
clear_thread_flag ( TIF_NOTIFY_RESUME ) ;
tracehook_notify_resume ( regs ) ;
}
2005-04-17 02:20:36 +04:00
}