2011-11-01 03:41:21 +04:00
/*
* Signal support for Hexagon processor
*
2012-09-20 01:22:02 +04:00
* Copyright ( c ) 2010 - 2011 , 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 ;
} ;
static void __user * get_sigframe ( struct k_sigaction * ka , struct pt_regs * regs ,
size_t frame_size )
{
unsigned long sp = regs - > r29 ;
/* Switch to signal stack if appropriate */
if ( ( ka - > sa . sa_flags & SA_ONSTACK ) & & ( sas_ss_flags ( sp ) = = 0 ) )
sp = current - > sas_ss_sp + current - > sas_ss_size ;
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 ) ;
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 ) ;
err | = __get_user ( tmp , & sc - > sc_regs . pc ) ; pt_set_elr ( regs , tmp ) ;
return err ;
}
/*
* Setup signal stack frame with siginfo structure
*/
static int setup_rt_frame ( int signr , struct k_sigaction * ka , siginfo_t * info ,
sigset_t * set , struct pt_regs * regs )
{
int err = 0 ;
struct rt_sigframe __user * frame ;
struct hexagon_vdso * vdso = current - > mm - > context . vdso ;
frame = get_sigframe ( ka , regs , sizeof ( struct rt_sigframe ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( struct rt_sigframe ) ) )
goto sigsegv ;
if ( copy_siginfo_to_user ( & frame - > info , info ) )
goto sigsegv ;
/* 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 )
goto sigsegv ;
/* Load r0/r1 pair with signumber/siginfo pointer... */
regs - > r0100 = ( ( unsigned long long ) ( ( unsigned long ) & frame - > info ) < < 32 )
| ( unsigned long long ) signr ;
regs - > r02 = ( unsigned long ) & frame - > uc ;
regs - > r31 = ( unsigned long ) vdso - > rt_signal_trampoline ;
pt_psp ( regs ) = ( unsigned long ) frame ;
pt_set_elr ( regs , ( unsigned long ) ka - > sa . sa_handler ) ;
return 0 ;
sigsegv :
force_sigsegv ( signr , current ) ;
return - EFAULT ;
}
/*
* Setup invocation of signal handler
*/
2012-05-22 07:42:15 +04:00
static void handle_signal ( int sig , siginfo_t * info , struct k_sigaction * ka ,
2012-05-02 17:59:21 +04:00
struct pt_regs * regs )
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 :
if ( ! ( ka - > sa . sa_flags & SA_RESTART ) ) {
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. */
2012-05-22 07:42:15 +04:00
if ( setup_rt_frame ( sig , ka , info , sigmask_to_save ( ) , regs ) < 0 )
return ;
2011-11-01 03:41:21 +04:00
2012-04-28 10:04:15 +04:00
signal_delivered ( sig , info , ka , regs ,
2012-05-22 07:42:15 +04:00
test_thread_flag ( TIF_SINGLESTEP ) ) ;
2011-11-01 03:41:21 +04:00
}
/*
* Called from return - from - event code .
*/
static void do_signal ( struct pt_regs * regs )
{
struct k_sigaction sigact ;
siginfo_t info ;
int signo ;
if ( ! user_mode ( regs ) )
return ;
signo = get_signal_to_deliver ( & info , & sigact , regs , NULL ) ;
if ( signo > 0 ) {
2012-05-22 07:42:15 +04:00
handle_signal ( signo , & info , & sigact , regs ) ;
2011-11-01 03:41:21 +04:00
return ;
}
/*
* If we came from a system call , handle the restart .
*/
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
}
void do_notify_resume ( struct pt_regs * regs , unsigned long thread_info_flags )
{
if ( thread_info_flags & _TIF_SIGPENDING )
do_signal ( regs ) ;
if ( thread_info_flags & _TIF_NOTIFY_RESUME ) {
clear_thread_flag ( TIF_NOTIFY_RESUME ) ;
2012-05-11 04:59:08 +04:00
tracehook_notify_resume ( regs ) ;
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 */
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
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 ;
/*
* Leave a trace in the stack frame that this was a sigreturn .
* If the system call is to replay , we ' ve already restored the
* number in the GPR slot and it will be regenerated on the
* new system call trap entry . Note that if restore_sigcontext ( )
* did something other than a bulk copy of the pt_regs struct ,
* we could avoid this assignment by simply not overwriting
* regs - > syscall_nr .
*/
regs - > syscall_nr = __NR_rt_sigreturn ;
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 ;
return 0 ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}