diff options
-rw-r--r-- | arch/sh/include/asm/ptrace.h | 6 | ||||
-rw-r--r-- | arch/sh/kernel/hw_breakpoint.c | 15 | ||||
-rw-r--r-- | arch/sh/kernel/ptrace_32.c | 53 |
3 files changed, 71 insertions, 3 deletions
diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h index 1dc12cb44a2d..201d11ef211f 100644 --- a/arch/sh/include/asm/ptrace.h +++ b/arch/sh/include/asm/ptrace.h | |||
@@ -124,6 +124,12 @@ struct task_struct; | |||
124 | extern void user_enable_single_step(struct task_struct *); | 124 | extern void user_enable_single_step(struct task_struct *); |
125 | extern void user_disable_single_step(struct task_struct *); | 125 | extern void user_disable_single_step(struct task_struct *); |
126 | 126 | ||
127 | struct perf_event; | ||
128 | struct perf_sample_data; | ||
129 | |||
130 | extern void ptrace_triggered(struct perf_event *bp, int nmi, | ||
131 | struct perf_sample_data *data, struct pt_regs *regs); | ||
132 | |||
127 | #define task_pt_regs(task) \ | 133 | #define task_pt_regs(task) \ |
128 | ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1) | 134 | ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1) |
129 | 135 | ||
diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index 022d8ed66bd6..c515a3ecf562 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/io.h> | 20 | #include <linux/io.h> |
21 | #include <asm/hw_breakpoint.h> | 21 | #include <asm/hw_breakpoint.h> |
22 | #include <asm/mmu_context.h> | 22 | #include <asm/mmu_context.h> |
23 | #include <asm/ptrace.h> | ||
23 | 24 | ||
24 | struct ubc_context { | 25 | struct ubc_context { |
25 | unsigned long pc; | 26 | unsigned long pc; |
@@ -372,7 +373,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) | |||
372 | rcu_read_unlock(); | 373 | rcu_read_unlock(); |
373 | } | 374 | } |
374 | 375 | ||
375 | if (bp) { | 376 | if (bp && bp->overflow_handler != ptrace_triggered) { |
376 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 377 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
377 | 378 | ||
378 | __raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0); | 379 | __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 | BUILD_TRAP_HANDLER(breakpoint) | 388 | BUILD_TRAP_HANDLER(breakpoint) |
388 | { | 389 | { |
389 | unsigned long ex = lookup_exception_vector(); | 390 | unsigned long ex = lookup_exception_vector(); |
391 | siginfo_t info; | ||
392 | int err; | ||
390 | TRAP_HANDLER_DECL; | 393 | TRAP_HANDLER_DECL; |
391 | 394 | ||
392 | notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); | 395 | err = notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); |
396 | if (err == NOTIFY_STOP) | ||
397 | return; | ||
398 | |||
399 | /* Deliver the signal to userspace */ | ||
400 | info.si_signo = SIGTRAP; | ||
401 | info.si_errno = 0; | ||
402 | info.si_code = TRAP_HWBKPT; | ||
403 | force_sig_info(SIGTRAP, &info, current); | ||
393 | } | 404 | } |
394 | 405 | ||
395 | /* | 406 | /* |
diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index bdb10446cbac..8e094c4c7bb4 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * SuperH process tracing | 2 | * SuperH process tracing |
3 | * | 3 | * |
4 | * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka | 4 | * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka |
5 | * Copyright (C) 2002 - 2008 Paul Mundt | 5 | * Copyright (C) 2002 - 2009 Paul Mundt |
6 | * | 6 | * |
7 | * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp> | 7 | * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp> |
8 | * | 8 | * |
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/tracehook.h> | 26 | #include <linux/tracehook.h> |
27 | #include <linux/elf.h> | 27 | #include <linux/elf.h> |
28 | #include <linux/regset.h> | 28 | #include <linux/regset.h> |
29 | #include <linux/hw_breakpoint.h> | ||
29 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
30 | #include <asm/pgtable.h> | 31 | #include <asm/pgtable.h> |
31 | #include <asm/system.h> | 32 | #include <asm/system.h> |
@@ -63,9 +64,59 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
63 | return 0; | 64 | return 0; |
64 | } | 65 | } |
65 | 66 | ||
67 | void ptrace_triggered(struct perf_event *bp, int nmi, | ||
68 | struct perf_sample_data *data, struct pt_regs *regs) | ||
69 | { | ||
70 | struct perf_event_attr attr; | ||
71 | |||
72 | /* | ||
73 | * Disable the breakpoint request here since ptrace has defined a | ||
74 | * one-shot behaviour for breakpoint exceptions. | ||
75 | */ | ||
76 | attr = bp->attr; | ||
77 | attr.disabled = true; | ||
78 | modify_user_hw_breakpoint(bp, &attr); | ||
79 | } | ||
80 | |||
81 | static int set_single_step(struct task_struct *tsk, unsigned long addr) | ||
82 | { | ||
83 | struct thread_struct *thread = &tsk->thread; | ||
84 | struct perf_event *bp; | ||
85 | struct perf_event_attr attr; | ||
86 | |||
87 | bp = thread->ptrace_bps[0]; | ||
88 | if (!bp) { | ||
89 | hw_breakpoint_init(&attr); | ||
90 | |||
91 | attr.bp_addr = addr; | ||
92 | attr.bp_len = HW_BREAKPOINT_LEN_2; | ||
93 | attr.bp_type = HW_BREAKPOINT_R; | ||
94 | |||
95 | bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); | ||
96 | if (IS_ERR(bp)) | ||
97 | return PTR_ERR(bp); | ||
98 | |||
99 | thread->ptrace_bps[0] = bp; | ||
100 | } else { | ||
101 | int err; | ||
102 | |||
103 | attr = bp->attr; | ||
104 | attr.bp_addr = addr; | ||
105 | err = modify_user_hw_breakpoint(bp, &attr); | ||
106 | if (unlikely(err)) | ||
107 | return err; | ||
108 | } | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
66 | void user_enable_single_step(struct task_struct *child) | 113 | void user_enable_single_step(struct task_struct *child) |
67 | { | 114 | { |
115 | unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc)); | ||
116 | |||
68 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | 117 | set_tsk_thread_flag(child, TIF_SINGLESTEP); |
118 | |||
119 | set_single_step(child, pc); | ||
69 | } | 120 | } |
70 | 121 | ||
71 | void user_disable_single_step(struct task_struct *child) | 122 | void user_disable_single_step(struct task_struct *child) |