Commit 34d0b5af authored by Paul Mundt's avatar Paul Mundt

sh: Convert ptrace to hw_breakpoint API.

This is the initial step for converting singlestep handling via ptrace
over to hw_breakpoints.
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent 22648735
...@@ -124,6 +124,12 @@ struct task_struct; ...@@ -124,6 +124,12 @@ struct task_struct;
extern void user_enable_single_step(struct task_struct *); extern void user_enable_single_step(struct task_struct *);
extern void user_disable_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) \ #define task_pt_regs(task) \
((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1) ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1)
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <asm/hw_breakpoint.h> #include <asm/hw_breakpoint.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/ptrace.h>
struct ubc_context { struct ubc_context {
unsigned long pc; unsigned long pc;
...@@ -372,7 +373,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) ...@@ -372,7 +373,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
rcu_read_unlock(); rcu_read_unlock();
} }
if (bp) { if (bp && bp->overflow_handler != ptrace_triggered) {
struct arch_hw_breakpoint *info = counter_arch_bp(bp); struct arch_hw_breakpoint *info = counter_arch_bp(bp);
__raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0); __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) ...@@ -387,9 +388,19 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
BUILD_TRAP_HANDLER(breakpoint) BUILD_TRAP_HANDLER(breakpoint)
{ {
unsigned long ex = lookup_exception_vector(); unsigned long ex = lookup_exception_vector();
siginfo_t info;
int err;
TRAP_HANDLER_DECL; 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);
} }
/* /*
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* SuperH process tracing * SuperH process tracing
* *
* Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka * 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> * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
* *
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/tracehook.h> #include <linux/tracehook.h>
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/regset.h> #include <linux/regset.h>
#include <linux/hw_breakpoint.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -63,9 +64,59 @@ static inline int put_stack_long(struct task_struct *task, int offset, ...@@ -63,9 +64,59 @@ static inline int put_stack_long(struct task_struct *task, int offset,
return 0; 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) 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_tsk_thread_flag(child, TIF_SINGLESTEP);
set_single_step(child, pc);
} }
void user_disable_single_step(struct task_struct *child) void user_disable_single_step(struct task_struct *child)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment