diff options
author | Stuart Menefy <stuart.menefy@st.com> | 2007-02-22 23:22:17 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2007-03-05 00:13:25 -0500 |
commit | 9432f96803139adaff0cd9f4fa38b7fb99cda366 (patch) | |
tree | e2006ca8390dc4c8b14d08c658ccd5bfd80d401e /arch/sh/kernel/ptrace.c | |
parent | 20b0f65d35ae45d43f363ace3744a775a752d265 (diff) |
sh: Clear UBC when not in use.
This takes care of tearing down the UBC so it's not inadvertently
left configured at the next context switch time. Failure to do
this results in spurious SIGTRAPs in certain debug sequences.
Signed-off-by: Stuart Menefy <stuart.menefy@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel/ptrace.c')
-rw-r--r-- | arch/sh/kernel/ptrace.c | 45 |
1 files changed, 33 insertions, 12 deletions
diff --git a/arch/sh/kernel/ptrace.c b/arch/sh/kernel/ptrace.c index 04ca13a041c1..855f7246cfff 100644 --- a/arch/sh/kernel/ptrace.c +++ b/arch/sh/kernel/ptrace.c | |||
@@ -8,7 +8,6 @@ | |||
8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka | 8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka |
9 | * | 9 | * |
10 | */ | 10 | */ |
11 | |||
12 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
13 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
14 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
@@ -20,8 +19,7 @@ | |||
20 | #include <linux/slab.h> | 19 | #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 | 22 | #include <linux/io.h> | |
24 | #include <asm/io.h> | ||
25 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
26 | #include <asm/pgtable.h> | 24 | #include <asm/pgtable.h> |
27 | #include <asm/system.h> | 25 | #include <asm/system.h> |
@@ -59,6 +57,23 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
59 | return 0; | 57 | return 0; |
60 | } | 58 | } |
61 | 59 | ||
60 | static void ptrace_disable_singlestep(struct task_struct *child) | ||
61 | { | ||
62 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
63 | |||
64 | /* | ||
65 | * Ensure the UBC is not programmed at the next context switch. | ||
66 | * | ||
67 | * Normally this is not needed but there are sequences such as | ||
68 | * singlestep, signal delivery, and continue that leave the | ||
69 | * ubc_pc non-zero leading to spurious SIGTRAPs. | ||
70 | */ | ||
71 | if (child->thread.ubc_pc != 0) { | ||
72 | ubc_usercnt -= 1; | ||
73 | child->thread.ubc_pc = 0; | ||
74 | } | ||
75 | } | ||
76 | |||
62 | /* | 77 | /* |
63 | * Called by kernel/ptrace.c when detaching.. | 78 | * Called by kernel/ptrace.c when detaching.. |
64 | * | 79 | * |
@@ -66,7 +81,7 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
66 | */ | 81 | */ |
67 | void ptrace_disable(struct task_struct *child) | 82 | void ptrace_disable(struct task_struct *child) |
68 | { | 83 | { |
69 | /* nothing to do.. */ | 84 | ptrace_disable_singlestep(child); |
70 | } | 85 | } |
71 | 86 | ||
72 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 87 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
@@ -76,7 +91,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
76 | 91 | ||
77 | switch (request) { | 92 | switch (request) { |
78 | /* when I and D space are separate, these will need to be fixed. */ | 93 | /* when I and D space are separate, these will need to be fixed. */ |
79 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 94 | case PTRACE_PEEKTEXT: /* read word at location addr. */ |
80 | case PTRACE_PEEKDATA: { | 95 | case PTRACE_PEEKDATA: { |
81 | unsigned long tmp; | 96 | unsigned long tmp; |
82 | int copied; | 97 | int copied; |
@@ -94,7 +109,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
94 | unsigned long tmp; | 109 | unsigned long tmp; |
95 | 110 | ||
96 | ret = -EIO; | 111 | ret = -EIO; |
97 | if ((addr & 3) || addr < 0 || | 112 | if ((addr & 3) || addr < 0 || |
98 | addr > sizeof(struct user) - 3) | 113 | addr > sizeof(struct user) - 3) |
99 | break; | 114 | break; |
100 | 115 | ||
@@ -129,7 +144,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
129 | 144 | ||
130 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | 145 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ |
131 | ret = -EIO; | 146 | ret = -EIO; |
132 | if ((addr & 3) || addr < 0 || | 147 | if ((addr & 3) || addr < 0 || |
133 | addr > sizeof(struct user) - 3) | 148 | addr > sizeof(struct user) - 3) |
134 | break; | 149 | break; |
135 | 150 | ||
@@ -156,6 +171,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
156 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 171 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
157 | else | 172 | else |
158 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 173 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
174 | |||
175 | ptrace_disable_singlestep(child); | ||
176 | |||
159 | child->exit_code = data; | 177 | child->exit_code = data; |
160 | wake_up_process(child); | 178 | wake_up_process(child); |
161 | ret = 0; | 179 | ret = 0; |
@@ -163,14 +181,15 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
163 | } | 181 | } |
164 | 182 | ||
165 | /* | 183 | /* |
166 | * make the child exit. Best I can do is send it a sigkill. | 184 | * make the child exit. Best I can do is send it a sigkill. |
167 | * perhaps it should be put in the status that it wants to | 185 | * perhaps it should be put in the status that it wants to |
168 | * exit. | 186 | * exit. |
169 | */ | 187 | */ |
170 | case PTRACE_KILL: { | 188 | case PTRACE_KILL: { |
171 | ret = 0; | 189 | ret = 0; |
172 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | 190 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ |
173 | break; | 191 | break; |
192 | ptrace_disable_singlestep(child); | ||
174 | child->exit_code = SIGKILL; | 193 | child->exit_code = SIGKILL; |
175 | wake_up_process(child); | 194 | wake_up_process(child); |
176 | break; | 195 | break; |
@@ -196,6 +215,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
196 | ubc_usercnt += 1; | 215 | ubc_usercnt += 1; |
197 | child->thread.ubc_pc = pc; | 216 | child->thread.ubc_pc = pc; |
198 | 217 | ||
218 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
199 | child->exit_code = data; | 219 | child->exit_code = data; |
200 | /* give it a chance to run. */ | 220 | /* give it a chance to run. */ |
201 | wake_up_process(child); | 221 | wake_up_process(child); |
@@ -248,14 +268,15 @@ asmlinkage void do_syscall_trace(void) | |||
248 | { | 268 | { |
249 | struct task_struct *tsk = current; | 269 | struct task_struct *tsk = current; |
250 | 270 | ||
251 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | 271 | if (!test_thread_flag(TIF_SYSCALL_TRACE) && |
272 | !test_thread_flag(TIF_SINGLESTEP)) | ||
252 | return; | 273 | return; |
253 | if (!(tsk->ptrace & PT_PTRACED)) | 274 | if (!(tsk->ptrace & PT_PTRACED)) |
254 | return; | 275 | return; |
255 | /* the 0x80 provides a way for the tracing parent to distinguish | 276 | /* the 0x80 provides a way for the tracing parent to distinguish |
256 | between a syscall stop and SIGTRAP delivery */ | 277 | between a syscall stop and SIGTRAP delivery */ |
257 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | 278 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) && |
258 | ? 0x80 : 0)); | 279 | !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0)); |
259 | 280 | ||
260 | /* | 281 | /* |
261 | * this isn't the same as continuing with a signal, but it will do | 282 | * this isn't the same as continuing with a signal, but it will do |