2005-04-17 02:20:36 +04:00
/*
2008-09-12 14:52:36 +04:00
* SuperH process tracing
2005-04-17 02:20:36 +04:00
*
2008-09-12 14:52:36 +04:00
* Copyright ( C ) 1999 , 2000 Kaz Kojima & Niibe Yutaka
2009-12-28 11:53:47 +03:00
* Copyright ( C ) 2002 - 2009 Paul Mundt
2005-04-17 02:20:36 +04:00
*
2008-09-12 14:52:36 +04:00
* Audit support by Yuichi Nakamura < ynakam @ hitachisoft . jp >
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
# include <linux/user.h>
# include <linux/security.h>
2005-05-01 19:59:14 +04:00
# include <linux/signal.h>
2007-02-23 07:22:17 +03:00
# include <linux/io.h>
2007-11-10 13:21:34 +03:00
# include <linux/audit.h>
2008-07-30 10:30:52 +04:00
# include <linux/seccomp.h>
2008-07-30 14:55:30 +04:00
# include <linux/tracehook.h>
2008-09-12 14:52:36 +04:00
# include <linux/elf.h>
# include <linux/regset.h>
2009-12-28 11:53:47 +03:00
# include <linux/hw_breakpoint.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/processor.h>
# include <asm/mmu_context.h>
2008-09-04 13:53:58 +04:00
# include <asm/syscalls.h>
2008-09-21 14:04:55 +04:00
# include <asm/fpu.h>
2005-04-17 02:20:36 +04:00
2009-09-16 09:30:34 +04:00
# define CREATE_TRACE_POINTS
# include <trace/events/syscalls.h>
2009-07-06 15:16:33 +04:00
2005-04-17 02:20:36 +04:00
/*
* This routine will get a word off of the process kernel stack .
*/
static inline int get_stack_long ( struct task_struct * task , int offset )
{
unsigned char * stack ;
2006-01-12 12:05:44 +03:00
stack = ( unsigned char * ) task_pt_regs ( task ) ;
2005-04-17 02:20:36 +04:00
stack + = offset ;
return ( * ( ( int * ) stack ) ) ;
}
/*
* This routine will put a word on the process kernel stack .
*/
static inline int put_stack_long ( struct task_struct * task , int offset ,
unsigned long data )
{
unsigned char * stack ;
2006-01-12 12:05:44 +03:00
stack = ( unsigned char * ) task_pt_regs ( task ) ;
2005-04-17 02:20:36 +04:00
stack + = offset ;
* ( unsigned long * ) stack = data ;
return 0 ;
}
2011-06-27 16:41:57 +04:00
void ptrace_triggered ( struct perf_event * bp ,
2009-12-28 11:53:47 +03:00
struct perf_sample_data * data , struct pt_regs * regs )
2008-07-30 14:09:31 +04:00
{
2009-12-28 11:53:47 +03:00
struct perf_event_attr attr ;
/*
* Disable the breakpoint request here since ptrace has defined a
* one - shot behaviour for breakpoint exceptions .
*/
attr = bp - > attr ;
attr . disabled = true ;
modify_user_hw_breakpoint ( bp , & attr ) ;
}
static int set_single_step ( struct task_struct * tsk , unsigned long addr )
2008-07-30 14:09:31 +04:00
{
2009-12-28 11:53:47 +03:00
struct thread_struct * thread = & tsk - > thread ;
struct perf_event * bp ;
struct perf_event_attr attr ;
bp = thread - > ptrace_bps [ 0 ] ;
if ( ! bp ) {
2010-04-22 07:05:45 +04:00
ptrace_breakpoint_init ( & attr ) ;
2009-12-28 11:53:47 +03:00
attr . bp_addr = addr ;
attr . bp_len = HW_BREAKPOINT_LEN_2 ;
attr . bp_type = HW_BREAKPOINT_R ;
2008-07-30 14:09:31 +04:00
2011-06-29 19:42:35 +04:00
bp = register_user_hw_breakpoint ( & attr , ptrace_triggered ,
NULL , tsk ) ;
2009-12-28 11:53:47 +03:00
if ( IS_ERR ( bp ) )
return PTR_ERR ( bp ) ;
thread - > ptrace_bps [ 0 ] = bp ;
} else {
int err ;
attr = bp - > attr ;
attr . bp_addr = addr ;
2011-03-23 14:35:42 +03:00
/* reenable breakpoint */
attr . disabled = false ;
2009-12-28 11:53:47 +03:00
err = modify_user_hw_breakpoint ( bp , & attr ) ;
if ( unlikely ( err ) )
return err ;
}
return 0 ;
}
2008-07-30 14:09:31 +04:00
void user_enable_single_step ( struct task_struct * child )
{
2009-12-28 11:53:47 +03:00
unsigned long pc = get_stack_long ( child , offsetof ( struct pt_regs , pc ) ) ;
2008-07-30 14:09:31 +04:00
set_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
2009-12-28 11:53:47 +03:00
2011-04-08 19:29:36 +04:00
if ( ptrace_get_breakpoints ( child ) < 0 )
return ;
2009-12-28 11:53:47 +03:00
set_single_step ( child , pc ) ;
2011-04-08 19:29:36 +04:00
ptrace_put_breakpoints ( child ) ;
2008-07-30 14:09:31 +04:00
}
void user_disable_single_step ( struct task_struct * child )
2007-02-23 07:22:17 +03:00
{
clear_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Called by kernel / ptrace . c when detaching . .
*
* Make sure single step bits etc are not set .
*/
void ptrace_disable ( struct task_struct * child )
{
2008-07-30 14:09:31 +04:00
user_disable_single_step ( child ) ;
2005-04-17 02:20:36 +04:00
}
2008-09-12 14:52:36 +04:00
static int genregs_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
const struct pt_regs * regs = task_pt_regs ( target ) ;
int ret ;
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
regs - > regs ,
0 , 16 * sizeof ( unsigned long ) ) ;
if ( ! ret )
/* PC, PR, SR, GBR, MACH, MACL, TRA */
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& regs - > pc ,
offsetof ( struct pt_regs , pc ) ,
sizeof ( struct pt_regs ) ) ;
if ( ! ret )
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
sizeof ( struct pt_regs ) , - 1 ) ;
return ret ;
}
static int genregs_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
struct pt_regs * regs = task_pt_regs ( target ) ;
int ret ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
regs - > regs ,
0 , 16 * sizeof ( unsigned long ) ) ;
if ( ! ret & & count > 0 )
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& regs - > pc ,
offsetof ( struct pt_regs , pc ) ,
sizeof ( struct pt_regs ) ) ;
if ( ! ret )
ret = user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
sizeof ( struct pt_regs ) , - 1 ) ;
return ret ;
}
2008-09-21 14:04:55 +04:00
# ifdef CONFIG_SH_FPU
int fpregs_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
int ret ;
ret = init_fpu ( target ) ;
if ( ret )
return ret ;
if ( ( boot_cpu_data . flags & CPU_HAS_FPU ) )
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
2010-01-13 06:51:40 +03:00
& target - > thread . xstate - > hardfpu , 0 , - 1 ) ;
2008-09-21 14:04:55 +04:00
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
2010-01-13 06:51:40 +03:00
& target - > thread . xstate - > softfpu , 0 , - 1 ) ;
2008-09-21 14:04:55 +04:00
}
static int fpregs_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
int ret ;
ret = init_fpu ( target ) ;
if ( ret )
return ret ;
set_stopped_child_used_math ( target ) ;
if ( ( boot_cpu_data . flags & CPU_HAS_FPU ) )
return user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
2010-01-13 06:51:40 +03:00
& target - > thread . xstate - > hardfpu , 0 , - 1 ) ;
2008-09-21 14:04:55 +04:00
return user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
2010-01-13 06:51:40 +03:00
& target - > thread . xstate - > softfpu , 0 , - 1 ) ;
2008-09-21 14:04:55 +04:00
}
static int fpregs_active ( struct task_struct * target ,
const struct user_regset * regset )
{
return tsk_used_math ( target ) ? regset - > n : 0 ;
}
# endif
2008-09-12 17:42:10 +04:00
# ifdef CONFIG_SH_DSP
static int dspregs_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
2009-04-03 21:32:33 +04:00
const struct pt_dspregs * regs =
( struct pt_dspregs * ) & target - > thread . dsp_status . dsp_regs ;
2008-09-12 17:42:10 +04:00
int ret ;
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf , regs ,
0 , sizeof ( struct pt_dspregs ) ) ;
if ( ! ret )
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
sizeof ( struct pt_dspregs ) , - 1 ) ;
return ret ;
}
static int dspregs_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
2009-04-03 21:32:33 +04:00
struct pt_dspregs * regs =
( struct pt_dspregs * ) & target - > thread . dsp_status . dsp_regs ;
2008-09-12 17:42:10 +04:00
int ret ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf , regs ,
0 , sizeof ( struct pt_dspregs ) ) ;
if ( ! ret )
ret = user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
sizeof ( struct pt_dspregs ) , - 1 ) ;
return ret ;
}
2008-09-12 17:56:35 +04:00
static int dspregs_active ( struct task_struct * target ,
const struct user_regset * regset )
{
struct pt_regs * regs = task_pt_regs ( target ) ;
return regs - > sr & SR_DSP ? regset - > n : 0 ;
}
2008-09-12 17:42:10 +04:00
# endif
2010-06-14 10:16:53 +04:00
const struct pt_regs_offset regoffset_table [ ] = {
REGS_OFFSET_NAME ( 0 ) ,
REGS_OFFSET_NAME ( 1 ) ,
REGS_OFFSET_NAME ( 2 ) ,
REGS_OFFSET_NAME ( 3 ) ,
REGS_OFFSET_NAME ( 4 ) ,
REGS_OFFSET_NAME ( 5 ) ,
REGS_OFFSET_NAME ( 6 ) ,
REGS_OFFSET_NAME ( 7 ) ,
REGS_OFFSET_NAME ( 8 ) ,
REGS_OFFSET_NAME ( 9 ) ,
REGS_OFFSET_NAME ( 10 ) ,
REGS_OFFSET_NAME ( 11 ) ,
REGS_OFFSET_NAME ( 12 ) ,
REGS_OFFSET_NAME ( 13 ) ,
REGS_OFFSET_NAME ( 14 ) ,
REGS_OFFSET_NAME ( 15 ) ,
REG_OFFSET_NAME ( pc ) ,
REG_OFFSET_NAME ( pr ) ,
REG_OFFSET_NAME ( sr ) ,
REG_OFFSET_NAME ( gbr ) ,
REG_OFFSET_NAME ( mach ) ,
REG_OFFSET_NAME ( macl ) ,
REG_OFFSET_NAME ( tra ) ,
REG_OFFSET_END ,
} ;
2008-09-12 14:52:36 +04:00
/*
* These are our native regset flavours .
*/
enum sh_regset {
REGSET_GENERAL ,
2008-09-21 14:04:55 +04:00
# ifdef CONFIG_SH_FPU
REGSET_FPU ,
# endif
2008-09-12 17:42:10 +04:00
# ifdef CONFIG_SH_DSP
REGSET_DSP ,
# endif
2008-09-12 14:52:36 +04:00
} ;
static const struct user_regset sh_regsets [ ] = {
/*
* Format is :
* R0 - - > R15
* PC , PR , SR , GBR , MACH , MACL , TRA
*/
[ REGSET_GENERAL ] = {
. core_note_type = NT_PRSTATUS ,
. n = ELF_NGREG ,
. size = sizeof ( long ) ,
. align = sizeof ( long ) ,
. get = genregs_get ,
. set = genregs_set ,
} ,
2008-09-12 17:42:10 +04:00
2008-09-21 14:04:55 +04:00
# ifdef CONFIG_SH_FPU
[ REGSET_FPU ] = {
. core_note_type = NT_PRFPREG ,
. n = sizeof ( struct user_fpu_struct ) / sizeof ( long ) ,
. size = sizeof ( long ) ,
. align = sizeof ( long ) ,
. get = fpregs_get ,
. set = fpregs_set ,
. active = fpregs_active ,
} ,
# endif
2008-09-12 17:42:10 +04:00
# ifdef CONFIG_SH_DSP
[ REGSET_DSP ] = {
. n = sizeof ( struct pt_dspregs ) / sizeof ( long ) ,
. size = sizeof ( long ) ,
. align = sizeof ( long ) ,
. get = dspregs_get ,
. set = dspregs_set ,
2008-09-12 17:56:35 +04:00
. active = dspregs_active ,
2008-09-12 17:42:10 +04:00
} ,
# endif
2008-09-12 14:52:36 +04:00
} ;
static const struct user_regset_view user_sh_native_view = {
. name = " sh " ,
. e_machine = EM_SH ,
. regsets = sh_regsets ,
. n = ARRAY_SIZE ( sh_regsets ) ,
} ;
2008-09-12 17:42:43 +04:00
const struct user_regset_view * task_user_regset_view ( struct task_struct * task )
{
return & user_sh_native_view ;
}
2010-10-28 02:33:47 +04:00
long arch_ptrace ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
2005-04-17 02:20:36 +04:00
{
2008-09-04 13:53:58 +04:00
unsigned long __user * datap = ( unsigned long __user * ) data ;
2005-04-17 02:20:36 +04:00
int ret ;
switch ( request ) {
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR : {
unsigned long tmp ;
ret = - EIO ;
2007-02-23 07:22:17 +03:00
if ( ( addr & 3 ) | | addr < 0 | |
2005-04-17 02:20:36 +04:00
addr > sizeof ( struct user ) - 3 )
break ;
if ( addr < sizeof ( struct pt_regs ) )
tmp = get_stack_long ( child , addr ) ;
2010-10-28 02:34:03 +04:00
else if ( addr > = offsetof ( struct user , fpu ) & &
addr < offsetof ( struct user , u_fpvalid ) ) {
2005-04-17 02:20:36 +04:00
if ( ! tsk_used_math ( child ) ) {
2010-10-28 02:34:03 +04:00
if ( addr = = offsetof ( struct user , fpu . fpscr ) )
2005-04-17 02:20:36 +04:00
tmp = FPSCR_INIT ;
else
tmp = 0 ;
2010-10-28 02:34:03 +04:00
} else {
unsigned long index ;
2011-03-18 17:16:31 +03:00
ret = init_fpu ( child ) ;
if ( ret )
break ;
2010-10-28 02:34:03 +04:00
index = addr - offsetof ( struct user , fpu ) ;
2010-10-28 02:33:47 +04:00
tmp = ( ( unsigned long * ) child - > thread . xstate )
2010-10-28 02:34:03 +04:00
[ index > > 2 ] ;
}
} else if ( addr = = offsetof ( struct user , u_fpvalid ) )
2005-04-17 02:20:36 +04:00
tmp = ! ! tsk_used_math ( child ) ;
2009-05-08 18:50:54 +04:00
else if ( addr = = PT_TEXT_ADDR )
tmp = child - > mm - > start_code ;
else if ( addr = = PT_DATA_ADDR )
tmp = child - > mm - > start_data ;
else if ( addr = = PT_TEXT_END_ADDR )
tmp = child - > mm - > end_code ;
else if ( addr = = PT_TEXT_LEN )
tmp = child - > mm - > end_code - child - > mm - > start_code ;
2005-04-17 02:20:36 +04:00
else
tmp = 0 ;
2008-09-04 13:53:58 +04:00
ret = put_user ( tmp , datap ) ;
2005-04-17 02:20:36 +04:00
break ;
}
case PTRACE_POKEUSR : /* write the word at location addr in the USER area */
ret = - EIO ;
2007-02-23 07:22:17 +03:00
if ( ( addr & 3 ) | | addr < 0 | |
2005-04-17 02:20:36 +04:00
addr > sizeof ( struct user ) - 3 )
break ;
if ( addr < sizeof ( struct pt_regs ) )
ret = put_stack_long ( child , addr , data ) ;
2010-10-28 02:34:03 +04:00
else if ( addr > = offsetof ( struct user , fpu ) & &
addr < offsetof ( struct user , u_fpvalid ) ) {
unsigned long index ;
2011-03-18 17:16:31 +03:00
ret = init_fpu ( child ) ;
if ( ret )
break ;
2010-10-28 02:34:03 +04:00
index = addr - offsetof ( struct user , fpu ) ;
2005-04-17 02:20:36 +04:00
set_stopped_child_used_math ( child ) ;
2010-10-28 02:33:47 +04:00
( ( unsigned long * ) child - > thread . xstate )
2010-10-28 02:34:03 +04:00
[ index > > 2 ] = data ;
2005-04-17 02:20:36 +04:00
ret = 0 ;
2010-10-28 02:34:03 +04:00
} else if ( addr = = offsetof ( struct user , u_fpvalid ) ) {
2005-04-17 02:20:36 +04:00
conditional_stopped_child_used_math ( data , child ) ;
ret = 0 ;
}
break ;
2008-09-12 14:52:36 +04:00
case PTRACE_GETREGS :
return copy_regset_to_user ( child , & user_sh_native_view ,
REGSET_GENERAL ,
0 , sizeof ( struct pt_regs ) ,
2010-10-28 02:34:03 +04:00
datap ) ;
2008-09-12 14:52:36 +04:00
case PTRACE_SETREGS :
return copy_regset_from_user ( child , & user_sh_native_view ,
REGSET_GENERAL ,
0 , sizeof ( struct pt_regs ) ,
2010-10-28 02:34:03 +04:00
datap ) ;
2008-09-21 14:04:55 +04:00
# ifdef CONFIG_SH_FPU
case PTRACE_GETFPREGS :
return copy_regset_to_user ( child , & user_sh_native_view ,
REGSET_FPU ,
0 , sizeof ( struct user_fpu_struct ) ,
2010-10-28 02:34:03 +04:00
datap ) ;
2008-09-21 14:04:55 +04:00
case PTRACE_SETFPREGS :
return copy_regset_from_user ( child , & user_sh_native_view ,
REGSET_FPU ,
0 , sizeof ( struct user_fpu_struct ) ,
2010-10-28 02:34:03 +04:00
datap ) ;
2008-09-21 14:04:55 +04:00
# endif
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SH_DSP
2008-09-12 17:42:10 +04:00
case PTRACE_GETDSPREGS :
return copy_regset_to_user ( child , & user_sh_native_view ,
REGSET_DSP ,
0 , sizeof ( struct pt_dspregs ) ,
2010-10-28 02:34:03 +04:00
datap ) ;
2008-09-12 17:42:10 +04:00
case PTRACE_SETDSPREGS :
return copy_regset_from_user ( child , & user_sh_native_view ,
REGSET_DSP ,
0 , sizeof ( struct pt_dspregs ) ,
2010-10-28 02:34:03 +04:00
datap ) ;
2005-04-17 02:20:36 +04:00
# endif
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
}
2005-11-07 11:59:47 +03:00
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-07-30 15:05:35 +04:00
static inline int audit_arch ( void )
{
int arch = EM_SH ;
# ifdef CONFIG_CPU_LITTLE_ENDIAN
arch | = __AUDIT_ARCH_LE ;
# endif
return arch ;
}
2008-07-30 14:55:30 +04:00
asmlinkage long do_syscall_trace_enter ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2008-07-30 14:55:30 +04:00
long ret = 0 ;
2005-04-17 02:20:36 +04:00
2008-07-30 10:30:52 +04:00
secure_computing ( regs - > regs [ 0 ] ) ;
2008-07-30 14:55:30 +04:00
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) & &
tracehook_report_syscall_entry ( regs ) )
/*
* Tracing decided this syscall should not happen .
* We ' ll return a bogus call number to get an ENOSYS
* error , but leave the original number in regs - > regs [ 0 ] .
*/
ret = - 1L ;
2007-11-10 13:21:34 +03:00
2009-09-16 09:30:34 +04:00
if ( unlikely ( test_thread_flag ( TIF_SYSCALL_TRACEPOINT ) ) )
trace_sys_enter ( regs , regs - > regs [ 0 ] ) ;
2009-07-06 15:16:33 +04:00
2008-07-30 14:55:30 +04:00
if ( unlikely ( current - > audit_context ) )
2008-07-30 15:05:35 +04:00
audit_syscall_entry ( audit_arch ( ) , regs - > regs [ 3 ] ,
2007-11-10 13:21:34 +03:00
regs - > regs [ 4 ] , regs - > regs [ 5 ] ,
regs - > regs [ 6 ] , regs - > regs [ 7 ] ) ;
2008-07-30 14:55:30 +04:00
return ret ? : regs - > regs [ 0 ] ;
}
asmlinkage void do_syscall_trace_leave ( struct pt_regs * regs )
{
int step ;
if ( unlikely ( current - > audit_context ) )
audit_syscall_exit ( AUDITSC_RESULT ( regs - > regs [ 0 ] ) ,
regs - > regs [ 0 ] ) ;
2009-09-16 09:30:34 +04:00
if ( unlikely ( test_thread_flag ( TIF_SYSCALL_TRACEPOINT ) ) )
trace_sys_exit ( regs , regs - > regs [ 0 ] ) ;
2009-07-06 15:16:33 +04:00
2008-07-30 14:55:30 +04:00
step = test_thread_flag ( TIF_SINGLESTEP ) ;
if ( step | | test_thread_flag ( TIF_SYSCALL_TRACE ) )
tracehook_report_syscall_exit ( regs , step ) ;
2005-04-17 02:20:36 +04:00
}