From: Haavard Skinnemoen <[EMAIL PROTECTED]>

Rip out most of the ptrace code for AVR32 and replace it with the
much nicer utrace stuff. It builds in all possible combinations of
CONFIG_UTRACE and CONFIG_PTRACE, and it seems to work as far as I've
tested it with strace and some simple debugging with gdb.

Signed-off-by: Haavard Skinnemoen <[EMAIL PROTECTED]>
---
 arch/avr32/kernel/entry-avr32b.S |   10 +-
 arch/avr32/kernel/process.c      |    2 -
 arch/avr32/kernel/ptrace.c       |  324 ++++++++++---------------------------
 include/asm-avr32/tracehook.h    |   69 ++++++++
 4 files changed, 165 insertions(+), 240 deletions(-)

diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
index eeb6679..81eaf20 100644
--- a/arch/avr32/kernel/entry-avr32b.S
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -229,15 +229,21 @@ ret_from_fork:
        rjmp    syscall_exit_cont
 
 syscall_trace_enter:
-       pushm   r8-r12
+       mov     r12, sp         /* regs         */
+       mov     r11, 0          /* is_exit      */
        rcall   syscall_trace
-       popm    r8-r12
+
+       /* syscall_trace may update r8, so reload r8-r12 from regs. */
+       sub     lr, sp, -REG_R12
+       ldm     lr, r8-r12
        rjmp    syscall_trace_cont
 
 syscall_exit_work:
        bld     r1, TIF_SYSCALL_TRACE
        brcc    1f
        unmask_interrupts
+       mov     r12, sp
+       mov     r11, 1
        rcall   syscall_trace
        mask_interrupts
        ld.w    r1, r0[TI_flags]
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 0b43259..f7cf378 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -229,8 +229,6 @@ asmlinkage int sys_execve(char __user *ufilename, char 
__user *__user *uargv,
                goto out;
 
        error = do_execve(filename, uargv, uenvp, regs);
-       if (error == 0)
-               current->ptrace &= ~PT_DTRACE;
        putname(filename);
 
 out:
diff --git a/arch/avr32/kernel/ptrace.c b/arch/avr32/kernel/ptrace.c
index 6f4388f..396b2f9 100644
--- a/arch/avr32/kernel/ptrace.c
+++ b/arch/avr32/kernel/ptrace.c
@@ -5,23 +5,23 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#undef DEBUG
+
+#include <linux/compile.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
 #include <linux/ptrace.h>
-#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tracehook.h>
 #include <linux/user.h>
-#include <linux/security.h>
-#include <linux/unistd.h>
-#include <linux/notifier.h>
 
-#include <asm/traps.h>
-#include <asm/uaccess.h>
-#include <asm/ocd.h>
-#include <asm/mmu_context.h>
 #include <asm/kdebug.h>
+#include <asm/mmu_context.h>
+#include <asm/ocd.h>
+
+#ifdef CONFIG_UTRACE
 
 static struct pt_regs *get_user_regs(struct task_struct *tsk)
 {
@@ -29,261 +29,113 @@ static struct pt_regs *get_user_regs(struct task_struct 
*tsk)
                                  THREAD_SIZE - sizeof(struct pt_regs));
 }
 
-static void ptrace_single_step(struct task_struct *tsk)
-{
-       pr_debug("ptrace_single_step: pid=%u, SR=0x%08lx\n",
-                tsk->pid, tsk->thread.cpu_context.sr);
-       if (!(tsk->thread.cpu_context.sr & SR_D)) {
-               /*
-                * Set a breakpoint at the current pc to force the
-                * process into debug mode.  The syscall/exception
-                * exit code will set a breakpoint at the return
-                * address when this flag is set.
-                */
-               pr_debug("ptrace_single_step: Setting TIF_BREAKPOINT\n");
-               set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
-       }
-
-       /* The monitor code will do the actual step for us */
-       set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
-}
-
-/*
- * Called by kernel/ptrace.c when detaching
- *
- * Make sure any single step bits, etc. are not set
- */
-void ptrace_disable(struct task_struct *child)
-{
-       clear_tsk_thread_flag(child, TIF_SINGLE_STEP);
-}
-
-/*
- * Handle hitting a breakpoint
- */
-static void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
-{
-       siginfo_t info;
-
-       info.si_signo = SIGTRAP;
-       info.si_errno = 0;
-       info.si_code  = TRAP_BRKPT;
-       info.si_addr  = (void __user *)instruction_pointer(regs);
-
-       pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
-                tsk->pid, info.si_addr);
-       force_sig_info(SIGTRAP, &info, tsk);
-}
-
-/*
- * Read the word at offset "offset" into the task's "struct user". We
- * actually access the pt_regs struct stored on the kernel stack.
- */
-static int ptrace_read_user(struct task_struct *tsk, unsigned long offset,
-                           unsigned long __user *data)
+static int genregs_get(struct task_struct *target,
+                      const struct utrace_regset *regset,
+                      unsigned int pos, unsigned int count,
+                      void *kbuf, void __user *ubuf)
 {
-       unsigned long *regs;
-       unsigned long value;
-
-       pr_debug("ptrace_read_user(%p, %#lx, %p)\n",
-                tsk, offset, data);
-
-       if (offset & 3 || offset >= sizeof(struct user)) {
-               printk("ptrace_read_user: invalid offset 0x%08lx\n", offset);
-               return -EIO;
-       }
-
-       regs = (unsigned long *)get_user_regs(tsk);
-
-       value = 0;
-       if (offset < sizeof(struct pt_regs))
-               value = regs[offset / sizeof(regs[0])];
+       struct pt_regs *regs = get_user_regs(target);
 
-       return put_user(value, data);
+       return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                    regs, 0, -1);
 }
 
-/*
- * Write the word "value" to offset "offset" into the task's "struct
- * user". We actually access the pt_regs struct stored on the kernel
- * stack.
- */
-static int ptrace_write_user(struct task_struct *tsk, unsigned long offset,
-                            unsigned long value)
+static int genregs_set(struct task_struct *target,
+                      const struct utrace_regset *regset,
+                      unsigned int pos, unsigned int count,
+                      const void *kbuf, const void __user *ubuf)
 {
-       unsigned long *regs;
-
-       if (offset & 3 || offset >= sizeof(struct user)) {
-               printk("ptrace_write_user: invalid offset 0x%08lx\n", offset);
-               return -EIO;
-       }
-
-       if (offset >= sizeof(struct pt_regs))
-               return 0;
-
-       regs = (unsigned long *)get_user_regs(tsk);
-       regs[offset / sizeof(regs[0])] = value;
+       struct pt_regs *regs = get_user_regs(target);
 
-       return 0;
+       return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                   regs, 0, -1);
 }
 
-static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
-{
-       struct pt_regs *regs = get_user_regs(tsk);
-
-       return copy_to_user(uregs, regs, sizeof(*regs)) ? -EFAULT : 0;
-}
-
-static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs)
-{
-       struct pt_regs newregs;
-       int ret;
-
-       ret = -EFAULT;
-       if (copy_from_user(&newregs, uregs, sizeof(newregs)) == 0) {
-               struct pt_regs *regs = get_user_regs(tsk);
-
-               ret = -EINVAL;
-               if (valid_user_regs(&newregs)) {
-                       *regs = newregs;
-                       ret = 0;
-               }
-       }
-
-       return ret;
-}
-
-long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+static const struct utrace_regset native_regsets[] = {
+       {
+               .n      = ELF_NGREG,
+               .size   = sizeof(long),
+               .align  = sizeof(long),
+               .get    = genregs_get,
+               .set    = genregs_set,
+       },
+       /*
+        * Other register sets that probably would make sense:
+        *   - Coprocessor registers (8 coprocs with 16 registers each)
+        *   - TLS stuff
+        */
+};
+
+const struct utrace_regset_view utrace_avr32_native_view = {
+       .name           = UTS_MACHINE,
+       .e_machine      = ELF_ARCH,
+       .regsets        = native_regsets,
+       .n              = ARRAY_SIZE(native_regsets),
+};
+EXPORT_SYMBOL_GPL(utrace_avr32_native_view);
+
+#ifdef CONFIG_PTRACE
+
+static const struct ptrace_layout_segment avr32_uarea[] = {
+       { 0, ELF_NGREG * sizeof(long), 0, 0 },
+       { 0, 0, -1, 0 },
+};
+
+int arch_ptrace(long *request, struct task_struct *child,
+               struct utrace_attached_engine *engine,
+               unsigned long addr, unsigned long data, long *val)
 {
-       unsigned long tmp;
-       int ret;
-
        pr_debug("arch_ptrace(%ld, %d, %#lx, %#lx)\n",
-                request, child->pid, addr, data);
+                *request, child->pid, addr, data);
 
        pr_debug("ptrace: Enabling monitor mode...\n");
        __mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE);
 
-       switch (request) {
-       /* Read the word at location addr in the child process */
-       case PTRACE_PEEKTEXT:
-       case PTRACE_PEEKDATA:
-               ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
-               if (ret == sizeof(tmp))
-                       ret = put_user(tmp, (unsigned long __user *)data);
-               else
-                       ret = -EIO;
-               break;
-
+       switch (*request) {
        case PTRACE_PEEKUSR:
-               ret = ptrace_read_user(child, addr,
-                                      (unsigned long __user *)data);
-               break;
-
-       /* Write the word in data at location addr */
-       case PTRACE_POKETEXT:
-       case PTRACE_POKEDATA:
-               ret = access_process_vm(child, addr, &data, sizeof(data), 1);
-               if (ret == sizeof(data))
-                       ret = 0;
-               else
-                       ret = -EIO;
-               break;
+               return ptrace_peekusr(child, engine, avr32_uarea, addr, data);
 
        case PTRACE_POKEUSR:
-               ret = ptrace_write_user(child, addr, data);
-               break;
-
-       /* continue and stop at next (return from) syscall */
-       case PTRACE_SYSCALL:
-       /* restart after signal */
-       case PTRACE_CONT:
-               ret = -EIO;
-               if (!valid_signal(data))
-                       break;
-               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;
-               /* XXX: Are we sure no breakpoints are active here? */
-               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->exit_state == EXIT_ZOMBIE)
-                       break;
-               child->exit_code = SIGKILL;
-               wake_up_process(child);
-               break;
-
-       /*
-        * execute single instruction.
-        */
-       case PTRACE_SINGLESTEP:
-               ret = -EIO;
-               if (!valid_signal(data))
-                       break;
-               clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-               ptrace_single_step(child);
-               child->exit_code = data;
-               wake_up_process(child);
-               ret = 0;
-               break;
-
-       /* Detach a process that was attached */
-       case PTRACE_DETACH:
-               ret = ptrace_detach(child, data);
+               return ptrace_pokeusr(child, engine, avr32_uarea, addr, data);
                break;
 
        case PTRACE_GETREGS:
-               ret = ptrace_getregs(child, (void __user *)data);
+               return ptrace_whole_regset(child, engine, data, 0, 0);
                break;
 
        case PTRACE_SETREGS:
-               ret = ptrace_setregs(child, (const void __user *)data);
-               break;
-
-       default:
-               ret = ptrace_request(child, request, addr, data);
+               return ptrace_whole_regset(child, engine, data, 0, 1);
                break;
        }
 
-       pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, 
__mfdr(DBGREG_DC));
-       return ret;
+       return -ENOSYS;
 }
+#endif /* CONFIG_PTRACE */
+#endif /* CONFIG_UTRACE */
 
-asmlinkage void syscall_trace(void)
+asmlinkage void syscall_trace(struct pt_regs *regs, int is_exit)
 {
-       pr_debug("syscall_trace called\n");
        if (!test_thread_flag(TIF_SYSCALL_TRACE))
                return;
-       if (!(current->ptrace & PT_PTRACED))
-               return;
 
-       pr_debug("syscall_trace: notifying parent\n");
-       /* The 0x80 provides a way for the tracing parent to
-        * distinguish between a syscall stop and SIGTRAP delivery */
-       ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-                                ? 0x80 : 0));
+       tracehook_report_syscall(regs, is_exit);
+}
 
-       /*
-        * 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) {
-               pr_debug("syscall_trace: sending signal %d to PID %u\n",
-                        current->exit_code, current->pid);
-               send_sig(current->exit_code, current, 1);
-               current->exit_code = 0;
-       }
+/*
+ * Handle hitting a breakpoint
+ */
+static void do_breakpoint(struct task_struct *tsk, struct pt_regs *regs)
+{
+       siginfo_t info;
+
+       info.si_signo = SIGTRAP;
+       info.si_errno = 0;
+       info.si_code  = TRAP_BRKPT;
+       info.si_addr  = (void __user *)instruction_pointer(regs);
+
+       pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
+                tsk->pid, info.si_addr);
+       force_sig_info(SIGTRAP, &info, tsk);
 }
 
 asmlinkage void do_debug_priv(struct pt_regs *regs)
@@ -362,10 +214,10 @@ asmlinkage void do_debug(struct pt_regs *regs)
                        __mtdr(DBGREG_DC, dc);
 
                        clear_thread_flag(TIF_SINGLE_STEP);
-                       ptrace_break(current, regs);
+                       do_breakpoint(current, regs);
                }
        } else {
                /* regular breakpoint */
-               ptrace_break(current, regs);
+               do_breakpoint(current, regs);
        }
 }
diff --git a/include/asm-avr32/tracehook.h b/include/asm-avr32/tracehook.h
new file mode 100644
index 0000000..30027ef
--- /dev/null
+++ b/include/asm-avr32/tracehook.h
@@ -0,0 +1,69 @@
+/*
+ * Tracing hooks for AVR32
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _ASM_AVR32_TRACEHOOK_H
+#define _ASM_AVR32_TRACEHOOK_H
+
+#include <linux/sched.h>
+
+#define ARCH_HAS_SINGLE_STEP   1
+
+static inline void tracehook_enable_single_step(struct task_struct *tsk)
+{
+       /*
+        * If the process is stopped in debug mode, simply set
+        * TIF_SINGLE_STEP to tell the monitor code to set the single
+        * step bit in DC before returning.
+        *
+        * Otherwise, we need to set a breakpoint at the return
+        * address before returning to userspace. TIF_BREAKPOINT will
+        * tell the syscall/exception exit code to do this.
+        */
+       if (!(tsk->thread.cpu_context.sr & SR_D))
+               set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+
+       set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline void tracehook_disable_single_step(struct task_struct *tsk)
+{
+       clear_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+       clear_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline int tracehook_single_step_enabled(struct task_struct *tsk)
+{
+       return test_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+static inline void tracehook_enable_syscall_trace(struct task_struct *tsk)
+{
+       set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_disable_syscall_trace(struct task_struct *tsk)
+{
+       clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE);
+}
+
+static inline void tracehook_abort_syscall(struct pt_regs *regs)
+{
+       /* Invalid system call number => return -ENOSYS */
+       regs->r8 = -1;
+}
+
+extern const struct utrace_regset_view utrace_avr32_native_view;
+
+static inline const struct utrace_regset_view *
+utrace_native_view(struct task_struct *tsk)
+{
+       return &utrace_avr32_native_view;
+}
+
+#endif /* _ASM_AVR32_TRACEHOOK_H */
-- 
1.4.4.4

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to