patch-2.3.99-pre9 linux/arch/mips64/kernel/ptrace.c
Next file: linux/arch/mips64/kernel/r4k_cache.S
Previous file: linux/arch/mips64/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 562
- Date:
Sat May 13 08:30:17 2000
- Orig file:
v2.3.99-pre8/linux/arch/mips64/kernel/ptrace.c
- Orig date:
Sat Feb 26 22:31:41 2000
diff -u --recursive --new-file v2.3.99-pre8/linux/arch/mips64/kernel/ptrace.c linux/arch/mips64/kernel/ptrace.c
@@ -8,13 +8,561 @@
* Copyright (C) Linus Torvalds
* Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle
* Copyright (C) 1996 David S. Miller
+ * Copyright (C) 2000 Ulf Carlsson
*
* At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
* binaries.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/user.h>
+#include <asm/mipsregs.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* Tracing a 32-bit process with a 64-bit strace and vice verca will not
+ work. I don't know how to fix this. */
+
+asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
+{
+ struct task_struct *child;
+ int ret;
+
+ lock_kernel();
+#if 0
+ printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
+ (int) request, (int) pid, (unsigned long) addr,
+ (unsigned long) data);
+#endif
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->flags & PF_PTRACED)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->flags |= PF_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) {
+ if (child == current)
+ goto out_tsk;
+ if ((!child->dumpable ||
+ (current->uid != child->euid) ||
+ (current->uid != child->suid) ||
+ (current->uid != child->uid) ||
+ (current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+ (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out_tsk;
+ /* the same process cannot be attached many times */
+ if (child->flags & PF_PTRACED)
+ goto out_tsk;
+ child->flags |= PF_PTRACED;
+
+ write_lock_irq(&tasklist_lock);
+ if (child->p_pptr != current) {
+ REMOVE_LINKS(child);
+ child->p_pptr = current;
+ SET_LINKS(child);
+ }
+ write_unlock_irq(&tasklist_lock);
+
+ send_sig(SIGSTOP, child, 1);
+ ret = 0;
+ goto out_tsk;
+ }
+ ret = -ESRCH;
+ if (!(child->flags & PF_PTRACED))
+ goto out_tsk;
+ if (child->state != TASK_STOPPED) {
+ if (request != PTRACE_KILL)
+ goto out_tsk;
+ }
+ if (child->p_pptr != current)
+ goto out_tsk;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned int tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp, (unsigned int *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ struct pt_regs *regs;
+ unsigned int tmp;
+
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+ ret = 0;
+
+ switch (addr) {
+ case 0 ... 31:
+ tmp = regs->regs[addr];
+ break;
+ case FPR_BASE ... FPR_BASE + 31:
+ if (child->used_math) {
+#ifndef CONFIG_SMP
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ }
+#endif
+ tmp = child->thread.fpu.hard.fp_regs[addr - 32];
+ } else {
+ tmp = -EIO;
+ }
+ break;
+ case PC:
+ tmp = regs->cp0_epc;
+ break;
+ case CAUSE:
+ tmp = regs->cp0_cause;
+ break;
+ case BADVADDR:
+ tmp = regs->cp0_badvaddr;
+ break;
+ case MMHI:
+ tmp = regs->hi;
+ break;
+ case MMLO:
+ tmp = regs->lo;
+ break;
+ case FPC_CSR:
+ tmp = child->thread.fpu.hard.control;
+ break;
+ case FPC_EIR: { /* implementation / version register */
+ unsigned int flags;
+ __save_flags(flags);
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
+ __restore_flags(flags);
+ break;
+ }
+ default:
+ tmp = 0;
+ ret = -EIO;
+ goto out_tsk;
+ }
+ ret = put_user(tmp, (unsigned *) data);
+ break;
+ }
+ /* when 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, &data, sizeof(data), 1) == sizeof(data))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: {
+ struct pt_regs *regs;
+ ret = 0;
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+
+ switch (addr) {
+ case 0 ... 31:
+ regs->regs[addr] = data;
+ break;
+ case FPR_BASE ... FPR_BASE + 31: {
+ unsigned long *fregs;
+ if (child->used_math) {
+#ifndef CONFIG_SMP
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ regs->cp0_status &= ~ST0_CU1;
+ }
+#endif
+ } else {
+ /* FP not yet used */
+ memset(&child->thread.fpu.hard, ~0,
+ sizeof(child->thread.fpu.hard));
+ child->thread.fpu.hard.control = 0;
+ }
+ fregs = child->thread.fpu.hard.fp_regs;
+ fregs[addr - FPR_BASE] = data;
+ break;
+ }
+ case PC:
+ regs->cp0_epc = data;
+ break;
+ case MMHI:
+ regs->hi = data;
+ break;
+ case MMLO:
+ regs->lo = data;
+ break;
+ case FPC_CSR:
+ child->thread.fpu.hard.control = data;
+ break;
+ default:
+ /* The rest are not allowed. */
+ ret = -EIO;
+ break;
+ }
+ goto out;
+ }
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ ret = -EIO;
+ if ((unsigned int) data > _NSIG)
+ break;
+ if (request == PTRACE_SYSCALL)
+ child->flags |= PF_TRACESYS;
+ else
+ child->flags &= ~PF_TRACESYS;
+ child->exit_code = data;
+ 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: {
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_DETACH: { /* detach a process that was attached. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+ child->exit_code = data;
+ write_lock_irq(&tasklist_lock);
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ write_unlock_irq(&tasklist_lock);
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EIO;
+ break;
+ }
+
+out_tsk:
+ free_task_struct(child);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int ret;
+
+ lock_kernel();
+#if 0
+ printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
+ (int) request, (int) pid, (unsigned long) addr,
+ (unsigned long) data);
+#endif
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->flags & PF_PTRACED)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->flags |= PF_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;
+
+ if (request == PTRACE_ATTACH) {
+ if (child == current)
+ goto out_tsk;
+ if ((!child->dumpable ||
+ (current->uid != child->euid) ||
+ (current->uid != child->suid) ||
+ (current->uid != child->uid) ||
+ (current->gid != child->egid) ||
+ (current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
+ (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out_tsk;
+ /* the same process cannot be attached many times */
+ if (child->flags & PF_PTRACED)
+ goto out_tsk;
+ child->flags |= PF_PTRACED;
+
+ write_lock_irq(&tasklist_lock);
+ if (child->p_pptr != current) {
+ REMOVE_LINKS(child);
+ child->p_pptr = current;
+ SET_LINKS(child);
+ }
+ write_unlock_irq(&tasklist_lock);
+
+ send_sig(SIGSTOP, child, 1);
+ ret = 0;
+ goto out_tsk;
+ }
+ ret = -ESRCH;
+ if (!(child->flags & PF_PTRACED))
+ goto out_tsk;
+ if (child->state != TASK_STOPPED) {
+ if (request != PTRACE_KILL)
+ goto out_tsk;
+ }
+ if (child->p_pptr != current)
+ goto out_tsk;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ struct pt_regs *regs;
+ unsigned long tmp;
+
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+ ret = 0;
+
+ switch (addr) {
+ case 0 ... 31:
+ tmp = regs->regs[addr];
+ break;
+ case FPR_BASE ... FPR_BASE + 31:
+ if (child->used_math) {
+#ifndef CONFIG_SMP
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ }
+#endif
+ tmp = child->thread.fpu.hard.fp_regs[addr - 32];
+ } else {
+ tmp = -EIO;
+ }
+ break;
+ case PC:
+ tmp = regs->cp0_epc;
+ break;
+ case CAUSE:
+ tmp = regs->cp0_cause;
+ break;
+ case BADVADDR:
+ tmp = regs->cp0_badvaddr;
+ break;
+ case MMHI:
+ tmp = regs->hi;
+ break;
+ case MMLO:
+ tmp = regs->lo;
+ break;
+ case FPC_CSR:
+ tmp = child->thread.fpu.hard.control;
+ break;
+ case FPC_EIR: { /* implementation / version register */
+ unsigned int flags;
+ __save_flags(flags);
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
+ __restore_flags(flags);
+ break;
+ }
+ default:
+ tmp = 0;
+ ret = -EIO;
+ goto out_tsk;
+ }
+ ret = put_user(tmp, (unsigned long *) data);
+ break;
+ }
+ /* when 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, &data, sizeof(data), 1) == sizeof(data))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: {
+ struct pt_regs *regs;
+ ret = 0;
+ regs = (struct pt_regs *) ((unsigned long) child +
+ KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
+
+ switch (addr) {
+ case 0 ... 31:
+ regs->regs[addr] = data;
+ break;
+ case FPR_BASE ... FPR_BASE + 31: {
+ unsigned long *fregs;
+ if (child->used_math) {
+#ifndef CONFIG_SMP
+ if (last_task_used_math == child) {
+ set_cp0_status(ST0_CU1, ST0_CU1);
+ save_fp(child);
+ set_cp0_status(ST0_CU1, 0);
+ last_task_used_math = NULL;
+ regs->cp0_status &= ~ST0_CU1;
+ }
+#endif
+ } else {
+ /* FP not yet used */
+ memset(&child->thread.fpu.hard, ~0,
+ sizeof(child->thread.fpu.hard));
+ child->thread.fpu.hard.control = 0;
+ }
+ fregs = child->thread.fpu.hard.fp_regs;
+ fregs[addr - FPR_BASE] = data;
+ break;
+ }
+ case PC:
+ regs->cp0_epc = data;
+ break;
+ case MMHI:
+ regs->hi = data;
+ break;
+ case MMLO:
+ regs->lo = data;
+ break;
+ case FPC_CSR:
+ child->thread.fpu.hard.control = data;
+ break;
+ default:
+ /* The rest are not allowed. */
+ ret = -EIO;
+ break;
+ }
+ goto out;
+ }
+ 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->flags |= PF_TRACESYS;
+ else
+ child->flags &= ~PF_TRACESYS;
+ child->exit_code = data;
+ 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: {
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_DETACH: { /* detach a process that was attached. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+ child->exit_code = data;
+ write_lock_irq(&tasklist_lock);
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ write_unlock_irq(&tasklist_lock);
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EIO;
+ break;
+ }
+
+out_tsk:
+ free_task_struct(child);
+out:
+ unlock_kernel();
+ return ret;
+}
asmlinkage void syscall_trace(void)
{
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)