2005-04-17 02:20:36 +04:00
/*
* 32 bit ptrace for x86 - 64.
*
* Copyright 2001 , 2002 Andi Kleen , SuSE Labs .
* Some parts copied from arch / i386 / kernel / ptrace . c . See that file for earlier
* copyright .
*
* This allows to access 64 bit processes too ; but there is no way to see the extended
* register contents .
*
* $ Id : ptrace32 . c , v 1.16 2003 / 03 / 14 16 : 06 : 35 ak Exp $
*/
# include <linux/kernel.h>
# include <linux/stddef.h>
# include <linux/sched.h>
# include <linux/syscalls.h>
# include <linux/unistd.h>
# include <linux/mm.h>
# include <linux/ptrace.h>
# include <asm/ptrace.h>
# include <asm/compat.h>
# include <asm/uaccess.h>
# include <asm/user32.h>
# include <asm/user.h>
# include <asm/errno.h>
# include <asm/debugreg.h>
# include <asm/i387.h>
# include <asm/fpu32.h>
2006-01-12 00:46:03 +03:00
/*
* Determines which flags the user has access to [ 1 = access , 0 = no access ] .
* Prohibits changing ID ( 21 ) , VIP ( 20 ) , VIF ( 19 ) , VM ( 17 ) , IOPL ( 12 - 13 ) , IF ( 9 ) .
* Also masks reserved bits ( 31 - 22 , 15 , 5 , 3 , 1 ) .
*/
# define FLAG_MASK 0x54dd5UL
2005-04-17 02:20:36 +04:00
# define R32(l,q) \
case offsetof ( struct user32 , regs . l ) : stack [ offsetof ( struct pt_regs , q ) / 8 ] = val ; break
static int putreg32 ( struct task_struct * child , unsigned regno , u32 val )
{
int i ;
2006-01-12 12:05:38 +03:00
__u64 * stack = ( __u64 * ) task_pt_regs ( child ) ;
2005-04-17 02:20:36 +04:00
switch ( regno ) {
case offsetof ( struct user32 , regs . fs ) :
if ( val & & ( val & 3 ) ! = 3 ) return - EIO ;
2005-08-05 00:41:09 +04:00
child - > thread . fsindex = val & 0xffff ;
2005-04-17 02:20:36 +04:00
break ;
case offsetof ( struct user32 , regs . gs ) :
if ( val & & ( val & 3 ) ! = 3 ) return - EIO ;
2005-08-05 00:41:09 +04:00
child - > thread . gsindex = val & 0xffff ;
2005-04-17 02:20:36 +04:00
break ;
case offsetof ( struct user32 , regs . ds ) :
if ( val & & ( val & 3 ) ! = 3 ) return - EIO ;
child - > thread . ds = val & 0xffff ;
break ;
case offsetof ( struct user32 , regs . es ) :
child - > thread . es = val & 0xffff ;
break ;
case offsetof ( struct user32 , regs . ss ) :
if ( ( val & 3 ) ! = 3 ) return - EIO ;
stack [ offsetof ( struct pt_regs , ss ) / 8 ] = val & 0xffff ;
break ;
case offsetof ( struct user32 , regs . cs ) :
if ( ( val & 3 ) ! = 3 ) return - EIO ;
stack [ offsetof ( struct pt_regs , cs ) / 8 ] = val & 0xffff ;
break ;
R32 ( ebx , rbx ) ;
R32 ( ecx , rcx ) ;
R32 ( edx , rdx ) ;
R32 ( edi , rdi ) ;
R32 ( esi , rsi ) ;
R32 ( ebp , rbp ) ;
R32 ( eax , rax ) ;
R32 ( orig_eax , orig_rax ) ;
R32 ( eip , rip ) ;
R32 ( esp , rsp ) ;
case offsetof ( struct user32 , regs . eflags ) : {
__u64 * flags = & stack [ offsetof ( struct pt_regs , eflags ) / 8 ] ;
val & = FLAG_MASK ;
* flags = val | ( * flags & ~ FLAG_MASK ) ;
break ;
}
case offsetof ( struct user32 , u_debugreg [ 4 ] ) :
case offsetof ( struct user32 , u_debugreg [ 5 ] ) :
return - EIO ;
case offsetof ( struct user32 , u_debugreg [ 0 ] ) :
child - > thread . debugreg0 = val ;
break ;
case offsetof ( struct user32 , u_debugreg [ 1 ] ) :
child - > thread . debugreg1 = val ;
break ;
case offsetof ( struct user32 , u_debugreg [ 2 ] ) :
child - > thread . debugreg2 = val ;
break ;
case offsetof ( struct user32 , u_debugreg [ 3 ] ) :
child - > thread . debugreg3 = val ;
break ;
case offsetof ( struct user32 , u_debugreg [ 6 ] ) :
child - > thread . debugreg6 = val ;
break ;
case offsetof ( struct user32 , u_debugreg [ 7 ] ) :
val & = ~ DR_CONTROL_RESERVED ;
/* See arch/i386/kernel/ptrace.c for an explanation of
* this awkward check . */
for ( i = 0 ; i < 4 ; i + + )
if ( ( 0x5454 > > ( ( val > > ( 16 + 4 * i ) ) & 0xf ) ) & 1 )
return - EIO ;
child - > thread . debugreg7 = val ;
break ;
default :
if ( regno > sizeof ( struct user32 ) | | ( regno & 3 ) )
return - EIO ;
/* Other dummy fields in the virtual user structure are ignored */
break ;
}
return 0 ;
}
# undef R32
# define R32(l,q) \
case offsetof ( struct user32 , regs . l ) : * val = stack [ offsetof ( struct pt_regs , q ) / 8 ] ; break
static int getreg32 ( struct task_struct * child , unsigned regno , u32 * val )
{
2006-01-12 12:05:38 +03:00
__u64 * stack = ( __u64 * ) task_pt_regs ( child ) ;
2005-04-17 02:20:36 +04:00
switch ( regno ) {
case offsetof ( struct user32 , regs . fs ) :
2005-08-05 00:41:09 +04:00
* val = child - > thread . fsindex ;
2005-04-17 02:20:36 +04:00
break ;
case offsetof ( struct user32 , regs . gs ) :
2005-08-05 00:41:09 +04:00
* val = child - > thread . gsindex ;
2005-04-17 02:20:36 +04:00
break ;
case offsetof ( struct user32 , regs . ds ) :
* val = child - > thread . ds ;
break ;
case offsetof ( struct user32 , regs . es ) :
* val = child - > thread . es ;
break ;
R32 ( cs , cs ) ;
R32 ( ss , ss ) ;
R32 ( ebx , rbx ) ;
R32 ( ecx , rcx ) ;
R32 ( edx , rdx ) ;
R32 ( edi , rdi ) ;
R32 ( esi , rsi ) ;
R32 ( ebp , rbp ) ;
R32 ( eax , rax ) ;
R32 ( orig_eax , orig_rax ) ;
R32 ( eip , rip ) ;
R32 ( eflags , eflags ) ;
R32 ( esp , rsp ) ;
case offsetof ( struct user32 , u_debugreg [ 0 ] ) :
* val = child - > thread . debugreg0 ;
break ;
case offsetof ( struct user32 , u_debugreg [ 1 ] ) :
* val = child - > thread . debugreg1 ;
break ;
case offsetof ( struct user32 , u_debugreg [ 2 ] ) :
* val = child - > thread . debugreg2 ;
break ;
case offsetof ( struct user32 , u_debugreg [ 3 ] ) :
* val = child - > thread . debugreg3 ;
break ;
case offsetof ( struct user32 , u_debugreg [ 6 ] ) :
* val = child - > thread . debugreg6 ;
break ;
case offsetof ( struct user32 , u_debugreg [ 7 ] ) :
* val = child - > thread . debugreg7 ;
break ;
default :
if ( regno > sizeof ( struct user32 ) | | ( regno & 3 ) )
return - EIO ;
/* Other dummy fields in the virtual user structure are ignored */
* val = 0 ;
break ;
}
return 0 ;
}
# undef R32
asmlinkage long sys32_ptrace ( long request , u32 pid , u32 addr , u32 data )
{
struct task_struct * child ;
struct pt_regs * childregs ;
void __user * datap = compat_ptr ( data ) ;
int ret ;
__u32 val ;
switch ( request ) {
default :
return sys_ptrace ( request , pid , addr , data ) ;
case PTRACE_PEEKTEXT :
case PTRACE_PEEKDATA :
case PTRACE_POKEDATA :
case PTRACE_POKETEXT :
case PTRACE_POKEUSR :
case PTRACE_PEEKUSR :
case PTRACE_GETREGS :
case PTRACE_SETREGS :
case PTRACE_SETFPREGS :
case PTRACE_GETFPREGS :
case PTRACE_SETFPXREGS :
case PTRACE_GETFPXREGS :
case PTRACE_GETEVENTMSG :
break ;
}
2006-01-08 12:02:33 +03:00
if ( request = = PTRACE_TRACEME )
return ptrace_traceme ( ) ;
child = ptrace_get_task_struct ( pid ) ;
if ( IS_ERR ( child ) )
return PTR_ERR ( child ) ;
ret = ptrace_check_attach ( child , request = = PTRACE_KILL ) ;
if ( ret < 0 )
goto out ;
2005-04-17 02:20:36 +04:00
2006-01-12 12:05:38 +03:00
childregs = task_pt_regs ( child ) ;
2005-04-17 02:20:36 +04:00
switch ( request ) {
case PTRACE_PEEKDATA :
case PTRACE_PEEKTEXT :
ret = 0 ;
if ( access_process_vm ( child , addr , & val , sizeof ( u32 ) , 0 ) ! = sizeof ( u32 ) )
ret = - EIO ;
else
ret = put_user ( val , ( unsigned int __user * ) datap ) ;
break ;
case PTRACE_POKEDATA :
case PTRACE_POKETEXT :
ret = 0 ;
if ( access_process_vm ( child , addr , & data , sizeof ( u32 ) , 1 ) ! = sizeof ( u32 ) )
ret = - EIO ;
break ;
case PTRACE_PEEKUSR :
ret = getreg32 ( child , addr , & val ) ;
if ( ret = = 0 )
ret = put_user ( val , ( __u32 __user * ) datap ) ;
break ;
case PTRACE_POKEUSR :
ret = putreg32 ( child , addr , data ) ;
break ;
case PTRACE_GETREGS : { /* Get all gp regs from the child. */
int i ;
if ( ! access_ok ( VERIFY_WRITE , datap , 16 * 4 ) ) {
ret = - EIO ;
break ;
}
ret = 0 ;
for ( i = 0 ; i < = 16 * 4 ; i + = sizeof ( __u32 ) ) {
getreg32 ( child , i , & val ) ;
ret | = __put_user ( val , ( u32 __user * ) datap ) ;
datap + = sizeof ( u32 ) ;
}
break ;
}
case PTRACE_SETREGS : { /* Set all gp regs in the child. */
unsigned long tmp ;
int i ;
if ( ! access_ok ( VERIFY_READ , datap , 16 * 4 ) ) {
ret = - EIO ;
break ;
}
ret = 0 ;
for ( i = 0 ; i < = 16 * 4 ; i + = sizeof ( u32 ) ) {
ret | = __get_user ( tmp , ( u32 __user * ) datap ) ;
putreg32 ( child , i , tmp ) ;
datap + = sizeof ( u32 ) ;
}
break ;
}
case PTRACE_GETFPREGS :
ret = - EIO ;
if ( ! access_ok ( VERIFY_READ , compat_ptr ( data ) ,
sizeof ( struct user_i387_struct ) ) )
break ;
save_i387_ia32 ( child , datap , childregs , 1 ) ;
ret = 0 ;
break ;
case PTRACE_SETFPREGS :
ret = - EIO ;
if ( ! access_ok ( VERIFY_WRITE , datap ,
sizeof ( struct user_i387_struct ) ) )
break ;
ret = 0 ;
/* don't check EFAULT to be bug-to-bug compatible to i386 */
restore_i387_ia32 ( child , datap , 1 ) ;
break ;
case PTRACE_GETFPXREGS : {
struct user32_fxsr_struct __user * u = datap ;
init_fpu ( child ) ;
ret = - EIO ;
if ( ! access_ok ( VERIFY_WRITE , u , sizeof ( * u ) ) )
break ;
ret = - EFAULT ;
if ( __copy_to_user ( u , & child - > thread . i387 . fxsave , sizeof ( * u ) ) )
break ;
ret = __put_user ( childregs - > cs , & u - > fcs ) ;
ret | = __put_user ( child - > thread . ds , & u - > fos ) ;
break ;
}
case PTRACE_SETFPXREGS : {
struct user32_fxsr_struct __user * u = datap ;
unlazy_fpu ( child ) ;
ret = - EIO ;
if ( ! access_ok ( VERIFY_READ , u , sizeof ( * u ) ) )
break ;
/* no checking to be bug-to-bug compatible with i386 */
__copy_from_user ( & child - > thread . i387 . fxsave , u , sizeof ( * u ) ) ;
set_stopped_child_used_math ( child ) ;
child - > thread . i387 . fxsave . mxcsr & = mxcsr_feature_mask ;
ret = 0 ;
break ;
}
case PTRACE_GETEVENTMSG :
ret = put_user ( child - > ptrace_message , ( unsigned int __user * ) compat_ptr ( data ) ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
2006-01-08 12:02:33 +03:00
out :
2005-04-17 02:20:36 +04:00
put_task_struct ( child ) ;
return ret ;
}