diff options
Diffstat (limited to 'arch/sparc/kernel/ptrace.c')
| -rw-r--r-- | arch/sparc/kernel/ptrace.c | 621 |
1 files changed, 0 insertions, 621 deletions
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c deleted file mode 100644 index 475c4c13462..00000000000 --- a/arch/sparc/kernel/ptrace.c +++ /dev/null @@ -1,621 +0,0 @@ -/* ptrace.c: Sparc process tracing support. - * - * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) - * - * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, - * and David Mosberger. - * - * Added Linux support -miguel (weird, eh?, the orignal 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> -#include <linux/signal.h> - -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/uaccess.h> - -#define MAGIC_CONSTANT 0x80000000 - - -/* Returning from ptrace is a bit tricky because the syscall return - * low level code assumes any value returned which is negative and - * is a valid errno will mean setting the condition codes to indicate - * an error return. This doesn't work, so we have this hook. - */ -static inline void pt_error_return(struct pt_regs *regs, unsigned long error) -{ - regs->u_regs[UREG_I0] = error; - regs->psr |= PSR_C; - regs->pc = regs->npc; - regs->npc += 4; -} - -static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) -{ - regs->u_regs[UREG_I0] = value; - regs->psr &= ~PSR_C; - regs->pc = regs->npc; - regs->npc += 4; -} - -static void -pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr) -{ - if (put_user(value, addr)) { - pt_error_return(regs, EFAULT); - return; - } - regs->u_regs[UREG_I0] = 0; - regs->psr &= ~PSR_C; - regs->pc = regs->npc; - regs->npc += 4; -} - -static void -pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr) -{ - if (current->personality == PER_SUNOS) - pt_succ_return (regs, val); - else - pt_succ_return_linux (regs, val, addr); -} - -/* Fuck me gently with a chainsaw... */ -static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset, - struct task_struct *tsk, long __user *addr) -{ - struct pt_regs *cregs = tsk->thread.kregs; - struct thread_info *t = tsk->thread_info; - int v; - - if(offset >= 1024) - offset -= 1024; /* whee... */ - if(offset & ((sizeof(unsigned long) - 1))) { - pt_error_return(regs, EIO); - return; - } - if(offset >= 16 && offset < 784) { - offset -= 16; offset >>= 2; - pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); - return; - } - if(offset >= 784 && offset < 832) { - offset -= 784; offset >>= 2; - pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); - return; - } - switch(offset) { - case 0: - v = t->ksp; - break; - case 4: - v = t->kpc; - break; - case 8: - v = t->kpsr; - break; - case 12: - v = t->uwinmask; - break; - case 832: - v = t->w_saved; - break; - case 896: - v = cregs->u_regs[UREG_I0]; - break; - case 900: - v = cregs->u_regs[UREG_I1]; - break; - case 904: - v = cregs->u_regs[UREG_I2]; - break; - case 908: - v = cregs->u_regs[UREG_I3]; - break; - case 912: - v = cregs->u_regs[UREG_I4]; - break; - case 916: - v = cregs->u_regs[UREG_I5]; - break; - case 920: - v = cregs->u_regs[UREG_I6]; - break; - case 924: - if(tsk->thread.flags & MAGIC_CONSTANT) - v = cregs->u_regs[UREG_G1]; - else - v = 0; - break; - case 940: - v = cregs->u_regs[UREG_I0]; - break; - case 944: - v = cregs->u_regs[UREG_I1]; - break; - - case 948: - /* Isn't binary compatibility _fun_??? */ - if(cregs->psr & PSR_C) - v = cregs->u_regs[UREG_I0] << 24; - else - v = 0; - break; - - /* Rest of them are completely unsupported. */ - default: - printk("%s [%d]: Wants to read user offset %ld\n", - current->comm, current->pid, offset); - pt_error_return(regs, EIO); - return; - } - if (current->personality == PER_SUNOS) - pt_succ_return (regs, v); - else - pt_succ_return_linux (regs, v, addr); - return; -} - -static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset, - struct task_struct *tsk) -{ - struct pt_regs *cregs = tsk->thread.kregs; - struct thread_info *t = tsk->thread_info; - unsigned long value = regs->u_regs[UREG_I3]; - - if(offset >= 1024) - offset -= 1024; /* whee... */ - if(offset & ((sizeof(unsigned long) - 1))) - goto failure; - if(offset >= 16 && offset < 784) { - offset -= 16; offset >>= 2; - *(((unsigned long *)(&t->reg_window[0]))+offset) = value; - goto success; - } - if(offset >= 784 && offset < 832) { - offset -= 784; offset >>= 2; - *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; - goto success; - } - switch(offset) { - case 896: - cregs->u_regs[UREG_I0] = value; - break; - case 900: - cregs->u_regs[UREG_I1] = value; - break; - case 904: - cregs->u_regs[UREG_I2] = value; - break; - case 908: - cregs->u_regs[UREG_I3] = value; - break; - case 912: - cregs->u_regs[UREG_I4] = value; - break; - case 916: - cregs->u_regs[UREG_I5] = value; - break; - case 920: - cregs->u_regs[UREG_I6] = value; - break; - case 924: - cregs->u_regs[UREG_I7] = value; - break; - case 940: - cregs->u_regs[UREG_I0] = value; - break; - case 944: - cregs->u_regs[UREG_I1] = value; - break; - - /* Rest of them are completely unsupported or "no-touch". */ - default: - printk("%s [%d]: Wants to write user offset %ld\n", - current->comm, current->pid, offset); - goto failure; - } -success: - pt_succ_return(regs, 0); - return; -failure: - pt_error_return(regs, EIO); - return; -} - -/* #define ALLOW_INIT_TRACING */ -/* #define DEBUG_PTRACE */ - -#ifdef DEBUG_PTRACE -char *pt_rq [] = { - /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", - /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", - /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", - /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", - /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", - /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", - /* 24 */ "SYSCALL", "" -}; -#endif - -/* - * 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 */ -} - -asmlinkage void do_ptrace(struct pt_regs *regs) -{ - unsigned long request = regs->u_regs[UREG_I0]; - unsigned long pid = regs->u_regs[UREG_I1]; - unsigned long addr = regs->u_regs[UREG_I2]; - unsigned long data = regs->u_regs[UREG_I3]; - unsigned long addr2 = regs->u_regs[UREG_I4]; - struct task_struct *child; - int ret; - - lock_kernel(); -#ifdef DEBUG_PTRACE - { - char *s; - - if ((request >= 0) && (request <= 24)) - s = pt_rq [request]; - else - s = "unknown"; - - if (request == PTRACE_POKEDATA && data == 0x91d02001){ - printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n", - pid, addr, addr2); - } else - printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n", - s, (int) request, (int) pid, addr, data, addr2); - } -#endif - if (request == PTRACE_TRACEME) { - int my_ret; - - /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) { - pt_error_return(regs, EPERM); - goto out; - } - my_ret = security_ptrace(current->parent, current); - if (my_ret) { - pt_error_return(regs, -my_ret); - goto out; - } - - /* set the ptrace bit in the process flags. */ - current->ptrace |= PT_PTRACED; - pt_succ_return(regs, 0); - goto out; - } -#ifndef ALLOW_INIT_TRACING - if (pid == 1) { - /* Can't dork with init. */ - pt_error_return(regs, EPERM); - goto out; - } -#endif - read_lock(&tasklist_lock); - child = find_task_by_pid(pid); - if (child) - get_task_struct(child); - read_unlock(&tasklist_lock); - - if (!child) { - pt_error_return(regs, ESRCH); - goto out; - } - - if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) - || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { - if (ptrace_attach(child)) { - pt_error_return(regs, EPERM); - goto out_tsk; - } - pt_succ_return(regs, 0); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) { - pt_error_return(regs, -ret); - goto out_tsk; - } - - switch(request) { - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - - if (access_process_vm(child, addr, - &tmp, sizeof(tmp), 0) == sizeof(tmp)) - pt_os_succ_return(regs, tmp, (long __user *)data); - else - pt_error_return(regs, EIO); - goto out_tsk; - } - - case PTRACE_PEEKUSR: - read_sunos_user(regs, addr, child, (long __user *) data); - goto out_tsk; - - case PTRACE_POKEUSR: - write_sunos_user(regs, addr, child); - goto out_tsk; - - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - if (access_process_vm(child, addr, - &data, sizeof(data), 1) == sizeof(data)) - pt_succ_return(regs, 0); - else - pt_error_return(regs, EIO); - goto out_tsk; - } - - case PTRACE_GETREGS: { - struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - struct pt_regs *cregs = child->thread.kregs; - int rval; - - if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) { - rval = -EFAULT; - pt_error_return(regs, -rval); - goto out_tsk; - } - __put_user(cregs->psr, (&pregs->psr)); - __put_user(cregs->pc, (&pregs->pc)); - __put_user(cregs->npc, (&pregs->npc)); - __put_user(cregs->y, (&pregs->y)); - for(rval = 1; rval < 16; rval++) - __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1])); - pt_succ_return(regs, 0); -#ifdef DEBUG_PTRACE - printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]); -#endif - goto out_tsk; - } - - case PTRACE_SETREGS: { - struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - struct pt_regs *cregs = child->thread.kregs; - unsigned long psr, pc, npc, y; - int i; - - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - __get_user(psr, (&pregs->psr)); - __get_user(pc, (&pregs->pc)); - __get_user(npc, (&pregs->npc)); - __get_user(y, (&pregs->y)); - psr &= PSR_ICC; - cregs->psr &= ~PSR_ICC; - cregs->psr |= psr; - if (!((pc | npc) & 3)) { - cregs->pc = pc; - cregs->npc =npc; - } - cregs->y = y; - for(i = 1; i < 16; i++) - __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1])); - pt_succ_return(regs, 0); - goto out_tsk; - } - - case PTRACE_GETFPREGS: { - struct fps { - unsigned long regs[32]; - unsigned long fsr; - unsigned long flags; - unsigned long extra; - unsigned long fpqd; - struct fq { - unsigned long *insnaddr; - unsigned long insn; - } fpq[16]; - }; - struct fps __user *fps = (struct fps __user *) addr; - int i; - - if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) { - i = -EFAULT; - pt_error_return(regs, -i); - goto out_tsk; - } - for(i = 0; i < 32; i++) - __put_user(child->thread.float_regs[i], (&fps->regs[i])); - __put_user(child->thread.fsr, (&fps->fsr)); - __put_user(child->thread.fpqdepth, (&fps->fpqd)); - __put_user(0, (&fps->flags)); - __put_user(0, (&fps->extra)); - for(i = 0; i < 16; i++) { - __put_user(child->thread.fpqueue[i].insn_addr, - (&fps->fpq[i].insnaddr)); - __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); - } - pt_succ_return(regs, 0); - goto out_tsk; - } - - case PTRACE_SETFPREGS: { - struct fps { - unsigned long regs[32]; - unsigned long fsr; - unsigned long flags; - unsigned long extra; - unsigned long fpqd; - struct fq { - unsigned long *insnaddr; - unsigned long insn; - } fpq[16]; - }; - struct fps __user *fps = (struct fps __user *) addr; - int i; - - if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) { - i = -EFAULT; - pt_error_return(regs, -i); - goto out_tsk; - } - copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long))); - __get_user(child->thread.fsr, (&fps->fsr)); - __get_user(child->thread.fpqdepth, (&fps->fpqd)); - for(i = 0; i < 16; i++) { - __get_user(child->thread.fpqueue[i].insn_addr, - (&fps->fpq[i].insnaddr)); - __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); - } - pt_succ_return(regs, 0); - goto out_tsk; - } - - case PTRACE_READTEXT: - case PTRACE_READDATA: { - int res = ptrace_readdata(child, addr, - (void __user *) addr2, data); - - if (res == data) { - pt_succ_return(regs, 0); - goto out_tsk; - } - /* Partial read is an IO failure */ - if (res >= 0) - res = -EIO; - pt_error_return(regs, -res); - goto out_tsk; - } - - case PTRACE_WRITETEXT: - case PTRACE_WRITEDATA: { - int res = ptrace_writedata(child, (void __user *) addr2, - addr, data); - - if (res == data) { - pt_succ_return(regs, 0); - goto out_tsk; - } - /* Partial write is an IO failure */ - if (res >= 0) - res = -EIO; - pt_error_return(regs, -res); - goto out_tsk; - } - - case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ - addr = 1; - - case PTRACE_CONT: { /* restart after signal. */ - if (!valid_signal(data)) { - pt_error_return(regs, EIO); - goto out_tsk; - } - - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - child->exit_code = data; -#ifdef DEBUG_PTRACE - printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", - child->comm, child->pid, child->exit_code, - child->thread.kregs->pc, - child->thread.kregs->npc); -#endif - wake_up_process(child); - pt_succ_return(regs, 0); - goto out_tsk; - } - -/* - * 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: { - if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ - pt_succ_return(regs, 0); - goto out_tsk; - } - wake_up_process(child); - child->exit_code = SIGKILL; - pt_succ_return(regs, 0); - goto out_tsk; - } - - case PTRACE_SUNDETACH: { /* detach a process that was attached. */ - int err = ptrace_detach(child, data); - if (err) { - pt_error_return(regs, EIO); - goto out_tsk; - } - pt_succ_return(regs, 0); - goto out_tsk; - } - - /* PTRACE_DUMPCORE unsupported... */ - - default: { - int err = ptrace_request(child, request, addr, data); - if (err) - pt_error_return(regs, -err); - else - pt_succ_return(regs, 0); - goto out_tsk; - } - } -out_tsk: - if (child) - put_task_struct(child); -out: - unlock_kernel(); -} - -asmlinkage void syscall_trace(void) -{ -#ifdef DEBUG_PTRACE - printk("%s [%d]: syscall_trace\n", current->comm, current->pid); -#endif - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; - current->thread.flags ^= MAGIC_CONSTANT; - 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 - */ -#ifdef DEBUG_PTRACE - printk("%s [%d]: syscall_trace exit= %x\n", current->comm, - current->pid, current->exit_code); -#endif - if (current->exit_code) { - send_sig (current->exit_code, current, 1); - current->exit_code = 0; - } -} |
