URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [s390x/] [kernel/] [ptrace.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * arch/s390/kernel/ptrace.c * * S390 version * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), * * Based on PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * * Derived from "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 * * Modified by Cort Dougan (cort@cs.nmt.edu) * * * This file is subject to the terms and conditions of the GNU General * Public License. See the file README.legal in the main directory of * this archive for more details. */ #include <stddef.h> #include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> #include <asm/segment.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/system.h> #include <asm/uaccess.h> #ifdef CONFIG_S390_SUPPORT #include "linux32.h" #else #define parent_31bit 0 #endif void FixPerRegisters(struct task_struct *task) { struct pt_regs *regs = __KSTK_PTREGS(task); per_struct *per_info= (per_struct *)&task->thread.per_info; per_info->control_regs.bits.em_instruction_fetch = per_info->single_step | per_info->instruction_fetch; if (per_info->single_step) { per_info->control_regs.bits.starting_addr=0; #ifdef CONFIG_S390_SUPPORT if (current->thread.flags & S390_FLAG_31BIT) { per_info->control_regs.bits.ending_addr=0x7fffffffUL; } else #endif { per_info->control_regs.bits.ending_addr=-1L; } } else { per_info->control_regs.bits.starting_addr= per_info->starting_addr; per_info->control_regs.bits.ending_addr= per_info->ending_addr; } /* if any of the control reg tracing bits are on we switch on per in the psw */ if (per_info->control_regs.words.cr[0] & PER_EM_MASK) regs->psw.mask |= PSW_PER_MASK; else regs->psw.mask &= ~PSW_PER_MASK; if (per_info->control_regs.bits.storage_alt_space_ctl) task->thread.user_seg |= USER_STD_MASK; else task->thread.user_seg &= ~USER_STD_MASK; } void set_single_step(struct task_struct *task) { per_struct *per_info= (per_struct *) &task->thread.per_info; per_info->single_step = 1; /* Single step */ FixPerRegisters (task); } void clear_single_step(struct task_struct *task) { per_struct *per_info= (per_struct *) &task->thread.per_info; per_info->single_step = 0; FixPerRegisters (task); } int ptrace_usercopy(addr_t realuseraddr, addr_t copyaddr, int len, int tofromuser, int writeuser, unsigned long mask) { unsigned long *realuserptr, *copyptr; unsigned long tempuser; int retval; retval = 0; realuserptr = (unsigned long *) realuseraddr; copyptr = (unsigned long *) copyaddr; if (writeuser && realuserptr == NULL) return 0; if (mask != -1L) { tempuser = *realuserptr; if (!writeuser) { tempuser &= mask; realuserptr = &tempuser; } } if (tofromuser) { if (writeuser) { retval = copy_from_user(realuserptr, copyptr, len); } else { if (realuserptr == NULL) retval = clear_user(copyptr, len); else retval = copy_to_user(copyptr,realuserptr,len); } retval = retval ? -EFAULT : 0; } else { if (writeuser) memcpy(realuserptr, copyptr, len); else memcpy(copyptr, realuserptr, len); } if (mask != -1L && writeuser) *realuserptr = (*realuserptr & mask) | (tempuser & ~mask); return retval; } #ifdef CONFIG_S390_SUPPORT typedef struct { __u32 cr[3]; } per_cr_words32 __attribute__((packed)); typedef struct { __u16 perc_atmid; /* 0x096 */ __u32 address; /* 0x098 */ __u8 access_id; /* 0x0a1 */ } per_lowcore_words32 __attribute__((packed)); typedef struct { union { per_cr_words32 words; } control_regs __attribute__((packed)); /* * Use these flags instead of setting em_instruction_fetch * directly they are used so that single stepping can be * switched on & off while not affecting other tracing */ unsigned single_step : 1; unsigned instruction_fetch : 1; unsigned : 30; /* * These addresses are copied into cr10 & cr11 if single * stepping is switched off */ __u32 starting_addr; __u32 ending_addr; union { per_lowcore_words32 words; } lowcore; } per_struct32 __attribute__((packed)); struct user_regs_struct32 { _psw_t32 psw; u32 gprs[NUM_GPRS]; u32 acrs[NUM_ACRS]; u32 orig_gpr2; s390_fp_regs fp_regs; /* * These per registers are in here so that gdb can modify them * itself as there is no "official" ptrace interface for hardware * watchpoints. This is the way intel does it. */ per_struct32 per_info; u32 ieee_instruction_pointer; /* Used to give failing instruction back to user for ieee exceptions */ }; struct user32 { /* We start with the registers, to mimic the way that "memory" is returned from the ptrace(3,...) function. */ struct user_regs_struct32 regs; /* Where the registers are actually stored */ /* The rest of this junk is to help gdb figure out what goes where */ u32 u_tsize; /* Text segment size (pages). */ u32 u_dsize; /* Data segment size (pages). */ u32 u_ssize; /* Stack segment size (pages). */ u32 start_code; /* Starting virtual address of text. */ u32 start_stack; /* Starting virtual address of stack area. This is actually the bottom of the stack, the top of the stack is always found in the esp register. */ s32 signal; /* Signal that caused the core dump. */ u32 u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ u32 magic; /* To uniquely identify a core file */ char u_comm[32]; /* User command that was responsible */ }; #define PT32_PSWMASK 0x0 #define PT32_PSWADDR 0x04 #define PT32_GPR0 0x08 #define PT32_GPR15 0x44 #define PT32_ACR0 0x48 #define PT32_ACR15 0x84 #define PT32_ORIGGPR2 0x88 #define PT32_FPC 0x90 #define PT32_FPR0_HI 0x98 #define PT32_FPR15_LO 0x114 #define PT32_CR_9 0x118 #define PT32_CR_11 0x120 #define PT32_IEEE_IP 0x13C #define PT32_LASTOFF PT32_IEEE_IP #define PT32_ENDREGS 0x140-1 #define U32OFFSETOF(member) offsetof(struct user32,regs.member) #define U64OFFSETOF(member) offsetof(struct user,regs.member) #define U6432DIFF(member) (U64OFFSETOF(member) - U32OFFSETOF(member)) #define PT_SINGLE_STEP (PT_CR_11+8) #define PT32_SINGLE_STEP (PT32_CR_11+4) #endif /* CONFIG_S390_SUPPORT */ int copy_user(struct task_struct *task,saddr_t useraddr, addr_t copyaddr, int len, int tofromuser, int writingtouser) { int copylen=0,copymax; addr_t realuseraddr; saddr_t enduseraddr; unsigned long mask; #ifdef CONFIG_S390_SUPPORT int parent_31bit=current->thread.flags & S390_FLAG_31BIT; int skip; #endif enduseraddr=useraddr+len; if ((useraddr<0||useraddr&3||enduseraddr&3)|| #ifdef CONFIG_S390_SUPPORT (parent_31bit && enduseraddr > sizeof(struct user32)) || #endif enduseraddr > sizeof(struct user)) return (-EIO); #ifdef CONFIG_S390_SUPPORT if(parent_31bit) { if(useraddr != PT32_PSWMASK) { if (useraddr == PT32_PSWADDR) useraddr = PT_PSWADDR+4; else if(useraddr <= PT32_GPR15) useraddr = ((useraddr-PT32_GPR0)*2) + PT_GPR0+4; else if(useraddr <= PT32_ACR15) useraddr += PT_ACR0-PT32_ACR0; else if(useraddr == PT32_ORIGGPR2) useraddr = PT_ORIGGPR2+4; else if(useraddr <= PT32_FPR15_LO) useraddr += PT_FPR0-PT32_FPR0_HI; else if(useraddr <= PT32_CR_11) useraddr = ((useraddr-PT32_CR_9)*2) + PT_CR_9+4; else if(useraddr == PT32_SINGLE_STEP) useraddr = PT_SINGLE_STEP; else if(useraddr <= U32OFFSETOF(per_info.ending_addr)) useraddr = (((useraddr-U32OFFSETOF(per_info.starting_addr)))*2) + U64OFFSETOF(per_info.starting_addr)+4; else if( useraddr == U32OFFSETOF(per_info.lowcore.words.perc_atmid)) useraddr = U64OFFSETOF(per_info.lowcore.words.perc_atmid); else if( useraddr == U32OFFSETOF(per_info.lowcore.words.address)) useraddr = U64OFFSETOF(per_info.lowcore.words.address)+4; else if(useraddr == U32OFFSETOF(per_info.lowcore.words.access_id)) useraddr = U64OFFSETOF(per_info.lowcore.words.access_id); else if(useraddr == PT32_IEEE_IP) useraddr = PT_IEEE_IP+4; } } #endif /* CONFIG_S390_SUPPORT */ while(len>0) { #ifdef CONFIG_S390_SUPPORT skip=0; #endif mask=PSW_ADDR_MASK; if(useraddr<PT_FPC) { realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr; if(useraddr<(PT_PSWMASK+8)) { if(parent_31bit) { copymax=PT_PSWMASK+4; #ifdef CONFIG_S390_SUPPORT skip=8; #endif } else { copymax=PT_PSWMASK+8; } if(writingtouser) mask=PSW_MASK_DEBUGCHANGE; } else if(useraddr<(PT_PSWADDR+8)) { copymax=PT_PSWADDR+8; mask=PSW_ADDR_DEBUGCHANGE; #ifdef CONFIG_S390_SUPPORT if(parent_31bit) skip=4; #endif } else { #ifdef CONFIG_S390_SUPPORT if(parent_31bit && useraddr <= PT_GPR15+4) { copymax=useraddr+4; if(useraddr<PT_GPR15+4) skip=4; } else #endif copymax=PT_FPC; } } else if(useraddr<(PT_FPR15+sizeof(freg_t))) { copymax=(PT_FPR15+sizeof(freg_t)); realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]); } else if(useraddr<sizeof(struct user_regs_struct)) { #ifdef CONFIG_S390_SUPPORT if( parent_31bit && useraddr <= PT_IEEE_IP+4) { switch(useraddr) { case PT_CR_11+4: case U64OFFSETOF(per_info.ending_addr)+4: case U64OFFSETOF(per_info.lowcore.words.address)+4: copymax=useraddr+4; break; case PT_SINGLE_STEP: case U64OFFSETOF(per_info.lowcore.words.perc_atmid): /* We copy 2 bytes in excess for the atmid member this also gets around */ /* alignment for this member in 32 bit */ skip=8; copymax=useraddr+4; break; default: copymax=useraddr+4; skip=4; } } else #endif { copymax=sizeof(struct user_regs_struct); } realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]); } else { copymax=sizeof(struct user); realuseraddr=(addr_t)NULL; } copylen=copymax-useraddr; copylen=(copylen>len ? len:copylen); if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask)) return (-EIO); copyaddr+=copylen; len-=copylen; useraddr+=copylen #if CONFIG_S390_SUPPORT +skip #endif ; } FixPerRegisters(task); return(0); } /* * Called by kernel/ptrace.c when detaching.. * * Make sure single step bits etc are not set. */ void ptrace_disable(struct task_struct *child) { /* make sure the single step bit is not set. */ clear_single_step(child); } typedef struct { __u32 len; __u32 kernel_addr; __u32 process_addr; } ptrace_area_emu31; asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret = -EPERM; int copied; #ifdef CONFIG_S390_SUPPORT int parent_31bit; int sizeof_parent_long; u8 *dataptr; #else #define sizeof_parent_long 8 #define dataptr (u8 *)&data #endif lock_kernel(); if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; // printk("child=%lX child->flags=%lX",child,child->flags); /* I added child!=current line so we can get the */ /* ieee_instruction_pointer from the user structure DJB */ if(child!=current) { if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; } #ifdef CONFIG_S390_SUPPORT parent_31bit=(current->thread.flags & S390_FLAG_31BIT); sizeof_parent_long=(parent_31bit ? 4:8); dataptr=&(((u8 *)&data)[parent_31bit ? 4:0]); #endif switch (request) { /* If I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { u8 tmp[8]; copied = access_process_vm(child, addr, tmp, sizeof_parent_long, 0); ret = -EIO; if (copied != sizeof_parent_long) break; ret = copy_to_user((void *)data,tmp,sizeof_parent_long); ret = ret ? -EFAULT : 0; break; } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: ret=copy_user(child,addr,data,sizeof_parent_long,1,0); break; /* If I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child, addr,dataptr, sizeof_parent_long, 1) == sizeof_parent_long) break; ret = -EIO; break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret=copy_user(child,addr,(addr_t)dataptr,sizeof_parent_long,0,1); break; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: /* restart after signal. */ ret = -EIO; if ((unsigned long) data >= _NSIG) break; if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); ret = 0; break; /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; clear_single_step(child); wake_up_process(child); /* make sure the single step bit is not set. */ break; case PTRACE_SINGLESTEP: /* set the trap flag. */ ret = -EIO; if ((unsigned long) data >= _NSIG) break; child->ptrace &= ~PT_TRACESYS; child->exit_code = data; set_single_step(child); /* give it a chance to run. */ wake_up_process(child); ret = 0; break; case PTRACE_DETACH: /* detach a process that was attached. */ ret = ptrace_detach(child, data); break; case PTRACE_PEEKUSR_AREA: case PTRACE_POKEUSR_AREA: if(parent_31bit) { ptrace_area_emu31 parea; if(copy_from_user(&parea,(void *)addr,sizeof(parea))==0) ret=copy_user(child,parea.kernel_addr,parea.process_addr, parea.len,1,(request==PTRACE_POKEUSR_AREA)); else ret = -EFAULT; } else { ptrace_area parea; if(copy_from_user(&parea,(void *)addr,sizeof(parea))==0) ret=copy_user(child,parea.kernel_addr,parea.process_addr, parea.len,1,(request==PTRACE_POKEUSR_AREA)); else ret = -EFAULT; } break; default: ret = -EIO; break; } out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; } asmlinkage void syscall_trace(void) { lock_kernel(); if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) != (PT_PTRACED|PT_TRACESYS)) goto out; current->exit_code = SIGTRAP; set_current_state(TASK_STOPPED); notify_parent(current, SIGCHLD); schedule(); /* * 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) { send_sig(current->exit_code, current, 1); current->exit_code = 0; } out: unlock_kernel(); }