2005-04-17 02:20:36 +04:00
/* ptrace.c: Sparc process tracing support.
*
2008-02-07 10:02:08 +03:00
* Copyright ( C ) 1996 , 2008 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1997 Jakub Jelinek ( jj @ sunsite . mff . cuni . cz )
*
* Based upon code written by Ross Biro , Linus Torvalds , Bob Manson ,
* and David Mosberger .
*
* Added Linux support - miguel ( weird , eh ? , the original code was meant
* to emulate SunOS ) .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
# include <linux/user.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/security.h>
2005-07-11 06:29:45 +04:00
# include <linux/seccomp.h>
# include <linux/audit.h>
2005-05-01 19:59:14 +04:00
# include <linux/signal.h>
2008-02-07 10:02:08 +03:00
# include <linux/regset.h>
# include <linux/compat.h>
# include <linux/elf.h>
2005-04-17 02:20:36 +04:00
# include <asm/asi.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/psrcompat.h>
# include <asm/visasm.h>
# include <asm/spitfire.h>
2005-09-20 07:11:57 +04:00
# include <asm/page.h>
2005-09-30 05:50:34 +04:00
# include <asm/cpudata.h>
2008-03-26 10:46:21 +03:00
# include <asm/cacheflush.h>
# include "entry.h"
2005-04-17 02:20:36 +04:00
/* #define ALLOW_INIT_TRACING */
/*
* Called by kernel / ptrace . c when detaching . .
*
* Make sure single step bits etc are not set .
*/
void ptrace_disable ( struct task_struct * child )
{
/* nothing to do */
}
2005-04-18 05:03:11 +04:00
/* To get the necessary page struct, access_process_vm() first calls
* get_user_pages ( ) . This has done a flush_dcache_page ( ) on the
* accessed page . Then our caller ( copy_ { to , from } _user_page ( ) ) did
* to memcpy to read / write the data from that page .
*
* Now , the only thing we have to do is :
* 1 ) flush the D - cache if it ' s possible than an illegal alias
* has been created
* 2 ) flush the I - cache if this is pre - cheetah and we did a write
*/
void flush_ptrace_access ( struct vm_area_struct * vma , struct page * page ,
unsigned long uaddr , void * kaddr ,
unsigned long len , int write )
{
BUG_ON ( len > PAGE_SIZE ) ;
2006-02-18 02:07:43 +03:00
if ( tlb_type = = hypervisor )
return ;
2008-03-26 14:51:12 +03:00
preempt_disable ( ) ;
2005-04-18 05:03:11 +04:00
# ifdef DCACHE_ALIASING_POSSIBLE
/* If bit 13 of the kernel address we used to access the
* user page is the same as the virtual address that page
* is mapped to in the user ' s address space , we can skip the
* D - cache flush .
*/
2005-09-20 07:11:57 +04:00
if ( ( uaddr ^ ( unsigned long ) kaddr ) & ( 1UL < < 13 ) ) {
2005-04-18 05:03:11 +04:00
unsigned long start = __pa ( kaddr ) ;
unsigned long end = start + len ;
2005-09-30 05:50:34 +04:00
unsigned long dcache_line_size ;
dcache_line_size = local_cpu_data ( ) . dcache_line_size ;
2005-04-18 05:03:11 +04:00
if ( tlb_type = = spitfire ) {
2005-09-30 05:50:34 +04:00
for ( ; start < end ; start + = dcache_line_size )
2005-09-20 07:11:57 +04:00
spitfire_put_dcache_tag ( start & 0x3fe0 , 0x0 ) ;
2005-04-18 05:03:11 +04:00
} else {
2005-09-30 05:50:34 +04:00
start & = ~ ( dcache_line_size - 1 ) ;
for ( ; start < end ; start + = dcache_line_size )
2005-04-18 05:03:11 +04:00
__asm__ __volatile__ (
" stxa %%g0, [%0] %1 \n \t "
" membar #Sync "
: /* no outputs */
2005-09-20 07:11:57 +04:00
: " r " ( start ) ,
2005-04-18 05:03:11 +04:00
" i " ( ASI_DCACHE_INVALIDATE ) ) ;
}
}
# endif
if ( write & & tlb_type = = spitfire ) {
unsigned long start = ( unsigned long ) kaddr ;
unsigned long end = start + len ;
2005-09-30 05:50:34 +04:00
unsigned long icache_line_size ;
icache_line_size = local_cpu_data ( ) . icache_line_size ;
2005-04-18 05:03:11 +04:00
2005-09-30 05:50:34 +04:00
for ( ; start < end ; start + = icache_line_size )
2005-04-18 05:03:11 +04:00
flushi ( start ) ;
}
2008-03-26 14:51:12 +03:00
preempt_enable ( ) ;
2005-04-18 05:03:11 +04:00
}
2008-04-10 06:39:25 +04:00
static int get_from_target ( struct task_struct * target , unsigned long uaddr ,
void * kbuf , int len )
{
if ( target = = current ) {
if ( copy_from_user ( kbuf , ( void __user * ) uaddr , len ) )
return - EFAULT ;
} else {
int len2 = access_process_vm ( target , uaddr , kbuf , len , 0 ) ;
if ( len2 ! = len )
return - EFAULT ;
}
return 0 ;
}
static int set_to_target ( struct task_struct * target , unsigned long uaddr ,
void * kbuf , int len )
{
if ( target = = current ) {
if ( copy_to_user ( ( void __user * ) uaddr , kbuf , len ) )
return - EFAULT ;
} else {
int len2 = access_process_vm ( target , uaddr , kbuf , len , 1 ) ;
if ( len2 ! = len )
return - EFAULT ;
}
return 0 ;
}
static int regwindow64_get ( struct task_struct * target ,
const struct pt_regs * regs ,
struct reg_window * wbuf )
{
unsigned long rw_addr = regs - > u_regs [ UREG_I6 ] ;
if ( test_tsk_thread_flag ( current , TIF_32BIT ) ) {
struct reg_window32 win32 ;
int i ;
if ( get_from_target ( target , rw_addr , & win32 , sizeof ( win32 ) ) )
return - EFAULT ;
for ( i = 0 ; i < 8 ; i + + )
wbuf - > locals [ i ] = win32 . locals [ i ] ;
for ( i = 0 ; i < 8 ; i + + )
wbuf - > ins [ i ] = win32 . ins [ i ] ;
} else {
rw_addr + = STACK_BIAS ;
if ( get_from_target ( target , rw_addr , wbuf , sizeof ( * wbuf ) ) )
return - EFAULT ;
}
return 0 ;
}
static int regwindow64_set ( struct task_struct * target ,
const struct pt_regs * regs ,
struct reg_window * wbuf )
{
unsigned long rw_addr = regs - > u_regs [ UREG_I6 ] ;
if ( test_tsk_thread_flag ( current , TIF_32BIT ) ) {
struct reg_window32 win32 ;
int i ;
for ( i = 0 ; i < 8 ; i + + )
win32 . locals [ i ] = wbuf - > locals [ i ] ;
for ( i = 0 ; i < 8 ; i + + )
win32 . ins [ i ] = wbuf - > ins [ i ] ;
if ( set_to_target ( target , rw_addr , & win32 , sizeof ( win32 ) ) )
return - EFAULT ;
} else {
rw_addr + = STACK_BIAS ;
if ( set_to_target ( target , rw_addr , wbuf , sizeof ( * wbuf ) ) )
return - EFAULT ;
}
return 0 ;
}
2008-02-07 10:02:08 +03:00
enum sparc_regset {
REGSET_GENERAL ,
REGSET_FP ,
} ;
static int genregs64_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 ;
if ( target = = current )
flushw_user ( ) ;
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
regs - > u_regs ,
0 , 16 * sizeof ( u64 ) ) ;
2008-04-10 06:39:25 +04:00
if ( ! ret & & count & & pos < ( 32 * sizeof ( u64 ) ) ) {
struct reg_window window ;
2008-02-07 10:02:08 +03:00
2008-04-10 06:39:25 +04:00
if ( regwindow64_get ( target , regs , & window ) )
return - EFAULT ;
2008-02-07 10:02:08 +03:00
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
2008-04-10 06:39:25 +04:00
& window ,
2008-02-07 10:02:08 +03:00
16 * sizeof ( u64 ) ,
32 * sizeof ( u64 ) ) ;
}
if ( ! ret ) {
/* TSTATE, TPC, TNPC */
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& regs - > tstate ,
32 * sizeof ( u64 ) ,
35 * sizeof ( u64 ) ) ;
}
if ( ! ret ) {
unsigned long y = regs - > y ;
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& y ,
35 * sizeof ( u64 ) ,
36 * sizeof ( u64 ) ) ;
}
2008-04-10 06:39:25 +04:00
if ( ! ret ) {
2008-02-07 10:02:08 +03:00
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
36 * sizeof ( u64 ) , - 1 ) ;
2008-04-10 06:39:25 +04:00
}
2008-02-07 10:02:08 +03:00
return ret ;
}
static int genregs64_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 ;
if ( target = = current )
flushw_user ( ) ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
regs - > u_regs ,
0 , 16 * sizeof ( u64 ) ) ;
2008-04-10 06:39:25 +04:00
if ( ! ret & & count & & pos < ( 32 * sizeof ( u64 ) ) ) {
struct reg_window window ;
2008-02-07 10:02:08 +03:00
2008-04-10 06:39:25 +04:00
if ( regwindow64_get ( target , regs , & window ) )
return - EFAULT ;
2008-02-07 10:02:08 +03:00
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
2008-04-10 06:39:25 +04:00
& window ,
2008-02-07 10:02:08 +03:00
16 * sizeof ( u64 ) ,
32 * sizeof ( u64 ) ) ;
2008-04-10 06:39:25 +04:00
if ( ! ret & &
regwindow64_set ( target , regs , & window ) )
return - EFAULT ;
2008-02-07 10:02:08 +03:00
}
if ( ! ret & & count > 0 ) {
unsigned long tstate ;
/* TSTATE */
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& tstate ,
32 * sizeof ( u64 ) ,
33 * sizeof ( u64 ) ) ;
if ( ! ret ) {
/* Only the condition codes can be modified
* in the % tstate register .
*/
tstate & = ( TSTATE_ICC | TSTATE_XCC ) ;
regs - > tstate & = ~ ( TSTATE_ICC | TSTATE_XCC ) ;
regs - > tstate | = tstate ;
}
}
if ( ! ret ) {
/* TPC, TNPC */
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& regs - > tpc ,
33 * sizeof ( u64 ) ,
35 * sizeof ( u64 ) ) ;
}
if ( ! ret ) {
unsigned long y ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& y ,
35 * sizeof ( u64 ) ,
36 * sizeof ( u64 ) ) ;
if ( ! ret )
regs - > y = y ;
}
if ( ! ret )
ret = user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
36 * sizeof ( u64 ) , - 1 ) ;
return ret ;
}
static int fpregs64_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
const unsigned long * fpregs = task_thread_info ( target ) - > fpregs ;
unsigned long fprs , fsr , gsr ;
int ret ;
if ( target = = current )
save_and_clear_fpu ( ) ;
fprs = task_thread_info ( target ) - > fpsaved [ 0 ] ;
if ( fprs & FPRS_DL )
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
fpregs ,
0 , 16 * sizeof ( u64 ) ) ;
else
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
0 ,
16 * sizeof ( u64 ) ) ;
if ( ! ret ) {
if ( fprs & FPRS_DU )
ret = user_regset_copyout ( & pos , & count ,
& kbuf , & ubuf ,
fpregs + 16 ,
16 * sizeof ( u64 ) ,
32 * sizeof ( u64 ) ) ;
else
ret = user_regset_copyout_zero ( & pos , & count ,
& kbuf , & ubuf ,
16 * sizeof ( u64 ) ,
32 * sizeof ( u64 ) ) ;
}
if ( fprs & FPRS_FEF ) {
fsr = task_thread_info ( target ) - > xfsr [ 0 ] ;
gsr = task_thread_info ( target ) - > gsr [ 0 ] ;
} else {
fsr = gsr = 0 ;
}
if ( ! ret )
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& fsr ,
32 * sizeof ( u64 ) ,
33 * sizeof ( u64 ) ) ;
if ( ! ret )
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& gsr ,
33 * sizeof ( u64 ) ,
34 * sizeof ( u64 ) ) ;
if ( ! ret )
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& fprs ,
34 * sizeof ( u64 ) ,
35 * sizeof ( u64 ) ) ;
if ( ! ret )
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
35 * sizeof ( u64 ) , - 1 ) ;
return ret ;
}
static int fpregs64_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
unsigned long * fpregs = task_thread_info ( target ) - > fpregs ;
unsigned long fprs ;
int ret ;
if ( target = = current )
save_and_clear_fpu ( ) ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
fpregs ,
0 , 32 * sizeof ( u64 ) ) ;
if ( ! ret )
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
task_thread_info ( target ) - > xfsr ,
32 * sizeof ( u64 ) ,
33 * sizeof ( u64 ) ) ;
if ( ! ret )
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
task_thread_info ( target ) - > gsr ,
33 * sizeof ( u64 ) ,
34 * sizeof ( u64 ) ) ;
fprs = task_thread_info ( target ) - > fpsaved [ 0 ] ;
if ( ! ret & & count > 0 ) {
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& fprs ,
34 * sizeof ( u64 ) ,
35 * sizeof ( u64 ) ) ;
}
fprs | = ( FPRS_FEF | FPRS_DL | FPRS_DU ) ;
task_thread_info ( target ) - > fpsaved [ 0 ] = fprs ;
if ( ! ret )
ret = user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
35 * sizeof ( u64 ) , - 1 ) ;
return ret ;
}
static const struct user_regset sparc64_regsets [ ] = {
/* Format is:
* G0 - - > G7
* O0 - - > O7
* L0 - - > L7
* I0 - - > I7
* TSTATE , TPC , TNPC , Y
*/
[ REGSET_GENERAL ] = {
. core_note_type = NT_PRSTATUS ,
. n = 36 * sizeof ( u64 ) ,
. size = sizeof ( u64 ) , . align = sizeof ( u64 ) ,
. get = genregs64_get , . set = genregs64_set
} ,
/* Format is:
* F0 - - > F63
* FSR
* GSR
* FPRS
*/
[ REGSET_FP ] = {
. core_note_type = NT_PRFPREG ,
. n = 35 * sizeof ( u64 ) ,
. size = sizeof ( u64 ) , . align = sizeof ( u64 ) ,
. get = fpregs64_get , . set = fpregs64_set
} ,
} ;
static const struct user_regset_view user_sparc64_view = {
. name = " sparc64 " , . e_machine = EM_SPARCV9 ,
. regsets = sparc64_regsets , . n = ARRAY_SIZE ( sparc64_regsets )
} ;
2008-03-26 14:31:50 +03:00
# ifdef CONFIG_COMPAT
2008-02-07 10:02:08 +03:00
static int genregs32_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 ) ;
compat_ulong_t __user * reg_window ;
compat_ulong_t * k = kbuf ;
compat_ulong_t __user * u = ubuf ;
compat_ulong_t reg ;
if ( target = = current )
flushw_user ( ) ;
pos / = sizeof ( reg ) ;
count / = sizeof ( reg ) ;
if ( kbuf ) {
for ( ; count > 0 & & pos < 16 ; count - - )
* k + + = regs - > u_regs [ pos + + ] ;
reg_window = ( compat_ulong_t __user * ) regs - > u_regs [ UREG_I6 ] ;
2008-04-04 03:55:14 +04:00
if ( target = = current ) {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( get_user ( * k + + , & reg_window [ pos + + ] ) )
return - EFAULT ;
}
} else {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( access_process_vm ( target ,
( unsigned long )
& reg_window [ pos ] ,
k , sizeof ( * k ) , 0 )
! = sizeof ( * k ) )
return - EFAULT ;
k + + ;
pos + + ;
}
2008-02-07 10:02:08 +03:00
}
} else {
for ( ; count > 0 & & pos < 16 ; count - - ) {
if ( put_user ( ( compat_ulong_t ) regs - > u_regs [ pos + + ] , u + + ) )
return - EFAULT ;
}
reg_window = ( compat_ulong_t __user * ) regs - > u_regs [ UREG_I6 ] ;
2008-04-04 03:55:14 +04:00
if ( target = = current ) {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( get_user ( reg , & reg_window [ pos + + ] ) | |
put_user ( reg , u + + ) )
return - EFAULT ;
}
} else {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( access_process_vm ( target ,
( unsigned long )
& reg_window [ pos ] ,
& reg , sizeof ( reg ) , 0 )
! = sizeof ( reg ) )
return - EFAULT ;
if ( access_process_vm ( target ,
( unsigned long ) u ,
& reg , sizeof ( reg ) , 1 )
! = sizeof ( reg ) )
return - EFAULT ;
pos + + ;
u + + ;
}
2008-02-07 10:02:08 +03:00
}
}
while ( count > 0 ) {
switch ( pos ) {
case 32 : /* PSR */
reg = tstate_to_psr ( regs - > tstate ) ;
break ;
case 33 : /* PC */
reg = regs - > tpc ;
break ;
case 34 : /* NPC */
reg = regs - > tnpc ;
break ;
case 35 : /* Y */
reg = regs - > y ;
break ;
case 36 : /* WIM */
case 37 : /* TBR */
reg = 0 ;
break ;
default :
goto finish ;
}
if ( kbuf )
* k + + = reg ;
else if ( put_user ( reg , u + + ) )
return - EFAULT ;
pos + + ;
count - - ;
}
finish :
pos * = sizeof ( reg ) ;
count * = sizeof ( reg ) ;
return user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
38 * sizeof ( reg ) , - 1 ) ;
}
static int genregs32_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 ) ;
compat_ulong_t __user * reg_window ;
const compat_ulong_t * k = kbuf ;
const compat_ulong_t __user * u = ubuf ;
compat_ulong_t reg ;
if ( target = = current )
flushw_user ( ) ;
pos / = sizeof ( reg ) ;
count / = sizeof ( reg ) ;
if ( kbuf ) {
for ( ; count > 0 & & pos < 16 ; count - - )
regs - > u_regs [ pos + + ] = * k + + ;
reg_window = ( compat_ulong_t __user * ) regs - > u_regs [ UREG_I6 ] ;
2008-04-04 03:55:14 +04:00
if ( target = = current ) {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( put_user ( * k + + , & reg_window [ pos + + ] ) )
return - EFAULT ;
}
} else {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( access_process_vm ( target ,
( unsigned long )
& reg_window [ pos ] ,
( void * ) k ,
sizeof ( * k ) , 1 )
! = sizeof ( * k ) )
return - EFAULT ;
k + + ;
pos + + ;
}
2008-02-07 10:02:08 +03:00
}
} else {
for ( ; count > 0 & & pos < 16 ; count - - ) {
if ( get_user ( reg , u + + ) )
return - EFAULT ;
regs - > u_regs [ pos + + ] = reg ;
}
reg_window = ( compat_ulong_t __user * ) regs - > u_regs [ UREG_I6 ] ;
2008-04-04 03:55:14 +04:00
if ( target = = current ) {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( get_user ( reg , u + + ) | |
put_user ( reg , & reg_window [ pos + + ] ) )
return - EFAULT ;
}
} else {
for ( ; count > 0 & & pos < 32 ; count - - ) {
if ( access_process_vm ( target ,
( unsigned long )
u ,
& reg , sizeof ( reg ) , 0 )
! = sizeof ( reg ) )
return - EFAULT ;
if ( access_process_vm ( target ,
( unsigned long )
& reg_window [ pos ] ,
& reg , sizeof ( reg ) , 1 )
! = sizeof ( reg ) )
return - EFAULT ;
pos + + ;
u + + ;
}
2008-02-07 10:02:08 +03:00
}
}
while ( count > 0 ) {
unsigned long tstate ;
if ( kbuf )
reg = * k + + ;
else if ( get_user ( reg , u + + ) )
return - EFAULT ;
switch ( pos ) {
case 32 : /* PSR */
tstate = regs - > tstate ;
tstate & = ~ ( TSTATE_ICC | TSTATE_XCC ) ;
tstate | = psr_to_tstate_icc ( reg ) ;
regs - > tstate = tstate ;
break ;
case 33 : /* PC */
regs - > tpc = reg ;
break ;
case 34 : /* NPC */
regs - > tnpc = reg ;
break ;
case 35 : /* Y */
regs - > y = reg ;
break ;
case 36 : /* WIM */
case 37 : /* TBR */
break ;
default :
goto finish ;
}
pos + + ;
count - - ;
}
finish :
pos * = sizeof ( reg ) ;
count * = sizeof ( reg ) ;
return user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
38 * sizeof ( reg ) , - 1 ) ;
}
static int fpregs32_get ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
void * kbuf , void __user * ubuf )
{
const unsigned long * fpregs = task_thread_info ( target ) - > fpregs ;
compat_ulong_t enabled ;
unsigned long fprs ;
compat_ulong_t fsr ;
int ret = 0 ;
if ( target = = current )
save_and_clear_fpu ( ) ;
fprs = task_thread_info ( target ) - > fpsaved [ 0 ] ;
if ( fprs & FPRS_FEF ) {
fsr = task_thread_info ( target ) - > xfsr [ 0 ] ;
enabled = 1 ;
} else {
fsr = 0 ;
enabled = 0 ;
}
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
fpregs ,
0 , 32 * sizeof ( u32 ) ) ;
if ( ! ret )
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
32 * sizeof ( u32 ) ,
33 * sizeof ( u32 ) ) ;
if ( ! ret )
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& fsr ,
33 * sizeof ( u32 ) ,
34 * sizeof ( u32 ) ) ;
if ( ! ret ) {
compat_ulong_t val ;
val = ( enabled < < 8 ) | ( 8 < < 16 ) ;
ret = user_regset_copyout ( & pos , & count , & kbuf , & ubuf ,
& val ,
34 * sizeof ( u32 ) ,
35 * sizeof ( u32 ) ) ;
}
if ( ! ret )
ret = user_regset_copyout_zero ( & pos , & count , & kbuf , & ubuf ,
35 * sizeof ( u32 ) , - 1 ) ;
return ret ;
}
static int fpregs32_set ( struct task_struct * target ,
const struct user_regset * regset ,
unsigned int pos , unsigned int count ,
const void * kbuf , const void __user * ubuf )
{
unsigned long * fpregs = task_thread_info ( target ) - > fpregs ;
unsigned long fprs ;
int ret ;
if ( target = = current )
save_and_clear_fpu ( ) ;
fprs = task_thread_info ( target ) - > fpsaved [ 0 ] ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
fpregs ,
0 , 32 * sizeof ( u32 ) ) ;
if ( ! ret )
user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
32 * sizeof ( u32 ) ,
33 * sizeof ( u32 ) ) ;
if ( ! ret & & count > 0 ) {
compat_ulong_t fsr ;
unsigned long val ;
ret = user_regset_copyin ( & pos , & count , & kbuf , & ubuf ,
& fsr ,
33 * sizeof ( u32 ) ,
34 * sizeof ( u32 ) ) ;
if ( ! ret ) {
val = task_thread_info ( target ) - > xfsr [ 0 ] ;
val & = 0xffffffff00000000UL ;
val | = fsr ;
task_thread_info ( target ) - > xfsr [ 0 ] = val ;
}
}
fprs | = ( FPRS_FEF | FPRS_DL ) ;
task_thread_info ( target ) - > fpsaved [ 0 ] = fprs ;
if ( ! ret )
ret = user_regset_copyin_ignore ( & pos , & count , & kbuf , & ubuf ,
34 * sizeof ( u32 ) , - 1 ) ;
return ret ;
}
static const struct user_regset sparc32_regsets [ ] = {
/* Format is:
* G0 - - > G7
* O0 - - > O7
* L0 - - > L7
* I0 - - > I7
* PSR , PC , nPC , Y , WIM , TBR
*/
[ REGSET_GENERAL ] = {
. core_note_type = NT_PRSTATUS ,
. n = 38 * sizeof ( u32 ) ,
. size = sizeof ( u32 ) , . align = sizeof ( u32 ) ,
. get = genregs32_get , . set = genregs32_set
} ,
/* Format is:
* F0 - - > F31
* empty 32 - bit word
* FSR ( 32 - - bit word )
* FPU QUEUE COUNT ( 8 - bit char )
* FPU QUEUE ENTRYSIZE ( 8 - bit char )
* FPU ENABLED ( 8 - bit char )
* empty 8 - bit char
* FPU QUEUE ( 64 32 - bit ints )
*/
[ REGSET_FP ] = {
. core_note_type = NT_PRFPREG ,
. n = 99 * sizeof ( u32 ) ,
. size = sizeof ( u32 ) , . align = sizeof ( u32 ) ,
. get = fpregs32_get , . set = fpregs32_set
} ,
} ;
static const struct user_regset_view user_sparc32_view = {
. name = " sparc " , . e_machine = EM_SPARC ,
. regsets = sparc32_regsets , . n = ARRAY_SIZE ( sparc32_regsets )
} ;
2008-03-26 14:31:50 +03:00
# endif /* CONFIG_COMPAT */
2008-02-07 10:02:08 +03:00
const struct user_regset_view * task_user_regset_view ( struct task_struct * task )
{
2008-03-26 14:31:50 +03:00
# ifdef CONFIG_COMPAT
2008-02-07 10:02:08 +03:00
if ( test_tsk_thread_flag ( task , TIF_32BIT ) )
return & user_sparc32_view ;
2008-03-26 14:31:50 +03:00
# endif
2008-02-07 10:02:08 +03:00
return & user_sparc64_view ;
}
2008-03-26 14:31:50 +03:00
# ifdef CONFIG_COMPAT
2008-02-08 09:46:09 +03:00
struct compat_fps {
unsigned int regs [ 32 ] ;
unsigned int fsr ;
unsigned int flags ;
unsigned int extra ;
unsigned int fpqd ;
struct compat_fq {
unsigned int insnaddr ;
unsigned int insn ;
} fpq [ 16 ] ;
} ;
long compat_arch_ptrace ( struct task_struct * child , compat_long_t request ,
compat_ulong_t caddr , compat_ulong_t cdata )
2005-04-17 02:20:36 +04:00
{
2008-04-10 06:39:25 +04:00
const struct user_regset_view * view = task_user_regset_view ( current ) ;
2008-02-08 09:46:09 +03:00
compat_ulong_t caddr2 = task_pt_regs ( current ) - > u_regs [ UREG_I4 ] ;
struct pt_regs32 __user * pregs ;
struct compat_fps __user * fps ;
unsigned long addr2 = caddr2 ;
unsigned long addr = caddr ;
unsigned long data = cdata ;
2008-02-07 16:06:12 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
2008-02-08 09:46:09 +03:00
pregs = ( struct pt_regs32 __user * ) addr ;
fps = ( struct compat_fps __user * ) addr ;
2005-04-17 02:20:36 +04:00
2008-02-08 09:46:09 +03:00
switch ( request ) {
2006-04-02 11:28:10 +04:00
case PTRACE_PEEKUSR :
2008-02-07 14:00:17 +03:00
ret = ( addr ! = 0 ) ? - EIO : 0 ;
break ;
2006-04-02 11:28:10 +04:00
2008-02-08 09:46:09 +03:00
case PTRACE_GETREGS :
2008-02-07 16:06:12 +03:00
ret = copy_regset_to_user ( child , view , REGSET_GENERAL ,
32 * sizeof ( u32 ) ,
4 * sizeof ( u32 ) ,
& pregs - > psr ) ;
if ( ! ret )
ret = copy_regset_to_user ( child , view , REGSET_GENERAL ,
1 * sizeof ( u32 ) ,
15 * sizeof ( u32 ) ,
& pregs - > u_regs [ 0 ] ) ;
2008-02-07 14:00:17 +03:00
break ;
2008-02-07 16:06:12 +03:00
2008-02-08 09:46:09 +03:00
case PTRACE_SETREGS :
2008-02-07 16:06:12 +03:00
ret = copy_regset_from_user ( child , view , REGSET_GENERAL ,
32 * sizeof ( u32 ) ,
4 * sizeof ( u32 ) ,
& pregs - > psr ) ;
if ( ! ret )
ret = copy_regset_from_user ( child , view , REGSET_GENERAL ,
1 * sizeof ( u32 ) ,
15 * sizeof ( u32 ) ,
& pregs - > u_regs [ 0 ] ) ;
2008-02-07 14:00:17 +03:00
break ;
2008-02-08 09:46:09 +03:00
case PTRACE_GETFPREGS :
2008-02-07 16:06:12 +03:00
ret = copy_regset_to_user ( child , view , REGSET_FP ,
0 * sizeof ( u32 ) ,
32 * sizeof ( u32 ) ,
& fps - > regs [ 0 ] ) ;
if ( ! ret )
ret = copy_regset_to_user ( child , view , REGSET_FP ,
33 * sizeof ( u32 ) ,
1 * sizeof ( u32 ) ,
& fps - > fsr ) ;
if ( ! ret ) {
if ( __put_user ( 0 , & fps - > flags ) | |
__put_user ( 0 , & fps - > extra ) | |
__put_user ( 0 , & fps - > fpqd ) | |
clear_user ( & fps - > fpq [ 0 ] , 32 * sizeof ( unsigned int ) ) )
ret = - EFAULT ;
}
2008-02-07 14:00:17 +03:00
break ;
2008-02-08 09:46:09 +03:00
case PTRACE_SETFPREGS :
2008-02-07 16:06:12 +03:00
ret = copy_regset_from_user ( child , view , REGSET_FP ,
0 * sizeof ( u32 ) ,
32 * sizeof ( u32 ) ,
& fps - > regs [ 0 ] ) ;
if ( ! ret )
ret = copy_regset_from_user ( child , view , REGSET_FP ,
33 * sizeof ( u32 ) ,
1 * sizeof ( u32 ) ,
& fps - > fsr ) ;
2008-02-07 14:00:17 +03:00
break ;
2008-02-08 09:46:09 +03:00
case PTRACE_READTEXT :
case PTRACE_READDATA :
ret = ptrace_readdata ( child , addr ,
( char __user * ) addr2 , data ) ;
if ( ret = = data )
ret = 0 ;
else if ( ret > = 0 )
ret = - EIO ;
break ;
case PTRACE_WRITETEXT :
case PTRACE_WRITEDATA :
ret = ptrace_writedata ( child , ( char __user * ) addr2 ,
addr , data ) ;
if ( ret = = data )
ret = 0 ;
else if ( ret > = 0 )
ret = - EIO ;
break ;
default :
ret = compat_ptrace_request ( child , request , addr , data ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 09:46:09 +03:00
return ret ;
}
2008-03-26 14:31:50 +03:00
# endif /* CONFIG_COMPAT */
2008-02-08 09:46:09 +03:00
struct fps {
unsigned int regs [ 64 ] ;
unsigned long fsr ;
} ;
long arch_ptrace ( struct task_struct * child , long request , long addr , long data )
{
2008-04-10 06:39:25 +04:00
const struct user_regset_view * view = task_user_regset_view ( current ) ;
2008-02-08 09:46:09 +03:00
unsigned long addr2 = task_pt_regs ( current ) - > u_regs [ UREG_I4 ] ;
2008-03-26 10:46:21 +03:00
struct pt_regs __user * pregs ;
struct fps __user * fps ;
2008-02-08 09:46:09 +03:00
int ret ;
2008-03-26 10:46:21 +03:00
pregs = ( struct pt_regs __user * ) ( unsigned long ) addr ;
fps = ( struct fps __user * ) ( unsigned long ) addr ;
2008-02-08 09:46:09 +03:00
switch ( request ) {
case PTRACE_PEEKUSR :
ret = ( addr ! = 0 ) ? - EIO : 0 ;
break ;
2008-02-07 14:00:17 +03:00
2008-02-08 09:46:09 +03:00
case PTRACE_GETREGS64 :
ret = copy_regset_to_user ( child , view , REGSET_GENERAL ,
1 * sizeof ( u64 ) ,
15 * sizeof ( u64 ) ,
& pregs - > u_regs [ 0 ] ) ;
if ( ! ret ) {
/* XXX doesn't handle 'y' register correctly XXX */
ret = copy_regset_to_user ( child , view , REGSET_GENERAL ,
32 * sizeof ( u64 ) ,
4 * sizeof ( u64 ) ,
& pregs - > tstate ) ;
}
break ;
case PTRACE_SETREGS64 :
ret = copy_regset_from_user ( child , view , REGSET_GENERAL ,
1 * sizeof ( u64 ) ,
15 * sizeof ( u64 ) ,
& pregs - > u_regs [ 0 ] ) ;
if ( ! ret ) {
/* XXX doesn't handle 'y' register correctly XXX */
ret = copy_regset_from_user ( child , view , REGSET_GENERAL ,
32 * sizeof ( u64 ) ,
4 * sizeof ( u64 ) ,
& pregs - > tstate ) ;
}
break ;
case PTRACE_GETFPREGS64 :
ret = copy_regset_to_user ( child , view , REGSET_FP ,
0 * sizeof ( u64 ) ,
33 * sizeof ( u64 ) ,
fps ) ;
break ;
case PTRACE_SETFPREGS64 :
2008-02-07 16:06:12 +03:00
ret = copy_regset_to_user ( child , view , REGSET_FP ,
0 * sizeof ( u64 ) ,
33 * sizeof ( u64 ) ,
fps ) ;
2008-02-07 14:00:17 +03:00
break ;
2005-04-17 02:20:36 +04:00
case PTRACE_READTEXT :
2008-02-07 14:00:17 +03:00
case PTRACE_READDATA :
ret = ptrace_readdata ( child , addr ,
( char __user * ) addr2 , data ) ;
if ( ret = = data )
ret = 0 ;
else if ( ret > = 0 )
ret = - EIO ;
break ;
2005-04-17 02:20:36 +04:00
case PTRACE_WRITETEXT :
2008-02-07 14:00:17 +03:00
case PTRACE_WRITEDATA :
ret = ptrace_writedata ( child , ( char __user * ) addr2 ,
addr , data ) ;
if ( ret = = data )
ret = 0 ;
else if ( ret > = 0 )
ret = - EIO ;
break ;
2005-04-17 02:20:36 +04:00
2008-02-07 14:00:17 +03:00
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 14:00:17 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-07-11 03:55:48 +04:00
asmlinkage void syscall_trace ( struct pt_regs * regs , int syscall_exit_p )
2005-04-17 02:20:36 +04:00
{
2005-07-11 03:49:28 +04:00
/* do the secure computing check first */
2005-07-11 03:55:48 +04:00
secure_computing ( regs - > u_regs [ UREG_G1 ] ) ;
2005-07-11 03:49:28 +04:00
2005-07-11 06:29:45 +04:00
if ( unlikely ( current - > audit_context ) & & syscall_exit_p ) {
unsigned long tstate = regs - > tstate ;
int result = AUDITSC_SUCCESS ;
if ( unlikely ( tstate & ( TSTATE_XCARRY | TSTATE_ICARRY ) ) )
result = AUDITSC_FAILURE ;
2006-03-30 05:23:36 +04:00
audit_syscall_exit ( result , regs - > u_regs [ UREG_I0 ] ) ;
2005-07-11 06:29:45 +04:00
}
2005-04-17 02:20:36 +04:00
if ( ! ( current - > ptrace & PT_PTRACED ) )
2005-07-11 06:29:45 +04:00
goto out ;
if ( ! test_thread_flag ( TIF_SYSCALL_TRACE ) )
goto out ;
2005-04-17 02:20:36 +04:00
ptrace_notify ( SIGTRAP | ( ( current - > ptrace & PT_TRACESYSGOOD )
? 0x80 : 0 ) ) ;
/*
* this isn ' t the same as continuing with a signal , but it will do
* for normal use . strace only continues with a signal if the
* stopping signal is not SIGTRAP . - brl
*/
if ( current - > exit_code ) {
2005-07-11 03:49:28 +04:00
send_sig ( current - > exit_code , current , 1 ) ;
2005-04-17 02:20:36 +04:00
current - > exit_code = 0 ;
}
2005-07-11 06:29:45 +04:00
out :
if ( unlikely ( current - > audit_context ) & & ! syscall_exit_p )
2006-03-30 05:23:36 +04:00
audit_syscall_entry ( ( test_thread_flag ( TIF_32BIT ) ?
2005-07-11 06:29:45 +04:00
AUDIT_ARCH_SPARC :
AUDIT_ARCH_SPARC64 ) ,
regs - > u_regs [ UREG_G1 ] ,
regs - > u_regs [ UREG_I0 ] ,
regs - > u_regs [ UREG_I1 ] ,
regs - > u_regs [ UREG_I2 ] ,
regs - > u_regs [ UREG_I3 ] ) ;
2005-04-17 02:20:36 +04:00
}