sh: Convert ptrace to hw_breakpoint API.
authorPaul Mundt <lethal@linux-sh.org>
Mon, 28 Dec 2009 08:53:47 +0000 (17:53 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Mon, 28 Dec 2009 08:53:47 +0000 (17:53 +0900)
This is the initial step for converting singlestep handling via ptrace
over to hw_breakpoints.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/include/asm/ptrace.h
arch/sh/kernel/hw_breakpoint.c
arch/sh/kernel/ptrace_32.c

index 1dc12cb44a2d7acfa6b918c0f14e5487ed8785ff..201d11ef211f1311152e911b10ff13ff6fff155b 100644 (file)
@@ -124,6 +124,12 @@ struct task_struct;
 extern void user_enable_single_step(struct task_struct *);
 extern void user_disable_single_step(struct task_struct *);
 
+struct perf_event;
+struct perf_sample_data;
+
+extern void ptrace_triggered(struct perf_event *bp, int nmi,
+                     struct perf_sample_data *data, struct pt_regs *regs);
+
 #define task_pt_regs(task) \
        ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1)
 
index 022d8ed66bd6d6994a64bc785a0f882c56803369..c515a3ecf562116bd2a98cb0f9bc44d76d5a5a9a 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/io.h>
 #include <asm/hw_breakpoint.h>
 #include <asm/mmu_context.h>
+#include <asm/ptrace.h>
 
 struct ubc_context {
        unsigned long pc;
@@ -372,7 +373,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
                rcu_read_unlock();
        }
 
-       if (bp) {
+       if (bp && bp->overflow_handler != ptrace_triggered) {
                struct arch_hw_breakpoint *info = counter_arch_bp(bp);
 
                __raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0);
@@ -387,9 +388,19 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
 BUILD_TRAP_HANDLER(breakpoint)
 {
        unsigned long ex = lookup_exception_vector();
+       siginfo_t info;
+       int err;
        TRAP_HANDLER_DECL;
 
-       notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP);
+       err = notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP);
+       if (err == NOTIFY_STOP)
+               return;
+
+       /* Deliver the signal to userspace */
+       info.si_signo = SIGTRAP;
+       info.si_errno = 0;
+       info.si_code = TRAP_HWBKPT;
+       force_sig_info(SIGTRAP, &info, current);
 }
 
 /*
index bdb10446cbacd0d321739b35971c89cb4247ba8d..8e094c4c7bb495a9c8fda2cdee2ce45086fc5df2 100644 (file)
@@ -2,7 +2,7 @@
  * SuperH process tracing
  *
  * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
- * Copyright (C) 2002 - 2008  Paul Mundt
+ * Copyright (C) 2002 - 2009  Paul Mundt
  *
  * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
@@ -26,6 +26,7 @@
 #include <linux/tracehook.h>
 #include <linux/elf.h>
 #include <linux/regset.h>
+#include <linux/hw_breakpoint.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -63,9 +64,59 @@ static inline int put_stack_long(struct task_struct *task, int offset,
        return 0;
 }
 
+void ptrace_triggered(struct perf_event *bp, int nmi,
+                     struct perf_sample_data *data, struct pt_regs *regs)
+{
+       struct perf_event_attr attr;
+
+       /*
+        * Disable the breakpoint request here since ptrace has defined a
+        * one-shot behaviour for breakpoint exceptions.
+        */
+       attr = bp->attr;
+       attr.disabled = true;
+       modify_user_hw_breakpoint(bp, &attr);
+}
+
+static int set_single_step(struct task_struct *tsk, unsigned long addr)
+{
+       struct thread_struct *thread = &tsk->thread;
+       struct perf_event *bp;
+       struct perf_event_attr attr;
+
+       bp = thread->ptrace_bps[0];
+       if (!bp) {
+               hw_breakpoint_init(&attr);
+
+               attr.bp_addr = addr;
+               attr.bp_len = HW_BREAKPOINT_LEN_2;
+               attr.bp_type = HW_BREAKPOINT_R;
+
+               bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
+               if (IS_ERR(bp))
+                       return PTR_ERR(bp);
+
+               thread->ptrace_bps[0] = bp;
+       } else {
+               int err;
+
+               attr = bp->attr;
+               attr.bp_addr = addr;
+               err = modify_user_hw_breakpoint(bp, &attr);
+               if (unlikely(err))
+                       return err;
+       }
+
+       return 0;
+}
+
 void user_enable_single_step(struct task_struct *child)
 {
+       unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc));
+
        set_tsk_thread_flag(child, TIF_SINGLESTEP);
+
+       set_single_step(child, pc);
 }
 
 void user_disable_single_step(struct task_struct *child)