Blackfin: initial tracehook support
Mike Frysinger [Tue, 26 Jan 2010 07:33:53 +0000 (07:33 +0000)]
Signed-off-by: Mike Frysinger <vapier@gentoo.org>

arch/blackfin/Kconfig
arch/blackfin/include/asm/ptrace.h
arch/blackfin/include/asm/syscall.h [new file with mode: 0644]
arch/blackfin/kernel/ptrace.c
arch/blackfin/kernel/signal.c
arch/blackfin/mach-common/entry.S

index 970df5b..c078849 100644 (file)
@@ -24,6 +24,7 @@ config RWSEM_XCHGADD_ALGORITHM
 config BLACKFIN
        def_bool y
        select HAVE_ARCH_KGDB
+       select HAVE_ARCH_TRACEHOOK
        select HAVE_FUNCTION_GRAPH_TRACER
        select HAVE_FUNCTION_TRACER
        select HAVE_FUNCTION_TRACE_MCOUNT_TEST
index c1aebdb..aaa1c6c 100644 (file)
@@ -24,6 +24,8 @@
 
 #ifndef __ASSEMBLY__
 
+struct task_struct;
+
 /* this struct defines the way the registers are stored on the
    stack during a system call. */
 
@@ -101,9 +103,30 @@ struct pt_regs {
    master interrupt enable.  */
 #define user_mode(regs) (!(((regs)->ipend & ~0x10) & (((regs)->ipend & ~0x10) - 1)))
 #define instruction_pointer(regs) ((regs)->pc)
+#define user_stack_pointer(regs)  ((regs)->usp)
 #define profile_pc(regs) instruction_pointer(regs)
 extern void show_regs(struct pt_regs *);
 
+#define arch_has_single_step() (1)
+extern void user_enable_single_step(struct task_struct *child);
+extern void user_disable_single_step(struct task_struct *child);
+/* common code demands this function */
+#define ptrace_disable(child) user_disable_single_step(child)
+
+/*
+ * Get the address of the live pt_regs for the specified task.
+ * These are saved onto the top kernel stack when the process
+ * is not running.
+ *
+ * Note: if a user thread is execve'd from kernel space, the
+ * kernel stack will not be empty on entry to the kernel, so
+ * ptracing these tasks will fail.
+ */
+#define task_pt_regs(task) \
+       (struct pt_regs *) \
+           ((unsigned long)task_stack_page(task) + \
+            (THREAD_SIZE - sizeof(struct pt_regs)))
+
 #endif  /*  __KERNEL__  */
 
 #endif                         /* __ASSEMBLY__ */
diff --git a/arch/blackfin/include/asm/syscall.h b/arch/blackfin/include/asm/syscall.h
new file mode 100644 (file)
index 0000000..4921a48
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Magic syscall break down functions
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __ASM_BLACKFIN_SYSCALL_H__
+#define __ASM_BLACKFIN_SYSCALL_H__
+
+/*
+ * Blackfin syscalls are simple:
+ *     enter:
+ *             p0: syscall number
+ *             r{0,1,2,3,4,5}: syscall args 0,1,2,3,4,5
+ *     exit:
+ *             r0: return/error value
+ */
+
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+static inline long
+syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
+{
+       return regs->p0;
+}
+
+static inline void
+syscall_rollback(struct task_struct *task, struct pt_regs *regs)
+{
+       regs->p0 = regs->orig_p0;
+}
+
+static inline long
+syscall_get_error(struct task_struct *task, struct pt_regs *regs)
+{
+       return IS_ERR_VALUE(regs->r0) ? regs->r0 : 0;
+}
+
+static inline long
+syscall_get_return_value(struct task_struct *task, struct pt_regs *regs)
+{
+       return regs->r0;
+}
+
+static inline void
+syscall_set_return_value(struct task_struct *task, struct pt_regs *regs,
+                         int error, long val)
+{
+       regs->r0 = error ? -error : val;
+}
+
+/**
+ *     syscall_get_arguments()
+ *     @task:   unused
+ *     @regs:   the register layout to extract syscall arguments from
+ *     @i:      first syscall argument to extract
+ *     @n:      number of syscall arguments to extract
+ *     @args:   array to return the syscall arguments in
+ *
+ * args[0] gets i'th argument, args[n - 1] gets the i+n-1'th argument
+ */
+static inline void
+syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
+                      unsigned int i, unsigned int n, unsigned long *args)
+{
+       /*
+        * Assume the ptrace layout doesn't change -- r5 is first in memory,
+        * then r4, ..., then r0.  So we simply reverse the ptrace register
+        * array in memory to store into the args array.
+        */
+       long *aregs = &regs->r0 - i;
+
+       BUG_ON(i > 5 || i + n > 6);
+
+       while (n--)
+               *args++ = *aregs--;
+}
+
+/* See syscall_get_arguments() comments */
+static inline void
+syscall_set_arguments(struct task_struct *task, struct pt_regs *regs,
+                      unsigned int i, unsigned int n, const unsigned long *args)
+{
+       long *aregs = &regs->r0 - i;
+
+       BUG_ON(i > 5 || i + n > 6);
+
+       while (n--)
+               *aregs-- = *args++;
+}
+
+#endif
index 92b4ca0..0618b82 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
- * these modifications are Copyright 2004-2009 Analog Devices Inc.
+ * these modifications are Copyright 2004-2010 Analog Devices Inc.
  *
  * Licensed under the GPL-2
  */
@@ -15,6 +15,7 @@
 #include <linux/user.h>
 #include <linux/regset.h>
 #include <linux/signal.h>
+#include <linux/tracehook.h>
 #include <linux/uaccess.h>
 
 #include <asm/page.h>
  * in exit.c or in signal.c.
  */
 
-/* Find the stack offset for a register, relative to thread.esp0. */
-#define PT_REG(reg)    ((long)&((struct pt_regs *)0)->reg)
-
-/*
- * Get the address of the live pt_regs for the specified task.
- * These are saved onto the top kernel stack when the process
- * is not running.
- *
- * Note: if a user thread is execve'd from kernel space, the
- * kernel stack will not be empty on entry to the kernel, so
- * ptracing these tasks will fail.
- */
-static inline struct pt_regs *task_pt_regs(struct task_struct *task)
-{
-       return (struct pt_regs *)
-           ((unsigned long)task_stack_page(task) +
-            (THREAD_SIZE - sizeof(struct pt_regs)));
-}
-
 /*
  * Get contents of register REGNO in task TASK.
  */
@@ -234,18 +216,13 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
        return &user_bfin_native_view;
 }
 
-void ptrace_enable(struct task_struct *child)
+void user_enable_single_step(struct task_struct *child)
 {
        struct pt_regs *regs = task_pt_regs(child);
        regs->syscfg |= SYSCFG_SSSTEP;
 }
 
-/*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure the single step bit is not set.
- */
-void ptrace_disable(struct task_struct *child)
+void user_disable_single_step(struct task_struct *child)
 {
        struct pt_regs *regs = task_pt_regs(child);
        regs->syscfg &= ~SYSCFG_SSSTEP;
@@ -412,27 +389,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
        return ret;
 }
 
-asmlinkage void syscall_trace(void)
+asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 {
-       if (!test_thread_flag(TIF_SYSCALL_TRACE))
-               return;
-
-       if (!(current->ptrace & PT_PTRACED))
-               return;
-
-       /* 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));
-
-       /*
-        * 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;
-       }
+       int ret = 0;
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               ret = tracehook_report_syscall_entry(regs);
+
+       return ret;
+}
+
+asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+{
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall_exit(regs, 0);
 }
index e0fd63e..e60990c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2009 Analog Devices Inc.
+ * Copyright 2004-2010 Analog Devices Inc.
  *
  * Licensed under the GPL-2 or later
  */
@@ -206,16 +206,6 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t * info,
        regs->r1 = (unsigned long)(&frame->info);
        regs->r2 = (unsigned long)(&frame->uc);
 
-       /*
-        * Clear the trace flag when entering the signal handler, but
-        * notify any tracer that was single-stepping it. The tracer
-        * may want to single-step inside the handler too.
-        */
-       if (regs->syscfg & TRACE_BITS) {
-               regs->syscfg &= ~TRACE_BITS;
-               ptrace_notify(SIGTRAP);
-       }
-
        return 0;
 
  give_sigsegv:
@@ -315,6 +305,8 @@ asmlinkage void do_signal(struct pt_regs *regs)
                         * clear the TIF_RESTORE_SIGMASK flag */
                        if (test_thread_flag(TIF_RESTORE_SIGMASK))
                                clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+                       tracehook_signal_handler(signr, &info, &ka, regs, 1);
                }
 
                return;
index 1fa414f..0df5b83 100644 (file)
@@ -736,7 +736,8 @@ ENDPROC(_system_call)
  * this symbol need not be global anyways, so ...
  */
 _sys_trace:
-       pseudo_long_call _syscall_trace, p5;
+       r0 = sp;
+       pseudo_long_call _syscall_trace_enter, p5;
 
        /* Execute the appropriate system call */
 
@@ -760,7 +761,8 @@ _sys_trace:
        SP += 24;
        [sp + PT_R0] = r0;
 
-       pseudo_long_call _syscall_trace, p5;
+       r0 = sp;
+       pseudo_long_call _syscall_trace_leave, p5;
        jump .Lresume_userspace;
 ENDPROC(_sys_trace)