2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2003 Broadcom Corporation
*
* 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 .
*
* 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 . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
2005-06-15 17:00:12 +04:00
# include <linux/cache.h>
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/smp_lock.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/compat.h>
# include <linux/bitops.h>
# include <asm/asm.h>
# include <asm/cacheflush.h>
# include <asm/sim.h>
# include <asm/uaccess.h>
# include <asm/ucontext.h>
# include <asm/system.h>
# include <asm/fpu.h>
# include <asm/cpu-features.h>
2005-06-15 17:00:12 +04:00
# include <asm/war.h>
2005-04-17 02:20:36 +04:00
# include "signal-common.h"
2006-09-30 22:52:18 +04:00
extern void sigset_from_compat ( sigset_t * set , compat_sigset_t * compat ) ;
2005-04-17 02:20:36 +04:00
/*
* Including < asm / unistd . h > would give use the 64 - bit syscall numbers . . .
*/
# define __NR_N32_rt_sigreturn 6211
# define __NR_N32_restart_syscall 6214
2006-02-01 19:26:34 +03:00
# define DEBUG_SIG 0
2005-04-17 02:20:36 +04:00
# define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
/* IRIX compatible stack_t */
typedef struct sigaltstack32 {
s32 ss_sp ;
compat_size_t ss_size ;
int ss_flags ;
} stack32_t ;
struct ucontextn32 {
u32 uc_flags ;
s32 uc_link ;
stack32_t uc_stack ;
struct sigcontext uc_mcontext ;
sigset_t uc_sigmask ; /* mask last for extensibility */
} ;
struct rt_sigframe_n32 {
u32 rs_ass [ 4 ] ; /* argument save space for o32 */
2005-06-15 17:00:12 +04:00
# if ICACHE_REFILLS_WORKAROUND_WAR
u32 rs_pad [ 2 ] ;
# else
u32 rs_code [ 2 ] ; /* signal trampoline */
# endif
struct siginfo rs_info ;
2005-04-17 02:20:36 +04:00
struct ucontextn32 rs_uc ;
2005-06-15 17:00:12 +04:00
# if ICACHE_REFILLS_WORKAROUND_WAR
u32 rs_code [ 8 ] ____cacheline_aligned ; /* signal trampoline */
# endif
2005-04-17 02:20:36 +04:00
} ;
2006-02-19 01:47:26 +03:00
save_static_function ( sysn32_rt_sigsuspend ) ;
__attribute_used__ noinline static int
_sysn32_rt_sigsuspend ( nabi_no_regargs struct pt_regs regs )
{
2006-02-20 19:27:59 +03:00
compat_sigset_t __user * unewset ;
compat_sigset_t uset ;
2006-02-19 01:47:26 +03:00
size_t sigsetsize ;
sigset_t newset ;
/* XXX Don't preclude handling different sized sigset_t's. */
sigsetsize = regs . regs [ 5 ] ;
if ( sigsetsize ! = sizeof ( sigset_t ) )
return - EINVAL ;
unewset = ( compat_sigset_t __user * ) regs . regs [ 4 ] ;
if ( copy_from_user ( & uset , unewset , sizeof ( uset ) ) )
return - EFAULT ;
sigset_from_compat ( & newset , & uset ) ;
sigdelsetmask ( & newset , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > saved_sigmask = current - > blocked ;
current - > blocked = newset ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
current - > state = TASK_INTERRUPTIBLE ;
schedule ( ) ;
set_thread_flag ( TIF_RESTORE_SIGMASK ) ;
return - ERESTARTNOHAND ;
}
2005-04-17 02:20:36 +04:00
save_static_function ( sysn32_rt_sigreturn ) ;
__attribute_used__ noinline static void
_sysn32_rt_sigreturn ( nabi_no_regargs struct pt_regs regs )
{
2006-01-31 19:41:09 +03:00
struct rt_sigframe_n32 __user * frame ;
2005-04-17 02:20:36 +04:00
sigset_t set ;
stack_t st ;
s32 sp ;
2006-01-31 19:41:09 +03:00
frame = ( struct rt_sigframe_n32 __user * ) regs . regs [ 29 ] ;
2005-04-17 02:20:36 +04:00
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set , & frame - > rs_uc . uc_sigmask , sizeof ( set ) ) )
goto badframe ;
sigdelsetmask ( & set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
if ( restore_sigcontext ( & regs , & frame - > rs_uc . uc_mcontext ) )
goto badframe ;
/* The ucontext contains a stack32_t, so we must convert! */
if ( __get_user ( sp , & frame - > rs_uc . uc_stack . ss_sp ) )
goto badframe ;
2006-02-19 17:46:44 +03:00
st . ss_sp = ( void __user * ) ( long ) sp ;
2005-04-17 02:20:36 +04:00
if ( __get_user ( st . ss_size , & frame - > rs_uc . uc_stack . ss_size ) )
goto badframe ;
if ( __get_user ( st . ss_flags , & frame - > rs_uc . uc_stack . ss_flags ) )
goto badframe ;
/* It is more difficult to avoid calling this function than to
call it and ignore errors . */
2006-01-31 19:41:09 +03:00
do_sigaltstack ( ( stack_t __user * ) & st , NULL , regs . regs [ 29 ] ) ;
2005-04-17 02:20:36 +04:00
/*
* Don ' t let your children do this . . .
*/
__asm__ __volatile__ (
" move \t $29, %0 \n \t "
" j \t syscall_exit "
: /* no outputs */
: " r " ( & regs ) ) ;
/* Unreached */
badframe :
force_sig ( SIGSEGV , current ) ;
}
2005-07-12 00:45:51 +04:00
int setup_rt_frame_n32 ( struct k_sigaction * ka ,
2005-04-17 02:20:36 +04:00
struct pt_regs * regs , int signr , sigset_t * set , siginfo_t * info )
{
2006-01-31 19:41:09 +03:00
struct rt_sigframe_n32 __user * frame ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
s32 sp ;
frame = get_sigframe ( ka , regs , sizeof ( * frame ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
goto give_sigsegv ;
2005-06-15 17:00:12 +04:00
install_sigtramp ( frame - > rs_code , __NR_N32_rt_sigreturn ) ;
2005-04-17 02:20:36 +04:00
/* Create siginfo. */
err | = copy_siginfo_to_user ( & frame - > rs_info , info ) ;
/* Create the ucontext. */
err | = __put_user ( 0 , & frame - > rs_uc . uc_flags ) ;
err | = __put_user ( 0 , & frame - > rs_uc . uc_link ) ;
sp = ( int ) ( long ) current - > sas_ss_sp ;
err | = __put_user ( sp ,
& frame - > rs_uc . uc_stack . ss_sp ) ;
err | = __put_user ( sas_ss_flags ( regs - > regs [ 29 ] ) ,
& frame - > rs_uc . uc_stack . ss_flags ) ;
err | = __put_user ( current - > sas_ss_size ,
& frame - > rs_uc . uc_stack . ss_size ) ;
err | = setup_sigcontext ( regs , & frame - > rs_uc . uc_mcontext ) ;
err | = __copy_to_user ( & frame - > rs_uc . uc_sigmask , set , sizeof ( * set ) ) ;
if ( err )
goto give_sigsegv ;
/*
* Arguments to signal handler :
*
* a0 = signal number
* a1 = 0 ( should be cause )
* a2 = pointer to ucontext
*
* $ 25 and c0_epc point to the signal handler , $ 29 points to
* the struct rt_sigframe .
*/
regs - > regs [ 4 ] = signr ;
regs - > regs [ 5 ] = ( unsigned long ) & frame - > rs_info ;
regs - > regs [ 6 ] = ( unsigned long ) & frame - > rs_uc ;
regs - > regs [ 29 ] = ( unsigned long ) frame ;
regs - > regs [ 31 ] = ( unsigned long ) frame - > rs_code ;
regs - > cp0_epc = regs - > regs [ 25 ] = ( unsigned long ) ka - > sa . sa_handler ;
# if DEBUG_SIG
printk ( " SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p \n " ,
current - > comm , current - > pid ,
frame , regs - > cp0_epc , regs - > regs [ 31 ] ) ;
# endif
2006-02-08 15:58:41 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
give_sigsegv :
force_sigsegv ( signr , current ) ;
2006-02-08 15:58:41 +03:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}