diff options
Diffstat (limited to 'arch/sh/kernel/ptrace_32.c')
-rw-r--r-- | arch/sh/kernel/ptrace_32.c | 83 |
1 files changed, 57 insertions, 26 deletions
diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 9be35f348093..7759a9a93211 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 | * |
@@ -17,7 +17,6 @@ | |||
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/ptrace.h> | 18 | #include <linux/ptrace.h> |
19 | #include <linux/user.h> | 19 | #include <linux/user.h> |
20 | #include <linux/slab.h> | ||
21 | #include <linux/security.h> | 20 | #include <linux/security.h> |
22 | #include <linux/signal.h> | 21 | #include <linux/signal.h> |
23 | #include <linux/io.h> | 22 | #include <linux/io.h> |
@@ -26,6 +25,7 @@ | |||
26 | #include <linux/tracehook.h> | 25 | #include <linux/tracehook.h> |
27 | #include <linux/elf.h> | 26 | #include <linux/elf.h> |
28 | #include <linux/regset.h> | 27 | #include <linux/regset.h> |
28 | #include <linux/hw_breakpoint.h> | ||
29 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
30 | #include <asm/pgtable.h> | 30 | #include <asm/pgtable.h> |
31 | #include <asm/system.h> | 31 | #include <asm/system.h> |
@@ -63,33 +63,64 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
63 | return 0; | 63 | return 0; |
64 | } | 64 | } |
65 | 65 | ||
66 | void user_enable_single_step(struct task_struct *child) | 66 | void ptrace_triggered(struct perf_event *bp, int nmi, |
67 | struct perf_sample_data *data, struct pt_regs *regs) | ||
67 | { | 68 | { |
68 | /* Next scheduling will set up UBC */ | 69 | struct perf_event_attr attr; |
69 | if (child->thread.ubc_pc == 0) | 70 | |
70 | ubc_usercnt += 1; | 71 | /* |
72 | * Disable the breakpoint request here since ptrace has defined a | ||
73 | * one-shot behaviour for breakpoint exceptions. | ||
74 | */ | ||
75 | attr = bp->attr; | ||
76 | attr.disabled = true; | ||
77 | modify_user_hw_breakpoint(bp, &attr); | ||
78 | } | ||
79 | |||
80 | static int set_single_step(struct task_struct *tsk, unsigned long addr) | ||
81 | { | ||
82 | struct thread_struct *thread = &tsk->thread; | ||
83 | struct perf_event *bp; | ||
84 | struct perf_event_attr attr; | ||
85 | |||
86 | bp = thread->ptrace_bps[0]; | ||
87 | if (!bp) { | ||
88 | hw_breakpoint_init(&attr); | ||
89 | |||
90 | attr.bp_addr = addr; | ||
91 | attr.bp_len = HW_BREAKPOINT_LEN_2; | ||
92 | attr.bp_type = HW_BREAKPOINT_R; | ||
93 | |||
94 | bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); | ||
95 | if (IS_ERR(bp)) | ||
96 | return PTR_ERR(bp); | ||
97 | |||
98 | thread->ptrace_bps[0] = bp; | ||
99 | } else { | ||
100 | int err; | ||
101 | |||
102 | attr = bp->attr; | ||
103 | attr.bp_addr = addr; | ||
104 | err = modify_user_hw_breakpoint(bp, &attr); | ||
105 | if (unlikely(err)) | ||
106 | return err; | ||
107 | } | ||
108 | |||
109 | return 0; | ||
110 | } | ||
71 | 111 | ||
72 | child->thread.ubc_pc = get_stack_long(child, | 112 | void user_enable_single_step(struct task_struct *child) |
73 | offsetof(struct pt_regs, pc)); | 113 | { |
114 | unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc)); | ||
74 | 115 | ||
75 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | 116 | set_tsk_thread_flag(child, TIF_SINGLESTEP); |
117 | |||
118 | set_single_step(child, pc); | ||
76 | } | 119 | } |
77 | 120 | ||
78 | void user_disable_single_step(struct task_struct *child) | 121 | void user_disable_single_step(struct task_struct *child) |
79 | { | 122 | { |
80 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | 123 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); |
81 | |||
82 | /* | ||
83 | * Ensure the UBC is not programmed at the next context switch. | ||
84 | * | ||
85 | * Normally this is not needed but there are sequences such as | ||
86 | * singlestep, signal delivery, and continue that leave the | ||
87 | * ubc_pc non-zero leading to spurious SIGTRAPs. | ||
88 | */ | ||
89 | if (child->thread.ubc_pc != 0) { | ||
90 | ubc_usercnt -= 1; | ||
91 | child->thread.ubc_pc = 0; | ||
92 | } | ||
93 | } | 124 | } |
94 | 125 | ||
95 | /* | 126 | /* |
@@ -163,10 +194,10 @@ int fpregs_get(struct task_struct *target, | |||
163 | 194 | ||
164 | if ((boot_cpu_data.flags & CPU_HAS_FPU)) | 195 | if ((boot_cpu_data.flags & CPU_HAS_FPU)) |
165 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 196 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
166 | &target->thread.fpu.hard, 0, -1); | 197 | &target->thread.xstate->hardfpu, 0, -1); |
167 | 198 | ||
168 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 199 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
169 | &target->thread.fpu.soft, 0, -1); | 200 | &target->thread.xstate->softfpu, 0, -1); |
170 | } | 201 | } |
171 | 202 | ||
172 | static int fpregs_set(struct task_struct *target, | 203 | static int fpregs_set(struct task_struct *target, |
@@ -184,10 +215,10 @@ static int fpregs_set(struct task_struct *target, | |||
184 | 215 | ||
185 | if ((boot_cpu_data.flags & CPU_HAS_FPU)) | 216 | if ((boot_cpu_data.flags & CPU_HAS_FPU)) |
186 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 217 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
187 | &target->thread.fpu.hard, 0, -1); | 218 | &target->thread.xstate->hardfpu, 0, -1); |
188 | 219 | ||
189 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 220 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
190 | &target->thread.fpu.soft, 0, -1); | 221 | &target->thread.xstate->softfpu, 0, -1); |
191 | } | 222 | } |
192 | 223 | ||
193 | static int fpregs_active(struct task_struct *target, | 224 | static int fpregs_active(struct task_struct *target, |
@@ -333,7 +364,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
333 | else | 364 | else |
334 | tmp = 0; | 365 | tmp = 0; |
335 | } else | 366 | } else |
336 | tmp = ((long *)&child->thread.fpu) | 367 | tmp = ((long *)child->thread.xstate) |
337 | [(addr - (long)&dummy->fpu) >> 2]; | 368 | [(addr - (long)&dummy->fpu) >> 2]; |
338 | } else if (addr == (long) &dummy->u_fpvalid) | 369 | } else if (addr == (long) &dummy->u_fpvalid) |
339 | tmp = !!tsk_used_math(child); | 370 | tmp = !!tsk_used_math(child); |
@@ -362,7 +393,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
362 | else if (addr >= (long) &dummy->fpu && | 393 | else if (addr >= (long) &dummy->fpu && |
363 | addr < (long) &dummy->u_fpvalid) { | 394 | addr < (long) &dummy->u_fpvalid) { |
364 | set_stopped_child_used_math(child); | 395 | set_stopped_child_used_math(child); |
365 | ((long *)&child->thread.fpu) | 396 | ((long *)child->thread.xstate) |
366 | [(addr - (long)&dummy->fpu) >> 2] = data; | 397 | [(addr - (long)&dummy->fpu) >> 2] = data; |
367 | ret = 0; | 398 | ret = 0; |
368 | } else if (addr == (long) &dummy->u_fpvalid) { | 399 | } else if (addr == (long) &dummy->u_fpvalid) { |