2011-11-01 03:41:21 +04:00
/*
* Signal support for Hexagon processor
*
2013-03-29 05:45:40 +04:00
* Copyright ( c ) 2010 - 2012 , The Linux Foundation . All rights reserved .
2011-11-01 03:41:21 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
*/
# include <linux/linkage.h>
# include <linux/syscalls.h>
# include <linux/tracehook.h>
# include <asm/registers.h>
# include <asm/thread_info.h>
# include <asm/unistd.h>
# include <asm/uaccess.h>
# include <asm/ucontext.h>
# include <asm/cacheflush.h>
# include <asm/signal.h>
# include <asm/vdso.h>
struct rt_sigframe {
unsigned long tramp [ 2 ] ;
struct siginfo info ;
struct ucontext uc ;
} ;
2014-03-05 18:24:06 +04:00
static void __user * get_sigframe ( struct ksignal * ksig , struct pt_regs * regs ,
2011-11-01 03:41:21 +04:00
size_t frame_size )
{
2014-03-05 18:24:06 +04:00
unsigned long sp = sigsp ( regs - > r29 , ksig ) ;
2011-11-01 03:41:21 +04:00
return ( void __user * ) ( ( sp - frame_size ) & ~ ( sizeof ( long long ) - 1 ) ) ;
}
static int setup_sigcontext ( struct pt_regs * regs , struct sigcontext __user * sc )
{
unsigned long tmp ;
int err = 0 ;
err | = copy_to_user ( & sc - > sc_regs . r0 , & regs - > r00 ,
32 * sizeof ( unsigned long ) ) ;
err | = __put_user ( regs - > sa0 , & sc - > sc_regs . sa0 ) ;
err | = __put_user ( regs - > lc0 , & sc - > sc_regs . lc0 ) ;
err | = __put_user ( regs - > sa1 , & sc - > sc_regs . sa1 ) ;
err | = __put_user ( regs - > lc1 , & sc - > sc_regs . lc1 ) ;
err | = __put_user ( regs - > m0 , & sc - > sc_regs . m0 ) ;
err | = __put_user ( regs - > m1 , & sc - > sc_regs . m1 ) ;
err | = __put_user ( regs - > usr , & sc - > sc_regs . usr ) ;
err | = __put_user ( regs - > preds , & sc - > sc_regs . p3_0 ) ;
err | = __put_user ( regs - > gp , & sc - > sc_regs . gp ) ;
err | = __put_user ( regs - > ugp , & sc - > sc_regs . ugp ) ;
2012-03-28 02:38:09 +04:00
# if CONFIG_HEXAGON_ARCH_VERSION >= 4
err | = __put_user ( regs - > cs0 , & sc - > sc_regs . cs0 ) ;
err | = __put_user ( regs - > cs1 , & sc - > sc_regs . cs1 ) ;
# endif
2011-11-01 03:41:21 +04:00
tmp = pt_elr ( regs ) ; err | = __put_user ( tmp , & sc - > sc_regs . pc ) ;
tmp = pt_cause ( regs ) ; err | = __put_user ( tmp , & sc - > sc_regs . cause ) ;
tmp = pt_badva ( regs ) ; err | = __put_user ( tmp , & sc - > sc_regs . badva ) ;
return err ;
}
static int restore_sigcontext ( struct pt_regs * regs ,
struct sigcontext __user * sc )
{
unsigned long tmp ;
int err = 0 ;
err | = copy_from_user ( & regs - > r00 , & sc - > sc_regs . r0 ,
32 * sizeof ( unsigned long ) ) ;
err | = __get_user ( regs - > sa0 , & sc - > sc_regs . sa0 ) ;
err | = __get_user ( regs - > lc0 , & sc - > sc_regs . lc0 ) ;
err | = __get_user ( regs - > sa1 , & sc - > sc_regs . sa1 ) ;
err | = __get_user ( regs - > lc1 , & sc - > sc_regs . lc1 ) ;
err | = __get_user ( regs - > m0 , & sc - > sc_regs . m0 ) ;
err | = __get_user ( regs - > m1 , & sc - > sc_regs . m1 ) ;
err | = __get_user ( regs - > usr , & sc - > sc_regs . usr ) ;
err | = __get_user ( regs - > preds , & sc - > sc_regs . p3_0 ) ;
err | = __get_user ( regs - > gp , & sc - > sc_regs . gp ) ;
err | = __get_user ( regs - > ugp , & sc - > sc_regs . ugp ) ;
2012-03-28 02:38:09 +04:00
# if CONFIG_HEXAGON_ARCH_VERSION >= 4
err | = __get_user ( regs - > cs0 , & sc - > sc_regs . cs0 ) ;
err | = __get_user ( regs - > cs1 , & sc - > sc_regs . cs1 ) ;
# endif
2011-11-01 03:41:21 +04:00
err | = __get_user ( tmp , & sc - > sc_regs . pc ) ; pt_set_elr ( regs , tmp ) ;
return err ;
}
/*
* Setup signal stack frame with siginfo structure
*/
2013-10-07 15:50:08 +04:00
static int setup_rt_frame ( struct ksignal * ksig , sigset_t * set ,
struct pt_regs * regs )
2011-11-01 03:41:21 +04:00
{
int err = 0 ;
struct rt_sigframe __user * frame ;
struct hexagon_vdso * vdso = current - > mm - > context . vdso ;
2014-03-05 18:24:06 +04:00
frame = get_sigframe ( ksig , regs , sizeof ( struct rt_sigframe ) ) ;
2011-11-01 03:41:21 +04:00
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( struct rt_sigframe ) ) )
2013-10-07 15:50:08 +04:00
return - EFAULT ;
2011-11-01 03:41:21 +04:00
2013-10-07 15:50:08 +04:00
if ( copy_siginfo_to_user ( & frame - > info , & ksig - > info ) )
return - EFAULT ;
2011-11-01 03:41:21 +04:00
/* The on-stack signal trampoline is no longer executed;
* however , the libgcc signal frame unwinding code checks for
* the presence of these two numeric magic values .
*/
err | = __put_user ( 0x7800d166 , & frame - > tramp [ 0 ] ) ;
err | = __put_user ( 0x5400c004 , & frame - > tramp [ 1 ] ) ;
err | = setup_sigcontext ( regs , & frame - > uc . uc_mcontext ) ;
err | = __copy_to_user ( & frame - > uc . uc_sigmask , set , sizeof ( * set ) ) ;
2012-12-23 11:46:34 +04:00
err | = __save_altstack ( & frame - > uc . uc_stack , user_stack_pointer ( regs ) ) ;
2011-11-01 03:41:21 +04:00
if ( err )
2013-10-07 15:50:08 +04:00
return - EFAULT ;
2011-11-01 03:41:21 +04:00
/* Load r0/r1 pair with signumber/siginfo pointer... */
regs - > r0100 = ( ( unsigned long long ) ( ( unsigned long ) & frame - > info ) < < 32 )
2013-10-07 15:50:08 +04:00
| ( unsigned long long ) ksig - > sig ;
2011-11-01 03:41:21 +04:00
regs - > r02 = ( unsigned long ) & frame - > uc ;
regs - > r31 = ( unsigned long ) vdso - > rt_signal_trampoline ;
pt_psp ( regs ) = ( unsigned long ) frame ;
2013-10-07 15:50:08 +04:00
pt_set_elr ( regs , ( unsigned long ) ksig - > ka . sa . sa_handler ) ;
2011-11-01 03:41:21 +04:00
return 0 ;
}
/*
* Setup invocation of signal handler
*/
2013-10-07 15:50:08 +04:00
static void handle_signal ( struct ksignal * ksig , struct pt_regs * regs )
2011-11-01 03:41:21 +04:00
{
2013-10-07 15:50:08 +04:00
int ret ;
2011-11-01 03:41:21 +04:00
/*
* If we ' re handling a signal that aborted a system call ,
* set up the error return value before adding the signal
* frame to the stack .
*/
if ( regs - > syscall_nr > = 0 ) {
switch ( regs - > r00 ) {
case - ERESTART_RESTARTBLOCK :
case - ERESTARTNOHAND :
regs - > r00 = - EINTR ;
break ;
case - ERESTARTSYS :
2013-10-07 15:50:08 +04:00
if ( ! ( ksig - > ka . sa . sa_flags & SA_RESTART ) ) {
2011-11-01 03:41:21 +04:00
regs - > r00 = - EINTR ;
break ;
}
/* Fall through */
case - ERESTARTNOINTR :
regs - > r06 = regs - > syscall_nr ;
pt_set_elr ( regs , pt_elr ( regs ) - 4 ) ;
regs - > r00 = regs - > restart_r0 ;
break ;
default :
break ;
}
}
/*
* Set up the stack frame ; not doing the SA_SIGINFO thing . We
* only set up the rt_frame flavor .
*/
/* If there was an error on setup, no signal was delivered. */
2013-10-07 15:50:08 +04:00
ret = setup_rt_frame ( ksig , sigmask_to_save ( ) , regs ) ;
2011-11-01 03:41:21 +04:00
2013-10-07 15:50:08 +04:00
signal_setup_done ( ret , ksig , test_thread_flag ( TIF_SINGLESTEP ) ) ;
2011-11-01 03:41:21 +04:00
}
/*
* Called from return - from - event code .
*/
2012-05-30 02:23:14 +04:00
void do_signal ( struct pt_regs * regs )
2011-11-01 03:41:21 +04:00
{
2013-10-07 15:50:08 +04:00
struct ksignal ksig ;
2011-11-01 03:41:21 +04:00
if ( ! user_mode ( regs ) )
return ;
2013-10-07 15:50:08 +04:00
if ( get_signal ( & ksig ) ) {
handle_signal ( & ksig , regs ) ;
2011-11-01 03:41:21 +04:00
return ;
}
/*
2012-05-30 02:23:14 +04:00
* No ( more ) signals ; if we came from a system call , handle the restart .
2011-11-01 03:41:21 +04:00
*/
2012-05-30 02:23:14 +04:00
2011-11-01 03:41:21 +04:00
if ( regs - > syscall_nr > = 0 ) {
switch ( regs - > r00 ) {
case - ERESTARTNOHAND :
case - ERESTARTSYS :
case - ERESTARTNOINTR :
regs - > r06 = regs - > syscall_nr ;
break ;
case - ERESTART_RESTARTBLOCK :
regs - > r06 = __NR_restart_syscall ;
break ;
default :
goto no_restart ;
}
pt_set_elr ( regs , pt_elr ( regs ) - 4 ) ;
regs - > r00 = regs - > restart_r0 ;
}
no_restart :
/* If there's no signal to deliver, put the saved sigmask back */
2012-05-22 07:33:55 +04:00
restore_saved_sigmask ( ) ;
2011-11-01 03:41:21 +04:00
}
/*
* Architecture - specific wrappers for signal - related system calls
*/
asmlinkage int sys_rt_sigreturn ( void )
{
2012-10-19 06:45:24 +04:00
struct pt_regs * regs = current_pt_regs ( ) ;
2011-11-01 03:41:21 +04:00
struct rt_sigframe __user * frame ;
sigset_t blocked ;
2012-04-22 11:31:24 +04:00
/* Always make any pending restarted system calls return -EINTR */
2015-02-13 02:01:14 +03:00
current - > restart_block . fn = do_no_restart_syscall ;
2012-04-22 11:31:24 +04:00
2011-11-01 03:41:21 +04:00
frame = ( struct rt_sigframe __user * ) pt_psp ( regs ) ;
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & blocked , & frame - > uc . uc_sigmask , sizeof ( blocked ) ) )
goto badframe ;
2012-03-24 02:02:43 +04:00
set_current_blocked ( & blocked ) ;
2011-11-01 03:41:21 +04:00
if ( restore_sigcontext ( regs , & frame - > uc . uc_mcontext ) )
goto badframe ;
/* Restore the user's stack as well */
pt_psp ( regs ) = regs - > r29 ;
2012-05-30 02:23:14 +04:00
regs - > syscall_nr = - 1 ;
2011-11-01 03:41:21 +04:00
2012-12-23 11:43:08 +04:00
if ( restore_altstack ( & frame - > uc . uc_stack ) )
2011-11-01 03:41:21 +04:00
goto badframe ;
2012-05-30 02:23:14 +04:00
return regs - > r00 ;
2011-11-01 03:41:21 +04:00
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}