2010-05-28 23:09:12 -04:00
/*
* Copyright 2010 Tilera Corporation . All Rights Reserved .
*
* 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 , version 2.
*
* 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 , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for
* more details .
*
* Copied from i386 : Ross Biro 1 / 23 / 92
*/
# include <linux/kernel.h>
# include <linux/ptrace.h>
# include <linux/kprobes.h>
# include <linux/compat.h>
# include <linux/uaccess.h>
2012-12-17 20:08:09 -05:00
# include <linux/regset.h>
# include <linux/elf.h>
2012-12-22 00:21:10 -05:00
# include <linux/tracehook.h>
2015-03-23 14:23:58 -04:00
# include <linux/context_tracking.h>
2010-06-25 17:04:17 -04:00
# include <asm/traps.h>
2012-12-17 20:08:09 -05:00
# include <arch/chip.h>
2010-05-28 23:09:12 -04:00
2013-01-21 19:54:57 -05:00
# define CREATE_TRACE_POINTS
# include <trace/events/syscalls.h>
2010-05-28 23:09:12 -04:00
void user_enable_single_step ( struct task_struct * child )
{
set_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
}
void user_disable_single_step ( struct task_struct * child )
{
clear_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
}
/*
* Called by kernel / ptrace . c when detaching . .
*/
void ptrace_disable ( struct task_struct * child )
{
clear_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
/*
* These two are currently unused , but will be set by arch_ptrace ( )
* and used in the syscall assembly when we do support them .
*/
clear_tsk_thread_flag ( child , TIF_SYSCALL_TRACE ) ;
}
2012-12-12 17:24:39 -05:00
/*
* Get registers from task and ready the result for userspace .
* Note that we localize the API issues to getregs ( ) and putregs ( ) at
* some cost in performance , e . g . we need a full pt_regs copy for
* PEEKUSR , and two copies for POKEUSR . But in general we expect
* GETREGS / PUTREGS to be the API of choice anyway .
*/
static char * getregs ( struct task_struct * child , struct pt_regs * uregs )
{
* uregs = * task_pt_regs ( child ) ;
/* Set up flags ABI bits. */
uregs - > flags = 0 ;
# ifdef CONFIG_COMPAT
if ( task_thread_info ( child ) - > status & TS_COMPAT )
uregs - > flags | = PT_FLAGS_COMPAT ;
# endif
return ( char * ) uregs ;
}
/* Put registers back to task. */
static void putregs ( struct task_struct * child , struct pt_regs * uregs )
{
struct pt_regs * regs = task_pt_regs ( child ) ;
/* Don't allow overwriting the kernel-internal flags word. */
uregs - > flags = regs - > flags ;
/* Only allow setting the ICS bit in the ex1 word. */
uregs - > ex1 = PL_ICS_EX1 ( USER_PL , EX1_ICS ( uregs - > ex1 ) ) ;
* regs = * uregs ;
}
2012-12-17 20:08:09 -05:00
enum tile_regset {
REGSET_GPR ,
} ;
static int tile_gpr_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
struct pt_regs regs ;
getregs ( target , & regs ) ;
return user_regset_copyout ( & pos , & count , & kbuf , & ubuf , & regs , 0 ,
sizeof ( regs ) ) ;
}
static int tile_gpr_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 ;
struct pt_regs regs ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf , & regs , 0 ,
sizeof ( regs ) ) ;
if ( ret )
return ret ;
putregs ( target , & regs ) ;
return 0 ;
}
static const struct user_regset tile_user_regset [ ] = {
[ REGSET_GPR ] = {
. core_note_type = NT_PRSTATUS ,
. n = ELF_NGREG ,
. size = sizeof ( elf_greg_t ) ,
. align = sizeof ( elf_greg_t ) ,
. get = tile_gpr_get ,
. set = tile_gpr_set ,
} ,
} ;
static const struct user_regset_view tile_user_regset_view = {
. name = CHIP_ARCH_NAME ,
. e_machine = ELF_ARCH ,
. ei_osabi = ELF_OSABI ,
. regsets = tile_user_regset ,
. n = ARRAY_SIZE ( tile_user_regset ) ,
} ;
const struct user_regset_view * task_user_regset_view ( struct task_struct * task )
{
return & tile_user_regset_view ;
}
2010-10-27 15:33:47 -07:00
long arch_ptrace ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
2010-05-28 23:09:12 -04:00
{
2010-10-14 16:48:00 -04:00
unsigned long __user * datap = ( long __user __force * ) data ;
2010-05-28 23:09:12 -04:00
unsigned long tmp ;
long ret = - EIO ;
2010-10-14 16:48:00 -04:00
char * childreg ;
2010-10-28 15:47:06 -04:00
struct pt_regs copyregs ;
2010-05-28 23:09:12 -04:00
switch ( request ) {
case PTRACE_PEEKUSR : /* Read register from pt_regs. */
2010-10-27 15:34:04 -07:00
if ( addr > = PTREGS_SIZE )
2010-05-28 23:09:12 -04:00
break ;
2012-12-12 17:24:39 -05:00
childreg = getregs ( child , & copyregs ) + addr ;
2010-10-14 16:48:00 -04:00
# ifdef CONFIG_COMPAT
if ( is_compat_task ( ) ) {
if ( addr & ( sizeof ( compat_long_t ) - 1 ) )
break ;
ret = put_user ( * ( compat_long_t * ) childreg ,
( compat_long_t __user * ) datap ) ;
} else
# endif
{
if ( addr & ( sizeof ( long ) - 1 ) )
break ;
ret = put_user ( * ( long * ) childreg , datap ) ;
}
2010-05-28 23:09:12 -04:00
break ;
case PTRACE_POKEUSR : /* Write register in pt_regs. */
2010-10-27 15:34:04 -07:00
if ( addr > = PTREGS_SIZE )
2010-05-28 23:09:12 -04:00
break ;
2012-12-12 17:24:39 -05:00
childreg = getregs ( child , & copyregs ) + addr ;
2010-10-14 16:48:00 -04:00
# ifdef CONFIG_COMPAT
if ( is_compat_task ( ) ) {
if ( addr & ( sizeof ( compat_long_t ) - 1 ) )
break ;
* ( compat_long_t * ) childreg = data ;
} else
# endif
{
if ( addr & ( sizeof ( long ) - 1 ) )
break ;
* ( long * ) childreg = data ;
}
2012-12-12 17:24:39 -05:00
putregs ( child , & copyregs ) ;
2010-07-02 14:17:52 -04:00
ret = 0 ;
2010-05-28 23:09:12 -04:00
break ;
case PTRACE_GETREGS : /* Get all registers from the child. */
2012-12-17 20:08:10 -05:00
ret = copy_regset_to_user ( child , & tile_user_regset_view ,
REGSET_GPR , 0 ,
sizeof ( struct pt_regs ) , datap ) ;
2010-05-28 23:09:12 -04:00
break ;
case PTRACE_SETREGS : /* Set all registers in the child. */
2012-12-17 20:08:10 -05:00
ret = copy_regset_from_user ( child , & tile_user_regset_view ,
REGSET_GPR , 0 ,
sizeof ( struct pt_regs ) , datap ) ;
2010-05-28 23:09:12 -04:00
break ;
case PTRACE_GETFPREGS : /* Get the child FPU state. */
case PTRACE_SETFPREGS : /* Set the child FPU state. */
break ;
case PTRACE_SETOPTIONS :
/* Support TILE-specific ptrace options. */
2012-12-13 11:34:45 -05:00
BUILD_BUG_ON ( PTRACE_O_MASK_TILE & PTRACE_O_MASK ) ;
2010-05-28 23:09:12 -04:00
tmp = data & PTRACE_O_MASK_TILE ;
data & = ~ PTRACE_O_MASK_TILE ;
ret = ptrace_request ( child , request , addr , data ) ;
2012-12-13 11:34:45 -05:00
if ( ret = = 0 ) {
unsigned int flags = child - > ptrace ;
flags & = ~ ( PTRACE_O_MASK_TILE < < PT_OPT_FLAG_SHIFT ) ;
flags | = ( tmp < < PT_OPT_FLAG_SHIFT ) ;
child - > ptrace = flags ;
}
2010-05-28 23:09:12 -04:00
break ;
default :
# ifdef CONFIG_COMPAT
if ( task_thread_info ( current ) - > status & TS_COMPAT ) {
ret = compat_ptrace_request ( child , request ,
addr , data ) ;
break ;
}
# endif
ret = ptrace_request ( child , request , addr , data ) ;
break ;
}
return ret ;
}
# ifdef CONFIG_COMPAT
/* Not used; we handle compat issues in arch_ptrace() directly. */
long compat_arch_ptrace ( struct task_struct * child , compat_long_t request ,
compat_ulong_t addr , compat_ulong_t data )
{
BUG ( ) ;
}
# endif
2012-12-22 00:21:10 -05:00
int do_syscall_trace_enter ( struct pt_regs * regs )
2010-05-28 23:09:12 -04:00
{
2015-03-23 14:23:58 -04:00
u32 work = ACCESS_ONCE ( current_thread_info ( ) - > flags ) ;
2016-06-02 19:56:43 -07:00
if ( ( work & _TIF_SYSCALL_TRACE ) & &
tracehook_report_syscall_entry ( regs ) ) {
regs - > regs [ TREG_SYSCALL_NR ] = - 1 ;
2015-07-22 14:30:14 -04:00
return - 1 ;
2012-12-22 00:21:10 -05:00
}
2010-05-28 23:09:12 -04:00
2016-06-02 19:56:43 -07:00
if ( secure_computing ( NULL ) = = - 1 )
return - 1 ;
2015-03-23 14:23:58 -04:00
if ( work & _TIF_SYSCALL_TRACEPOINT )
2013-01-21 19:54:57 -05:00
trace_sys_enter ( regs , regs - > regs [ TREG_SYSCALL_NR ] ) ;
2012-12-22 00:21:10 -05:00
return regs - > regs [ TREG_SYSCALL_NR ] ;
}
2010-05-28 23:09:12 -04:00
2012-12-22 00:21:10 -05:00
void do_syscall_trace_exit ( struct pt_regs * regs )
{
2013-08-09 15:42:56 -04:00
long errno ;
/*
* The standard tile calling convention returns the value ( or negative
* errno ) in r0 , and zero ( or positive errno ) in r1 .
* It saves a couple of cycles on the hot path to do this work in
* registers only as we return , rather than updating the in - memory
* struct ptregs .
*/
errno = ( long ) regs - > regs [ 0 ] ;
if ( errno < 0 & & errno > - 4096 )
regs - > regs [ 1 ] = - errno ;
else
regs - > regs [ 1 ] = 0 ;
2013-01-21 19:54:57 -05:00
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
tracehook_report_syscall_exit ( regs , 0 ) ;
if ( test_thread_flag ( TIF_SYSCALL_TRACEPOINT ) )
2013-04-17 11:01:22 -04:00
trace_sys_exit ( regs , regs - > regs [ 0 ] ) ;
2010-05-28 23:09:12 -04:00
}
2013-08-06 16:04:13 -04:00
void send_sigtrap ( struct task_struct * tsk , struct pt_regs * regs )
2010-05-28 23:09:12 -04:00
{
struct siginfo info ;
memset ( & info , 0 , sizeof ( info ) ) ;
info . si_signo = SIGTRAP ;
info . si_code = TRAP_BRKPT ;
info . si_addr = ( void __user * ) regs - > pc ;
/* Send us the fakey SIGTRAP */
force_sig_info ( SIGTRAP , & info , tsk ) ;
}
/* Handle synthetic interrupt delivered only by the simulator. */
void __kprobes do_breakpoint ( struct pt_regs * regs , int fault_num )
{
2013-08-06 16:04:13 -04:00
send_sigtrap ( current , regs ) ;
2010-05-28 23:09:12 -04:00
}