2011-06-04 22:38:59 +04:00
/*
* OpenRISC signal . c
*
* Linux architectural port borrowing liberally from similar works of
* others . All original copyrights apply as per the original source
* declaration .
*
* Modifications for the OpenRISC architecture :
* Copyright ( C ) 2003 Matjaz Breskvar < phoenix @ bsemi . com >
* Copyright ( C ) 2010 - 2011 Jonas Bonn < jonas @ southpole . se >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# 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/tracehook.h>
# include <asm/processor.h>
2012-02-19 20:36:53 +04:00
# include <asm/syscall.h>
2011-06-04 22:38:59 +04:00
# include <asm/ucontext.h>
# include <asm/uaccess.h>
# define DEBUG_SIG 0
struct rt_sigframe {
struct siginfo info ;
struct ucontext uc ;
unsigned char retcode [ 16 ] ; /* trampoline code */
} ;
2012-02-19 20:36:53 +04:00
static int restore_sigcontext ( struct pt_regs * regs ,
struct sigcontext __user * sc )
2011-06-04 22:38:59 +04:00
{
2012-02-19 20:36:53 +04:00
int err = 0 ;
2011-06-04 22:38:59 +04:00
2012-02-19 20:36:53 +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 ;
2011-06-04 22:38:59 +04:00
2011-07-30 18:15:42 +04:00
/*
* Restore the regs from & sc - > regs .
2011-06-04 22:38:59 +04:00
* ( sc is already checked for VERIFY_READ since the sigframe was
* checked in sys_sigreturn previously )
*/
2012-02-19 20:36:53 +04:00
err | = __copy_from_user ( regs , sc - > regs . gpr , 32 * sizeof ( unsigned long ) ) ;
err | = __copy_from_user ( & regs - > pc , & sc - > regs . pc , sizeof ( unsigned long ) ) ;
err | = __copy_from_user ( & regs - > sr , & sc - > regs . sr , sizeof ( unsigned long ) ) ;
2011-06-04 22:38:59 +04:00
/* make sure the SM-bit is cleared so user-mode cannot fool us */
regs - > sr & = ~ SPR_SR_SM ;
2012-02-19 20:36:53 +04:00
regs - > orig_gpr11 = - 1 ; /* Avoid syscall restart checks */
2011-06-04 22:38:59 +04:00
/* TODO: the other ports use regs->orig_XX to disable syscall checks
* after this completes , but we don ' t use that mechanism . maybe we can
* use it now ?
*/
return err ;
}
asmlinkage long _sys_rt_sigreturn ( struct pt_regs * regs )
{
struct rt_sigframe * frame = ( struct rt_sigframe __user * ) regs - > sp ;
sigset_t set ;
/*
* Since we stacked the signal on a dword boundary ,
* then frame should be dword aligned here . If it ' s
* not , then the user is trying to mess with us .
*/
if ( ( ( long ) frame ) & 3 )
goto badframe ;
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set , & frame - > uc . uc_sigmask , sizeof ( set ) ) )
goto badframe ;
2012-02-14 15:40:57 +04:00
set_current_blocked ( & set ) ;
2011-06-04 22:38:59 +04:00
if ( restore_sigcontext ( regs , & frame - > uc . uc_mcontext ) )
goto badframe ;
2012-12-23 12:19:49 +04:00
if ( restore_altstack ( & frame - > uc . uc_stack ) )
2012-09-20 23:49:09 +04:00
goto badframe ;
2011-06-04 22:38:59 +04:00
return regs - > gpr [ 11 ] ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
/*
* Set up a signal frame .
*/
2012-02-19 20:36:53 +04:00
static int setup_sigcontext ( struct pt_regs * regs , struct sigcontext __user * sc )
2011-06-04 22:38:59 +04:00
{
int err = 0 ;
2011-07-30 18:15:42 +04:00
/* copy the regs */
2012-02-19 20:36:53 +04:00
/* There should be no need to save callee-saved registers here...
* . . . but we save them anyway . Revisit this
*/
2011-07-30 18:15:42 +04:00
err | = __copy_to_user ( sc - > regs . gpr , regs , 32 * sizeof ( unsigned long ) ) ;
err | = __copy_to_user ( & sc - > regs . pc , & regs - > pc , sizeof ( unsigned long ) ) ;
err | = __copy_to_user ( & sc - > regs . sr , & regs - > sr , sizeof ( unsigned long ) ) ;
2011-06-04 22:38:59 +04:00
return err ;
}
static inline unsigned long align_sigframe ( unsigned long sp )
{
return sp & ~ 3UL ;
}
/*
* Work out where the signal frame should go . It ' s either on the user stack
* or the alternate stack .
*/
2014-03-05 18:49:40 +04:00
static inline void __user * get_sigframe ( struct ksignal * ksig ,
2011-06-04 22:38:59 +04:00
struct pt_regs * regs , size_t frame_size )
{
unsigned long sp = regs - > sp ;
/* redzone */
sp - = STACK_FRAME_OVERHEAD ;
2014-03-05 18:49:40 +04:00
sp = sigsp ( sp , ksig ) ;
2011-06-04 22:38:59 +04:00
sp = align_sigframe ( sp - frame_size ) ;
return ( void __user * ) sp ;
}
/* grab and setup a signal frame.
*
* basically we stack a lot of state info , and arrange for the
* user - mode program to return to the kernel using either a
* trampoline which performs the syscall sigreturn , or a provided
* user - mode trampoline .
*/
2013-10-07 00:35:03 +04:00
static int setup_rt_frame ( struct ksignal * ksig , sigset_t * set ,
struct pt_regs * regs )
2011-06-04 22:38:59 +04:00
{
struct rt_sigframe * frame ;
unsigned long return_ip ;
int err = 0 ;
2014-03-05 18:49:40 +04:00
frame = get_sigframe ( ksig , regs , sizeof ( * frame ) ) ;
2013-10-07 00:35:03 +04:00
2011-06-04 22:38:59 +04:00
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
2013-10-07 00:35:03 +04:00
return - EFAULT ;
2011-06-04 22:38:59 +04:00
2012-02-19 20:36:53 +04:00
/* Create siginfo. */
2013-10-07 00:35:03 +04:00
if ( ksig - > ka . sa . sa_flags & SA_SIGINFO )
err | = copy_siginfo_to_user ( & frame - > info , & ksig - > info ) ;
2011-06-04 22:38:59 +04:00
2012-02-19 20:36:53 +04:00
/* Create the ucontext. */
2011-06-04 22:38:59 +04:00
err | = __put_user ( 0 , & frame - > uc . uc_flags ) ;
err | = __put_user ( NULL , & frame - > uc . uc_link ) ;
2012-12-23 12:19:49 +04:00
err | = __save_altstack ( & frame - > uc . uc_stack , regs - > sp ) ;
2012-02-19 20:36:53 +04:00
err | = setup_sigcontext ( regs , & frame - > uc . uc_mcontext ) ;
2011-06-04 22:38:59 +04:00
err | = __copy_to_user ( & frame - > uc . uc_sigmask , set , sizeof ( * set ) ) ;
if ( err )
2013-10-07 00:35:03 +04:00
return - EFAULT ;
2011-06-04 22:38:59 +04:00
/* trampoline - the desired return ip is the retcode itself */
return_ip = ( unsigned long ) & frame - > retcode ;
2012-02-19 20:36:53 +04:00
/* This is:
l . ori r11 , r0 , __NR_sigreturn
l . sys 1
*/
err | = __put_user ( 0xa960 , ( short * ) ( frame - > retcode + 0 ) ) ;
err | = __put_user ( __NR_rt_sigreturn , ( short * ) ( frame - > retcode + 2 ) ) ;
2011-06-04 22:38:59 +04:00
err | = __put_user ( 0x20000001 , ( unsigned long * ) ( frame - > retcode + 4 ) ) ;
err | = __put_user ( 0x15000000 , ( unsigned long * ) ( frame - > retcode + 8 ) ) ;
if ( err )
2013-10-07 00:35:03 +04:00
return - EFAULT ;
2011-06-04 22:38:59 +04:00
/* TODO what is the current->exec_domain stuff and invmap ? */
/* Set up registers for signal handler */
2013-10-07 00:35:03 +04:00
regs - > pc = ( unsigned long ) ksig - > ka . sa . sa_handler ; /* what we enter NOW */
2011-06-04 22:38:59 +04:00
regs - > gpr [ 9 ] = ( unsigned long ) return_ip ; /* what we enter LATER */
2013-10-07 00:35:03 +04:00
regs - > gpr [ 3 ] = ( unsigned long ) ksig - > sig ; /* arg 1: signo */
2011-06-04 22:38:59 +04:00
regs - > gpr [ 4 ] = ( unsigned long ) & frame - > info ; /* arg 2: (siginfo_t*) */
regs - > gpr [ 5 ] = ( unsigned long ) & frame - > uc ; /* arg 3: ucontext */
/* actually move the usp to reflect the stacked frame */
regs - > sp = ( unsigned long ) frame ;
2012-02-14 15:40:56 +04:00
return 0 ;
2011-06-04 22:38:59 +04:00
}
2012-04-27 07:42:55 +04:00
static inline void
2013-10-07 00:35:03 +04:00
handle_signal ( struct ksignal * ksig , struct pt_regs * regs )
2011-06-04 22:38:59 +04:00
{
2012-02-14 15:40:56 +04:00
int ret ;
2013-10-07 00:35:03 +04:00
ret = setup_rt_frame ( ksig , sigmask_to_save ( ) , regs ) ;
2011-06-04 22:38:59 +04:00
2013-10-07 00:35:03 +04:00
signal_setup_done ( ret , ksig , test_thread_flag ( TIF_SINGLESTEP ) ) ;
2011-06-04 22:38:59 +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 .
*
* Also note that the regs structure given here as an argument , is the latest
* pushed pt_regs . It may or may not be the same as the first pushed registers
* when the initial usermode - > kernelmode transition took place . Therefore
* we can use user_mode ( regs ) to see if we came directly from kernel or user
* mode below .
*/
2012-02-19 20:36:53 +04:00
int do_signal ( struct pt_regs * regs , int syscall )
2011-06-04 22:38:59 +04:00
{
2013-10-07 00:35:03 +04:00
struct ksignal ksig ;
2012-02-19 20:36:53 +04:00
unsigned long continue_addr = 0 ;
unsigned long restart_addr = 0 ;
unsigned long retval = 0 ;
int restart = 0 ;
if ( syscall ) {
continue_addr = regs - > pc ;
restart_addr = continue_addr - 4 ;
retval = regs - > gpr [ 11 ] ;
/*
* Setup syscall restart here so that a debugger will
* see the already changed PC .
*/
switch ( retval ) {
2011-06-04 22:38:59 +04:00
case - ERESTART_RESTARTBLOCK :
2012-02-19 20:36:53 +04:00
restart = - 2 ;
/* Fall through */
2011-06-04 22:38:59 +04:00
case - ERESTARTNOHAND :
case - ERESTARTSYS :
case - ERESTARTNOINTR :
2012-02-19 20:36:53 +04:00
restart + + ;
regs - > gpr [ 11 ] = regs - > orig_gpr11 ;
regs - > pc = restart_addr ;
2011-06-04 22:38:59 +04:00
break ;
}
2012-02-19 20:36:53 +04:00
}
2011-06-04 22:38:59 +04:00
2012-02-19 20:36:53 +04:00
/*
2013-10-07 00:35:03 +04:00
* Get the signal to deliver . During the call to get_signal the
* debugger may change all our registers so we may need to revert
* the decision to restart the syscall ; specifically , if the PC is
* changed , don ' t restart the syscall .
2012-02-19 20:36:53 +04:00
*/
2013-10-07 00:35:03 +04:00
if ( get_signal ( & ksig ) ) {
2012-02-19 20:36:53 +04:00
if ( unlikely ( restart ) & & regs - > pc = = restart_addr ) {
if ( retval = = - ERESTARTNOHAND | |
retval = = - ERESTART_RESTARTBLOCK
| | ( retval = = - ERESTARTSYS
2013-10-07 00:35:03 +04:00
& & ! ( ksig . ka . sa . sa_flags & SA_RESTART ) ) ) {
2012-02-19 20:36:53 +04:00
/* No automatic restart */
regs - > gpr [ 11 ] = - EINTR ;
regs - > pc = continue_addr ;
}
2011-06-04 22:38:59 +04:00
}
2013-10-07 00:35:03 +04:00
handle_signal ( & ksig , regs ) ;
2012-02-19 20:36:53 +04:00
} else {
/* no handler */
restore_saved_sigmask ( ) ;
/*
* Restore pt_regs PC as syscall restart will be handled by
* kernel without return to userspace
*/
if ( unlikely ( restart ) & & regs - > pc = = restart_addr ) {
regs - > pc = continue_addr ;
return restart ;
}
2011-06-04 22:38:59 +04:00
}
2012-02-19 20:36:53 +04:00
return 0 ;
2011-06-04 22:38:59 +04:00
}
2012-02-19 20:36:53 +04:00
asmlinkage int
do_work_pending ( struct pt_regs * regs , unsigned int thread_flags , int syscall )
2011-06-04 22:38:59 +04:00
{
2012-02-19 20:36:53 +04:00
do {
if ( likely ( thread_flags & _TIF_NEED_RESCHED ) ) {
schedule ( ) ;
} else {
if ( unlikely ( ! user_mode ( regs ) ) )
return 0 ;
local_irq_enable ( ) ;
if ( thread_flags & _TIF_SIGPENDING ) {
int restart = do_signal ( regs , syscall ) ;
if ( unlikely ( restart ) ) {
/*
* Restart without handlers .
* Deal with it without leaving
* the kernel space .
*/
return restart ;
}
syscall = 0 ;
} else {
clear_thread_flag ( TIF_NOTIFY_RESUME ) ;
tracehook_notify_resume ( regs ) ;
}
}
local_irq_disable ( ) ;
thread_flags = current_thread_info ( ) - > flags ;
} while ( thread_flags & _TIF_WORK_MASK ) ;
return 0 ;
2011-06-04 22:38:59 +04:00
}