2008-02-08 04:19:31 -08:00
/* MN10300 Signal handling
*
* Copyright ( C ) 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , 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/tty.h>
# include <linux/personality.h>
# include <linux/suspend.h>
2009-06-11 13:08:37 +01:00
# include <linux/tracehook.h>
2008-02-08 04:19:31 -08:00
# include <asm/cacheflush.h>
# include <asm/ucontext.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2008-02-08 04:19:31 -08:00
# include <asm/fpu.h>
# include "sigframe.h"
# define DEBUG_SIG 0
/*
* do a signal return ; undo the signal stack .
*/
static int restore_sigcontext ( struct pt_regs * regs ,
struct sigcontext __user * sc , long * _d0 )
{
unsigned int err = 0 ;
2010-09-26 19:28:52 +01:00
/* Always make any pending restarted system calls return -EINTR */
2015-02-12 15:01:14 -08:00
current - > restart_block . fn = do_no_restart_syscall ;
2010-09-26 19:28:52 +01:00
2008-02-08 04:19:31 -08:00
if ( is_using_fpu ( current ) )
fpu_kill_state ( current ) ;
# define COPY(x) err |= __get_user(regs->x, &sc->x)
COPY ( d1 ) ; COPY ( d2 ) ; COPY ( d3 ) ;
COPY ( a0 ) ; COPY ( a1 ) ; COPY ( a2 ) ; COPY ( a3 ) ;
COPY ( e0 ) ; COPY ( e1 ) ; COPY ( e2 ) ; COPY ( e3 ) ;
COPY ( e4 ) ; COPY ( e5 ) ; COPY ( e6 ) ; COPY ( e7 ) ;
COPY ( lar ) ; COPY ( lir ) ;
COPY ( mdr ) ; COPY ( mdrq ) ;
COPY ( mcvf ) ; COPY ( mcrl ) ; COPY ( mcrh ) ;
COPY ( sp ) ; COPY ( pc ) ;
# undef COPY
{
unsigned int tmpflags ;
# ifndef CONFIG_MN10300_USING_JTAG
# define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
EPSW_T | EPSW_nAR )
# else
# define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
EPSW_nAR )
# endif
err | = __get_user ( tmpflags , & sc - > epsw ) ;
regs - > epsw = ( regs - > epsw & ~ USER_EPSW ) |
( tmpflags & USER_EPSW ) ;
regs - > orig_d0 = - 1 ; /* disable syscall checks */
}
{
struct fpucontext * buf ;
err | = __get_user ( buf , & sc - > fpucontext ) ;
if ( buf ) {
2016-09-03 18:05:00 -04:00
if ( ! access_ok ( VERIFY_READ , buf , sizeof ( * buf ) ) )
2008-02-08 04:19:31 -08:00
goto badframe ;
err | = fpu_restore_sigcontext ( buf ) ;
}
}
err | = __get_user ( * _d0 , & sc - > d0 ) ;
return err ;
badframe :
return 1 ;
}
/*
* standard signal return syscall
*/
asmlinkage long sys_sigreturn ( void )
{
2010-10-27 17:29:01 +01:00
struct sigframe __user * frame ;
2008-02-08 04:19:31 -08:00
sigset_t set ;
long d0 ;
2010-10-27 17:29:01 +01:00
frame = ( struct sigframe __user * ) current_frame ( ) - > sp ;
2016-09-03 18:05:00 -04:00
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
2008-02-08 04:19:31 -08:00
goto badframe ;
if ( __get_user ( set . sig [ 0 ] , & frame - > sc . oldmask ) )
goto badframe ;
if ( _NSIG_WORDS > 1 & &
__copy_from_user ( & set . sig [ 1 ] , & frame - > extramask ,
sizeof ( frame - > extramask ) ) )
goto badframe ;
2012-05-11 10:57:59 +10:00
set_current_blocked ( & set ) ;
2008-02-08 04:19:31 -08:00
2010-10-27 17:29:01 +01:00
if ( restore_sigcontext ( current_frame ( ) , & frame - > sc , & d0 ) )
2008-02-08 04:19:31 -08:00
goto badframe ;
return d0 ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
/*
* realtime signal return syscall
*/
asmlinkage long sys_rt_sigreturn ( void )
{
2010-10-27 17:29:01 +01:00
struct rt_sigframe __user * frame ;
2008-02-08 04:19:31 -08:00
sigset_t set ;
2010-10-27 17:29:01 +01:00
long d0 ;
2008-02-08 04:19:31 -08:00
2010-10-27 17:29:01 +01:00
frame = ( struct rt_sigframe __user * ) current_frame ( ) - > sp ;
2016-09-03 18:05:00 -04:00
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
2008-02-08 04:19:31 -08:00
goto badframe ;
if ( __copy_from_user ( & set , & frame - > uc . uc_sigmask , sizeof ( set ) ) )
goto badframe ;
2012-05-11 10:57:59 +10:00
set_current_blocked ( & set ) ;
2008-02-08 04:19:31 -08:00
2010-10-27 17:29:01 +01:00
if ( restore_sigcontext ( current_frame ( ) , & frame - > uc . uc_mcontext , & d0 ) )
2008-02-08 04:19:31 -08:00
goto badframe ;
2012-12-23 03:17:06 -05:00
if ( restore_altstack ( & frame - > uc . uc_stack ) )
2008-02-08 04:19:31 -08:00
goto badframe ;
return d0 ;
badframe :
force_sig ( SIGSEGV , current ) ;
return 0 ;
}
/*
* store the userspace context into a signal frame
*/
static int setup_sigcontext ( struct sigcontext __user * sc ,
struct fpucontext * fpuctx ,
struct pt_regs * regs ,
unsigned long mask )
{
int tmp , err = 0 ;
# define COPY(x) err |= __put_user(regs->x, &sc->x)
COPY ( d0 ) ; COPY ( d1 ) ; COPY ( d2 ) ; COPY ( d3 ) ;
COPY ( a0 ) ; COPY ( a1 ) ; COPY ( a2 ) ; COPY ( a3 ) ;
COPY ( e0 ) ; COPY ( e1 ) ; COPY ( e2 ) ; COPY ( e3 ) ;
COPY ( e4 ) ; COPY ( e5 ) ; COPY ( e6 ) ; COPY ( e7 ) ;
COPY ( lar ) ; COPY ( lir ) ;
COPY ( mdr ) ; COPY ( mdrq ) ;
COPY ( mcvf ) ; COPY ( mcrl ) ; COPY ( mcrh ) ;
COPY ( sp ) ; COPY ( epsw ) ; COPY ( pc ) ;
# undef COPY
tmp = fpu_setup_sigcontext ( fpuctx ) ;
if ( tmp < 0 )
err = 1 ;
else
err | = __put_user ( tmp ? fpuctx : NULL , & sc - > fpucontext ) ;
/* non-iBCS2 extensions.. */
err | = __put_user ( mask , & sc - > oldmask ) ;
return err ;
}
/*
* determine which stack to use . .
*/
2014-03-05 15:36:49 +01:00
static inline void __user * get_sigframe ( struct ksignal * ksig ,
2008-02-08 04:19:31 -08:00
struct pt_regs * regs ,
size_t frame_size )
{
2014-03-05 15:36:49 +01:00
unsigned long sp = sigsp ( regs - > sp , ksig ) ;
2008-02-08 04:19:31 -08:00
return ( void __user * ) ( ( sp - frame_size ) & ~ 7UL ) ;
}
/*
* set up a normal signal frame
*/
2013-10-07 14:22:50 +02:00
static int setup_frame ( struct ksignal * ksig , sigset_t * set ,
2008-02-08 04:19:31 -08:00
struct pt_regs * regs )
{
struct sigframe __user * frame ;
2014-07-13 17:28:15 +02:00
int sig = ksig - > sig ;
2008-02-08 04:19:31 -08:00
2014-03-05 15:36:49 +01:00
frame = get_sigframe ( ksig , regs , sizeof ( * frame ) ) ;
2008-02-08 04:19:31 -08:00
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
2014-07-13 17:28:15 +02:00
if ( __put_user ( sig , & frame - > sig ) < 0 | |
2008-02-08 04:19:31 -08:00
__put_user ( & frame - > sc , & frame - > psc ) < 0 )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
if ( setup_sigcontext ( & frame - > sc , & frame - > fpuctx , regs , set - > sig [ 0 ] ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
if ( _NSIG_WORDS > 1 ) {
if ( __copy_to_user ( frame - > extramask , & set - > sig [ 1 ] ,
sizeof ( frame - > extramask ) ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
}
/* set up to return from userspace. If provided, use a stub already in
* userspace */
2013-10-07 14:22:50 +02:00
if ( ksig - > ka . sa . sa_flags & SA_RESTORER ) {
if ( __put_user ( ksig - > ka . sa . sa_restorer , & frame - > pretcode ) )
return - EFAULT ;
2008-02-08 04:19:31 -08:00
} else {
if ( __put_user ( ( void ( * ) ( void ) ) frame - > retcode ,
& frame - > pretcode ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
/* this is mov $,d0; syscall 0 */
if ( __put_user ( 0x2c , ( char * ) ( frame - > retcode + 0 ) ) | |
__put_user ( __NR_sigreturn , ( char * ) ( frame - > retcode + 1 ) ) | |
__put_user ( 0x00 , ( char * ) ( frame - > retcode + 2 ) ) | |
__put_user ( 0xf0 , ( char * ) ( frame - > retcode + 3 ) ) | |
__put_user ( 0xe0 , ( char * ) ( frame - > retcode + 4 ) ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
flush_icache_range ( ( unsigned long ) frame - > retcode ,
( unsigned long ) frame - > retcode + 5 ) ;
}
/* set up registers for signal handler */
regs - > sp = ( unsigned long ) frame ;
2013-10-07 14:22:50 +02:00
regs - > pc = ( unsigned long ) ksig - > ka . sa . sa_handler ;
2008-02-08 04:19:31 -08:00
regs - > d0 = sig ;
regs - > d1 = ( unsigned long ) & frame - > sc ;
# if DEBUG_SIG
printk ( KERN_DEBUG " SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p \n " ,
sig , current - > comm , current - > pid , frame , regs - > pc ,
frame - > pretcode ) ;
# endif
return 0 ;
}
/*
* set up a realtime signal frame
*/
2013-10-07 14:22:50 +02:00
static int setup_rt_frame ( struct ksignal * ksig , sigset_t * set ,
struct pt_regs * regs )
2008-02-08 04:19:31 -08:00
{
struct rt_sigframe __user * frame ;
2014-07-13 17:28:15 +02:00
int sig = ksig - > sig ;
2008-02-08 04:19:31 -08:00
2014-03-05 15:36:49 +01:00
frame = get_sigframe ( ksig , regs , sizeof ( * frame ) ) ;
2008-02-08 04:19:31 -08:00
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
2014-07-13 17:28:15 +02:00
if ( __put_user ( sig , & frame - > sig ) | |
2008-02-08 04:19:31 -08:00
__put_user ( & frame - > info , & frame - > pinfo ) | |
__put_user ( & frame - > uc , & frame - > puc ) | |
2013-10-07 14:22:50 +02:00
copy_siginfo_to_user ( & frame - > info , & ksig - > info ) )
return - EFAULT ;
2008-02-08 04:19:31 -08:00
/* create the ucontext. */
if ( __put_user ( 0 , & frame - > uc . uc_flags ) | |
__put_user ( 0 , & frame - > uc . uc_link ) | |
2012-12-23 03:17:06 -05:00
__save_altstack ( & frame - > uc . uc_stack , regs - > sp ) | |
2008-02-08 04:19:31 -08:00
setup_sigcontext ( & frame - > uc . uc_mcontext ,
& frame - > fpuctx , regs , set - > sig [ 0 ] ) | |
__copy_to_user ( & frame - > uc . uc_sigmask , set , sizeof ( * set ) ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
/* set up to return from userspace. If provided, use a stub already in
* userspace */
2013-10-07 14:22:50 +02:00
if ( ksig - > ka . sa . sa_flags & SA_RESTORER ) {
if ( __put_user ( ksig - > ka . sa . sa_restorer , & frame - > pretcode ) )
return - EFAULT ;
2008-02-08 04:19:31 -08:00
} else {
if ( __put_user ( ( void ( * ) ( void ) ) frame - > retcode ,
& frame - > pretcode ) | |
/* This is mov $,d0; syscall 0 */
__put_user ( 0x2c , ( char * ) ( frame - > retcode + 0 ) ) | |
__put_user ( __NR_rt_sigreturn ,
( char * ) ( frame - > retcode + 1 ) ) | |
__put_user ( 0x00 , ( char * ) ( frame - > retcode + 2 ) ) | |
__put_user ( 0xf0 , ( char * ) ( frame - > retcode + 3 ) ) | |
__put_user ( 0xe0 , ( char * ) ( frame - > retcode + 4 ) ) )
2013-10-07 14:22:50 +02:00
return - EFAULT ;
2008-02-08 04:19:31 -08:00
flush_icache_range ( ( u_long ) frame - > retcode ,
( u_long ) frame - > retcode + 5 ) ;
}
/* Set up registers for signal handler */
regs - > sp = ( unsigned long ) frame ;
2013-10-07 14:22:50 +02:00
regs - > pc = ( unsigned long ) ksig - > ka . sa . sa_handler ;
2008-02-08 04:19:31 -08:00
regs - > d0 = sig ;
regs - > d1 = ( long ) & frame - > info ;
# if DEBUG_SIG
printk ( KERN_DEBUG " SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p \n " ,
sig , current - > comm , current - > pid , frame , regs - > pc ,
frame - > pretcode ) ;
# endif
return 0 ;
}
2010-09-26 19:28:42 +01:00
static inline void stepback ( struct pt_regs * regs )
{
regs - > pc - = 2 ;
regs - > orig_d0 = - 1 ;
}
2008-02-08 04:19:31 -08:00
/*
* handle the actual delivery of a signal to userspace
*/
2013-10-07 14:22:50 +02:00
static int handle_signal ( struct ksignal * ksig , struct pt_regs * regs )
2008-02-08 04:19:31 -08:00
{
2012-05-02 09:59:21 -04:00
sigset_t * oldset = sigmask_to_save ( ) ;
2008-02-08 04:19:31 -08:00
int ret ;
/* Are we from a system call? */
if ( regs - > orig_d0 > = 0 ) {
/* If so, check system call restarting.. */
switch ( regs - > d0 ) {
case - ERESTART_RESTARTBLOCK :
case - ERESTARTNOHAND :
regs - > d0 = - EINTR ;
break ;
case - ERESTARTSYS :
2013-10-07 14:22:50 +02:00
if ( ! ( ksig - > ka . sa . sa_flags & SA_RESTART ) ) {
2008-02-08 04:19:31 -08:00
regs - > d0 = - EINTR ;
break ;
}
/* fallthrough */
case - ERESTARTNOINTR :
regs - > d0 = regs - > orig_d0 ;
2010-09-26 19:28:42 +01:00
stepback ( regs ) ;
2008-02-08 04:19:31 -08:00
}
}
/* Set up the stack frame */
2013-10-07 14:22:50 +02:00
if ( ksig - > ka . sa . sa_flags & SA_SIGINFO )
ret = setup_rt_frame ( ksig , oldset , regs ) ;
2008-02-08 04:19:31 -08:00
else
2013-10-07 14:22:50 +02:00
ret = setup_frame ( ksig , oldset , regs ) ;
2008-02-08 04:19:31 -08:00
2013-10-07 14:22:50 +02:00
signal_setup_done ( ret , ksig , test_thread_flag ( TIF_SINGLESTEP ) ) ;
2012-07-17 15:47:54 -07:00
return 0 ;
2008-02-08 04:19:31 -08:00
}
/*
* handle a potential signal
*/
static void do_signal ( struct pt_regs * regs )
{
2013-10-07 14:22:50 +02:00
struct ksignal ksig ;
2008-02-08 04:19:31 -08:00
2013-10-07 14:22:50 +02:00
if ( get_signal ( & ksig ) ) {
handle_signal ( & ksig , regs ) ;
2008-02-08 04:19:31 -08:00
return ;
}
/* did we come from a system call? */
if ( regs - > orig_d0 > = 0 ) {
/* restart the system call - no handlers present */
switch ( regs - > d0 ) {
case - ERESTARTNOHAND :
case - ERESTARTSYS :
case - ERESTARTNOINTR :
regs - > d0 = regs - > orig_d0 ;
2010-09-26 19:28:42 +01:00
stepback ( regs ) ;
2008-02-08 04:19:31 -08:00
break ;
case - ERESTART_RESTARTBLOCK :
regs - > d0 = __NR_restart_syscall ;
2010-09-26 19:28:42 +01:00
stepback ( regs ) ;
2008-02-08 04:19:31 -08:00
break ;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
2012-05-21 23:33:55 -04:00
restore_saved_sigmask ( ) ;
2008-02-08 04:19:31 -08:00
}
/*
* notification of userspace execution resumption
* - triggered by current - > work . notify_resume
*/
asmlinkage void do_notify_resume ( struct pt_regs * regs , u32 thread_info_flags )
{
/* Pending single-step? */
if ( thread_info_flags & _TIF_SINGLESTEP ) {
# ifndef CONFIG_MN10300_USING_JTAG
regs - > epsw | = EPSW_T ;
clear_thread_flag ( TIF_SINGLESTEP ) ;
# else
BUG ( ) ; /* no h/w single-step if using JTAG unit */
# endif
}
/* deal with pending signal delivery */
2012-05-23 15:28:58 -04:00
if ( thread_info_flags & _TIF_SIGPENDING )
2008-02-08 04:19:31 -08:00
do_signal ( regs ) ;
2009-06-11 13:08:37 +01:00
if ( thread_info_flags & _TIF_NOTIFY_RESUME ) {
clear_thread_flag ( TIF_NOTIFY_RESUME ) ;
2010-10-27 17:29:01 +01:00
tracehook_notify_resume ( current_frame ( ) ) ;
2009-06-11 13:08:37 +01:00
}
2008-02-08 04:19:31 -08:00
}