2012-01-24 16:16:35 +10:00
/*
* linux / arch / m68k / kernel / ptrace . c
*
* Copyright ( C ) 1994 by Hamish Macdonald
* Taken from linux / kernel / ptrace . c and modified for M680x0 .
* linux / kernel / ptrace . c is by Ross Biro 1 / 23 / 92 , edited by Linus Torvalds
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
2017-02-08 18:51:37 +01:00
# include <linux/sched/task_stack.h>
2012-01-24 16:16:35 +10:00
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
# include <linux/user.h>
# include <linux/signal.h>
2022-04-27 23:09:37 +10:00
# include <linux/regset.h>
# include <linux/elf.h>
2023-01-12 16:55:28 +13:00
# include <linux/seccomp.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2012-01-24 16:16:35 +10:00
# include <asm/page.h>
# include <asm/processor.h>
/*
* does not yet catch signals sent when the child dies .
* in exit . c or in signal . c .
*/
/* determines which bits in the SR the user has access to. */
/* 1 = access 0 = no access */
# define SR_MASK 0x001f
/* sets the trace bits. */
# define TRACE_BITS 0xC000
# define T1_BIT 0x8000
# define T0_BIT 0x4000
/* Find the stack offset for a register, relative to thread.esp0. */
# define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg)
# define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \
- sizeof ( struct switch_stack ) )
/* Mapping from PT_xxx to the stack offset at which the register is
saved . Notice that usp has no stack - slot and needs to be treated
specially ( see get_reg / put_reg below ) . */
static const int regoff [ ] = {
[ 0 ] = PT_REG ( d1 ) ,
[ 1 ] = PT_REG ( d2 ) ,
[ 2 ] = PT_REG ( d3 ) ,
[ 3 ] = PT_REG ( d4 ) ,
[ 4 ] = PT_REG ( d5 ) ,
[ 5 ] = SW_REG ( d6 ) ,
[ 6 ] = SW_REG ( d7 ) ,
[ 7 ] = PT_REG ( a0 ) ,
[ 8 ] = PT_REG ( a1 ) ,
[ 9 ] = PT_REG ( a2 ) ,
[ 10 ] = SW_REG ( a3 ) ,
[ 11 ] = SW_REG ( a4 ) ,
[ 12 ] = SW_REG ( a5 ) ,
[ 13 ] = SW_REG ( a6 ) ,
[ 14 ] = PT_REG ( d0 ) ,
[ 15 ] = - 1 ,
[ 16 ] = PT_REG ( orig_d0 ) ,
[ 17 ] = PT_REG ( sr ) ,
[ 18 ] = PT_REG ( pc ) ,
} ;
/*
* Get contents of register REGNO in task TASK .
*/
static inline long get_reg ( struct task_struct * task , int regno )
{
unsigned long * addr ;
if ( regno = = PT_USP )
addr = & task - > thread . usp ;
else if ( regno < ARRAY_SIZE ( regoff ) )
addr = ( unsigned long * ) ( task - > thread . esp0 + regoff [ regno ] ) ;
else
return 0 ;
/* Need to take stkadj into account. */
if ( regno = = PT_SR | | regno = = PT_PC ) {
long stkadj = * ( long * ) ( task - > thread . esp0 + PT_REG ( stkadj ) ) ;
addr = ( unsigned long * ) ( ( unsigned long ) addr + stkadj ) ;
/* The sr is actually a 16 bit register. */
if ( regno = = PT_SR )
return * ( unsigned short * ) addr ;
}
return * addr ;
}
/*
* Write contents of register REGNO in task TASK .
*/
static inline int put_reg ( struct task_struct * task , int regno ,
unsigned long data )
{
unsigned long * addr ;
if ( regno = = PT_USP )
addr = & task - > thread . usp ;
else if ( regno < ARRAY_SIZE ( regoff ) )
addr = ( unsigned long * ) ( task - > thread . esp0 + regoff [ regno ] ) ;
else
return - 1 ;
/* Need to take stkadj into account. */
if ( regno = = PT_SR | | regno = = PT_PC ) {
long stkadj = * ( long * ) ( task - > thread . esp0 + PT_REG ( stkadj ) ) ;
addr = ( unsigned long * ) ( ( unsigned long ) addr + stkadj ) ;
/* The sr is actually a 16 bit register. */
if ( regno = = PT_SR ) {
* ( unsigned short * ) addr = data ;
return 0 ;
}
}
* addr = data ;
return 0 ;
}
/*
* Make sure the single step bit is not set .
*/
static inline void singlestep_disable ( struct task_struct * child )
{
unsigned long tmp = get_reg ( child , PT_SR ) & ~ TRACE_BITS ;
put_reg ( child , PT_SR , tmp ) ;
clear_tsk_thread_flag ( child , TIF_DELAYED_TRACE ) ;
}
/*
* Called by kernel / ptrace . c when detaching . .
*/
void ptrace_disable ( struct task_struct * child )
{
singlestep_disable ( child ) ;
}
void user_enable_single_step ( struct task_struct * child )
{
unsigned long tmp = get_reg ( child , PT_SR ) & ~ TRACE_BITS ;
put_reg ( child , PT_SR , tmp | T1_BIT ) ;
set_tsk_thread_flag ( child , TIF_DELAYED_TRACE ) ;
}
2011-03-22 13:39:27 +10:00
# ifdef CONFIG_MMU
2012-01-24 16:16:35 +10:00
void user_enable_block_step ( struct task_struct * child )
{
unsigned long tmp = get_reg ( child , PT_SR ) & ~ TRACE_BITS ;
put_reg ( child , PT_SR , tmp | T0_BIT ) ;
}
2011-03-22 13:39:27 +10:00
# endif
2012-01-24 16:16:35 +10:00
void user_disable_single_step ( struct task_struct * child )
{
singlestep_disable ( child ) ;
}
long arch_ptrace ( struct task_struct * child , long request ,
unsigned long addr , unsigned long data )
{
unsigned long tmp ;
int i , ret = 0 ;
int regno = addr > > 2 ; /* temporary hack. */
unsigned long __user * datap = ( unsigned long __user * ) data ;
switch ( request ) {
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR :
if ( addr & 3 )
goto out_eio ;
if ( regno > = 0 & & regno < 19 ) {
tmp = get_reg ( child , regno ) ;
} else if ( regno > = 21 & & regno < 49 ) {
tmp = child - > thread . fp [ regno - 21 ] ;
/* Convert internal fpu reg representation
* into long double format
*/
if ( FPU_IS_EMU & & ( regno < 45 ) & & ! ( regno % 3 ) )
tmp = ( ( tmp & 0xffff0000 ) < < 15 ) |
( ( tmp & 0x0000ffff ) < < 16 ) ;
# ifndef CONFIG_MMU
} else if ( regno = = 49 ) {
tmp = child - > mm - > start_code ;
} else if ( regno = = 50 ) {
tmp = child - > mm - > start_data ;
} else if ( regno = = 51 ) {
tmp = child - > mm - > end_code ;
# endif
} else
goto out_eio ;
ret = put_user ( tmp , datap ) ;
break ;
case PTRACE_POKEUSR :
/* write the word at location addr in the USER area */
if ( addr & 3 )
goto out_eio ;
if ( regno = = PT_SR ) {
data & = SR_MASK ;
data | = get_reg ( child , PT_SR ) & ~ SR_MASK ;
}
if ( regno > = 0 & & regno < 19 ) {
if ( put_reg ( child , regno , data ) )
goto out_eio ;
} else if ( regno > = 21 & & regno < 48 ) {
/* Convert long double format
* into internal fpu reg representation
*/
if ( FPU_IS_EMU & & ( regno < 45 ) & & ! ( regno % 3 ) ) {
data < < = 15 ;
data = ( data & 0xffff0000 ) |
( ( data & 0x0000ffff ) > > 1 ) ;
}
child - > thread . fp [ regno - 21 ] = data ;
} else
goto out_eio ;
break ;
case PTRACE_GETREGS : /* Get all gp regs from the child. */
for ( i = 0 ; i < 19 ; i + + ) {
tmp = get_reg ( child , i ) ;
ret = put_user ( tmp , datap ) ;
if ( ret )
break ;
datap + + ;
}
break ;
case PTRACE_SETREGS : /* Set all gp regs in the child. */
for ( i = 0 ; i < 19 ; i + + ) {
ret = get_user ( tmp , datap ) ;
if ( ret )
break ;
if ( i = = PT_SR ) {
tmp & = SR_MASK ;
tmp | = get_reg ( child , PT_SR ) & ~ SR_MASK ;
}
put_reg ( child , i , tmp ) ;
datap + + ;
}
break ;
case PTRACE_GETFPREGS : /* Get the child FPU state. */
if ( copy_to_user ( datap , & child - > thread . fp ,
sizeof ( struct user_m68kfp_struct ) ) )
ret = - EFAULT ;
break ;
case PTRACE_SETFPREGS : /* Set the child FPU state. */
if ( copy_from_user ( & child - > thread . fp , datap ,
sizeof ( struct user_m68kfp_struct ) ) )
ret = - EFAULT ;
break ;
case PTRACE_GET_THREAD_AREA :
ret = put_user ( task_thread_info ( child ) - > tp_value , datap ) ;
break ;
default :
ret = ptrace_request ( child , request , addr , data ) ;
break ;
}
return ret ;
out_eio :
return - EIO ;
}
asmlinkage int syscall_trace_enter ( void )
{
int ret = 0 ;
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
2022-01-27 11:46:37 -06:00
ret = ptrace_report_syscall_entry ( task_pt_regs ( current ) ) ;
2023-01-12 16:55:28 +13:00
if ( secure_computing ( ) = = - 1 )
return - 1 ;
2012-01-24 16:16:35 +10:00
return ret ;
}
asmlinkage void syscall_trace_leave ( void )
{
if ( test_thread_flag ( TIF_SYSCALL_TRACE ) )
2022-01-27 11:46:37 -06:00
ptrace_report_syscall_exit ( task_pt_regs ( current ) , 0 ) ;
2012-01-24 16:16:35 +10:00
}
2022-04-27 23:09:37 +10:00
# if defined(CONFIG_BINFMT_ELF_FDPIC) && defined(CONFIG_ELF_CORE)
/*
* Currently the only thing that needs to use regsets for m68k is the
* coredump support of the elf_fdpic loader . Implement the minimum
* definitions required for that .
*/
static int m68k_regset_get ( struct task_struct * target ,
const struct user_regset * regset ,
struct membuf to )
{
struct pt_regs * ptregs = task_pt_regs ( target ) ;
u32 uregs [ ELF_NGREG ] ;
ELF_CORE_COPY_REGS ( uregs , ptregs ) ;
return membuf_write ( & to , uregs , sizeof ( uregs ) ) ;
}
enum m68k_regset {
REGSET_GPR ,
# ifdef CONFIG_FPU
REGSET_FPU ,
# endif
} ;
static const struct user_regset m68k_user_regsets [ ] = {
[ REGSET_GPR ] = {
. core_note_type = NT_PRSTATUS ,
. n = ELF_NGREG ,
. size = sizeof ( u32 ) ,
. align = sizeof ( u16 ) ,
. regset_get = m68k_regset_get ,
} ,
# ifdef CONFIG_FPU
[ REGSET_FPU ] = {
. core_note_type = NT_PRFPREG ,
. n = sizeof ( struct user_m68kfp_struct ) / sizeof ( u32 ) ,
. size = sizeof ( u32 ) ,
. align = sizeof ( u32 ) ,
}
# endif /* CONFIG_FPU */
} ;
static const struct user_regset_view user_m68k_view = {
. name = " m68k " ,
. e_machine = EM_68K ,
. ei_osabi = ELF_OSABI ,
. regsets = m68k_user_regsets ,
. n = ARRAY_SIZE ( m68k_user_regsets )
} ;
const struct user_regset_view * task_user_regset_view ( struct task_struct * task )
{
return & user_m68k_view ;
}
# endif /* CONFIG_BINFMT_ELF_FDPIC && CONFIG_ELF_CORE */