2009-03-27 16:25:14 +03:00
/*
* Signal handling
*
* Copyright ( C ) 2008 - 2009 Michal Simek < monstr @ monstr . eu >
* Copyright ( C ) 2008 - 2009 PetaLogix
* Copyright ( C ) 2003 , 2004 John Williams < jwilliams @ itee . uq . edu . au >
* Copyright ( C ) 2001 NEC Corporation
* Copyright ( C ) 2001 Miles Bader < miles @ gnu . org >
* Copyright ( C ) 1999 , 2000 Niibe Yutaka & Kaz Kojima
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*
* 1997 - 11 - 28 Modified for POSIX .1 b signals by Richard Henderson
*
* This file was was derived from the sh version , arch / sh / kernel / signal . c
*
* This file is subject to the terms and conditions of the GNU General
* Public License . See the file COPYING in the main directory of this
* archive for more details .
*/
# 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/personality.h>
# include <linux/percpu.h>
# include <linux/linkage.h>
2012-04-24 10:03:06 +04:00
# include <linux/tracehook.h>
2009-03-27 16:25:14 +03:00
# include <asm/entry.h>
# include <asm/ucontext.h>
# include <linux/uaccess.h>
# include <asm/pgtable.h>
# include <asm/pgalloc.h>
# include <linux/syscalls.h>
# include <asm/cacheflush.h>
# include <asm/syscalls.h>
/*
* Do a signal return ; undo the signal stack .
*/
struct sigframe {
struct sigcontext sc ;
unsigned long extramask [ _NSIG_WORDS - 1 ] ;
unsigned long tramp [ 2 ] ; /* signal trampoline */
} ;
struct rt_sigframe {
struct siginfo info ;
struct ucontext uc ;
unsigned long tramp [ 2 ] ; /* signal trampoline */
} ;
2009-05-01 17:37:46 +04:00
static int restore_sigcontext ( struct pt_regs * regs ,
struct sigcontext __user * sc , int * rval_p )
2009-03-27 16:25:14 +03:00
{
unsigned int err = 0 ;
# define COPY(x) {err |= __get_user(regs->x, &sc->regs.x); }
COPY ( r0 ) ;
COPY ( r1 ) ;
COPY ( r2 ) ; COPY ( r3 ) ; COPY ( r4 ) ; COPY ( r5 ) ;
COPY ( r6 ) ; COPY ( r7 ) ; COPY ( r8 ) ; COPY ( r9 ) ;
COPY ( r10 ) ; COPY ( r11 ) ; COPY ( r12 ) ; COPY ( r13 ) ;
COPY ( r14 ) ; COPY ( r15 ) ; COPY ( r16 ) ; COPY ( r17 ) ;
COPY ( r18 ) ; COPY ( r19 ) ; COPY ( r20 ) ; COPY ( r21 ) ;
COPY ( r22 ) ; COPY ( r23 ) ; COPY ( r24 ) ; COPY ( r25 ) ;
COPY ( r26 ) ; COPY ( r27 ) ; COPY ( r28 ) ; COPY ( r29 ) ;
COPY ( r30 ) ; COPY ( r31 ) ;
COPY ( pc ) ; COPY ( ear ) ; COPY ( esr ) ; COPY ( fsr ) ;
# undef COPY
* rval_p = regs - > r3 ;
return err ;
}
2009-06-18 21:55:29 +04:00
asmlinkage long sys_rt_sigreturn ( struct pt_regs * regs )
2009-03-27 16:25:14 +03:00
{
2009-05-01 17:37:46 +04:00
struct rt_sigframe __user * frame =
2011-01-31 17:10:04 +03:00
( struct rt_sigframe __user * ) ( regs - > r1 ) ;
2009-04-21 16:08:47 +04:00
2009-03-27 16:25:14 +03:00
sigset_t set ;
int rval ;
2012-04-22 11:32:18 +04:00
/* Always make any pending restarted system calls return -EINTR */
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
2009-03-27 16:25:14 +03:00
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set , & frame - > uc . uc_sigmask , sizeof ( set ) ) )
goto badframe ;
2012-05-11 04:58:05 +04:00
set_current_blocked ( & set ) ;
2009-03-27 16:25:14 +03:00
if ( restore_sigcontext ( regs , & frame - > uc . uc_mcontext , & rval ) )
goto badframe ;
2012-12-23 12:08:53 +04:00
if ( restore_altstack ( & frame - > uc . uc_stack ) )
2009-05-01 17:37:46 +04:00
goto badframe ;
2009-03-27 16:25:14 +03:00
return rval ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
/*
* Set up a signal frame .
*/
static int
2009-05-01 17:37:46 +04:00
setup_sigcontext ( struct sigcontext __user * sc , struct pt_regs * regs ,
2009-03-27 16:25:14 +03:00
unsigned long mask )
{
int err = 0 ;
# define COPY(x) {err |= __put_user(regs->x, &sc->regs.x); }
COPY ( r0 ) ;
COPY ( r1 ) ;
COPY ( r2 ) ; COPY ( r3 ) ; COPY ( r4 ) ; COPY ( r5 ) ;
COPY ( r6 ) ; COPY ( r7 ) ; COPY ( r8 ) ; COPY ( r9 ) ;
COPY ( r10 ) ; COPY ( r11 ) ; COPY ( r12 ) ; COPY ( r13 ) ;
COPY ( r14 ) ; COPY ( r15 ) ; COPY ( r16 ) ; COPY ( r17 ) ;
COPY ( r18 ) ; COPY ( r19 ) ; COPY ( r20 ) ; COPY ( r21 ) ;
COPY ( r22 ) ; COPY ( r23 ) ; COPY ( r24 ) ; COPY ( r25 ) ;
COPY ( r26 ) ; COPY ( r27 ) ; COPY ( r28 ) ; COPY ( r29 ) ;
COPY ( r30 ) ; COPY ( r31 ) ;
COPY ( pc ) ; COPY ( ear ) ; COPY ( esr ) ; COPY ( fsr ) ;
# undef COPY
err | = __put_user ( mask , & sc - > oldmask ) ;
return err ;
}
/*
* Determine which stack to use . .
*/
2009-05-01 17:37:46 +04:00
static inline void __user *
2014-03-05 18:32:52 +04:00
get_sigframe ( struct ksignal * ksig , struct pt_regs * regs , size_t frame_size )
2009-03-27 16:25:14 +03:00
{
/* Default to using normal stack */
2014-03-05 18:32:52 +04:00
unsigned long sp = sigsp ( regs - > r1 , ksig ) ;
2009-03-27 16:25:14 +03:00
2009-05-01 17:37:46 +04:00
return ( void __user * ) ( ( sp - frame_size ) & - 8UL ) ;
2009-03-27 16:25:14 +03:00
}
2013-10-07 16:14:38 +04:00
static int setup_rt_frame ( struct ksignal * ksig , sigset_t * set ,
struct pt_regs * regs )
2009-03-27 16:25:14 +03:00
{
2009-05-01 17:37:46 +04:00
struct rt_sigframe __user * frame ;
2013-10-07 16:14:38 +04:00
int err = 0 , sig = ksig - > sig ;
2009-03-27 16:25:14 +03:00
int signal ;
2009-12-10 13:43:57 +03:00
unsigned long address = 0 ;
# ifdef CONFIG_MMU
pmd_t * pmdp ;
pte_t * ptep ;
# endif
2009-03-27 16:25:14 +03:00
2014-03-05 18:32:52 +04:00
frame = get_sigframe ( ksig , regs , sizeof ( * frame ) ) ;
2009-03-27 16:25:14 +03:00
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
2013-10-07 16:14:38 +04:00
return - EFAULT ;
2009-03-27 16:25:14 +03:00
signal = current_thread_info ( ) - > exec_domain
& & current_thread_info ( ) - > exec_domain - > signal_invmap
& & sig < 32
? current_thread_info ( ) - > exec_domain - > signal_invmap [ sig ]
: sig ;
2013-10-07 16:14:38 +04:00
if ( ksig - > ka . sa . sa_flags & SA_SIGINFO )
err | = copy_siginfo_to_user ( & frame - > info , & ksig - > info ) ;
2009-03-27 16:25:14 +03:00
/* Create the ucontext. */
err | = __put_user ( 0 , & frame - > uc . uc_flags ) ;
2011-02-07 14:25:37 +03:00
err | = __put_user ( NULL , & frame - > uc . uc_link ) ;
2012-12-23 12:08:53 +04:00
err | = __save_altstack ( & frame - > uc . uc_stack , regs - > r1 ) ;
2009-03-27 16:25:14 +03:00
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 . */
/* minus 8 is offset to cater for "rtsd r15,8" */
2009-06-18 21:55:29 +04:00
/* addi r12, r0, __NR_sigreturn */
err | = __put_user ( 0x31800000 | __NR_rt_sigreturn ,
frame - > tramp + 0 ) ;
/* brki r14, 0x8 */
err | = __put_user ( 0xb9cc0008 , frame - > tramp + 1 ) ;
/* Return from sighandler will jump to the tramp.
Negative 8 offset because return is rtsd r15 , 8 */
regs - > r15 = ( ( unsigned long ) frame - > tramp ) - 8 ;
2009-12-10 13:43:57 +03:00
address = ( ( unsigned long ) frame - > tramp ) ;
# ifdef CONFIG_MMU
pmdp = pmd_offset ( pud_offset (
pgd_offset ( current - > mm , address ) ,
address ) , address ) ;
preempt_disable ( ) ;
ptep = pte_offset_map ( pmdp , address ) ;
if ( pte_present ( * ptep ) ) {
address = ( unsigned long ) page_address ( pte_page ( * ptep ) ) ;
/* MS: I need add offset in page */
address + = ( ( unsigned long ) frame - > tramp ) & ~ PAGE_MASK ;
/* MS address is virtual */
2013-02-07 18:12:24 +04:00
address = __virt_to_phys ( address ) ;
2009-12-10 13:43:57 +03:00
invalidate_icache_range ( address , address + 8 ) ;
flush_dcache_range ( address , address + 8 ) ;
}
pte_unmap ( ptep ) ;
preempt_enable ( ) ;
# else
flush_icache_range ( address , address + 8 ) ;
flush_dcache_range ( address , address + 8 ) ;
# endif
2009-03-27 16:25:14 +03:00
if ( err )
2013-10-07 16:14:38 +04:00
return - EFAULT ;
2009-03-27 16:25:14 +03:00
/* Set up registers for signal handler */
2011-01-31 17:10:04 +03:00
regs - > r1 = ( unsigned long ) frame ;
2009-04-21 16:08:47 +04:00
2009-03-27 16:25:14 +03:00
/* Signal handler args: */
regs - > r5 = signal ; /* arg 0: signum */
regs - > r6 = ( unsigned long ) & frame - > info ; /* arg 1: siginfo */
regs - > r7 = ( unsigned long ) & frame - > uc ; /* arg2: ucontext */
/* Offset to handle microblaze rtid r14, 0 */
2013-10-07 16:14:38 +04:00
regs - > pc = ( unsigned long ) ksig - > ka . sa . sa_handler ;
2009-03-27 16:25:14 +03:00
set_fs ( USER_DS ) ;
# ifdef DEBUG_SIG
2012-12-27 13:40:38 +04:00
pr_info ( " SIG deliver (%s:%d): sp=%p pc=%08lx \n " ,
2009-03-27 16:25:14 +03:00
current - > comm , current - > pid , frame , regs - > pc ) ;
# endif
2012-05-11 04:58:04 +04:00
return 0 ;
2009-03-27 16:25:14 +03:00
}
/* Handle restarting system calls */
static inline void
handle_restart ( struct pt_regs * regs , struct k_sigaction * ka , int has_handler )
{
switch ( regs - > r3 ) {
case - ERESTART_RESTARTBLOCK :
case - ERESTARTNOHAND :
if ( ! has_handler )
goto do_restart ;
regs - > r3 = - EINTR ;
break ;
case - ERESTARTSYS :
if ( has_handler & & ! ( ka - > sa . sa_flags & SA_RESTART ) ) {
regs - > r3 = - EINTR ;
break ;
}
/* fallthrough */
case - ERESTARTNOINTR :
do_restart :
/* offset of 4 bytes to re-execute trap (brki) instruction */
regs - > pc - = 4 ;
break ;
}
}
/*
* OK , we ' re invoking a handler
*/
2012-05-22 07:42:15 +04:00
static void
2013-10-07 16:14:38 +04:00
handle_signal ( struct ksignal * ksig , struct pt_regs * regs )
2009-03-27 16:25:14 +03:00
{
2012-05-02 17:59:21 +04:00
sigset_t * oldset = sigmask_to_save ( ) ;
2012-05-11 04:58:04 +04:00
int ret ;
2009-03-27 16:25:14 +03:00
/* Set up the stack frame */
2013-10-07 16:14:38 +04:00
ret = setup_rt_frame ( ksig , oldset , regs ) ;
2012-05-11 04:58:04 +04:00
2013-10-07 16:14:38 +04:00
signal_setup_done ( ret , ksig , test_thread_flag ( TIF_SINGLESTEP ) ) ;
2009-03-27 16:25:14 +03: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 .
*/
2012-04-24 10:21:18 +04:00
static void do_signal ( struct pt_regs * regs , int in_syscall )
2009-03-27 16:25:14 +03:00
{
2013-10-07 16:14:38 +04:00
struct ksignal ksig ;
2009-03-27 16:25:14 +03:00
# ifdef DEBUG_SIG
2012-12-27 13:40:38 +04:00
pr_info ( " do signal: %p %d \n " , regs , in_syscall ) ;
pr_info ( " do signal2: %lx %lx %ld [%lx] \n " , regs - > pc , regs - > r1 ,
2009-03-27 16:25:14 +03:00
regs - > r12 , current_thread_info ( ) - > flags ) ;
# endif
2013-10-07 16:14:38 +04:00
if ( get_signal ( & ksig ) ) {
2009-03-27 16:25:14 +03:00
/* Whee! Actually deliver the signal. */
if ( in_syscall )
2013-10-07 16:14:38 +04:00
handle_restart ( regs , & ksig . ka , 1 ) ;
handle_signal ( & ksig , regs ) ;
2012-04-24 10:21:18 +04:00
return ;
2009-03-27 16:25:14 +03:00
}
if ( in_syscall )
handle_restart ( regs , NULL , 0 ) ;
2009-06-18 21:55:29 +04:00
/*
* If there ' s no signal to deliver , we just put the saved sigmask
* back .
*/
2012-05-22 07:33:55 +04:00
restore_saved_sigmask ( ) ;
2009-03-27 16:25:14 +03:00
}
2012-04-24 10:03:06 +04:00
2012-12-13 21:03:50 +04:00
asmlinkage void do_notify_resume ( struct pt_regs * regs , int in_syscall )
2012-04-24 10:03:06 +04:00
{
if ( test_thread_flag ( TIF_SIGPENDING ) )
2012-04-24 10:21:18 +04:00
do_signal ( regs , in_syscall ) ;
2012-04-24 10:03:06 +04:00
2012-05-23 22:44:37 +04:00
if ( test_and_clear_thread_flag ( TIF_NOTIFY_RESUME ) )
2012-04-24 10:03:06 +04:00
tracehook_notify_resume ( regs ) ;
}