aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/ptrace.c
diff options
context:
space:
mode:
authorStuart Menefy <stuart.menefy@st.com>2007-02-22 23:22:17 -0500
committerPaul Mundt <lethal@linux-sh.org>2007-03-05 00:13:25 -0500
commit9432f96803139adaff0cd9f4fa38b7fb99cda366 (patch)
treee2006ca8390dc4c8b14d08c658ccd5bfd80d401e /arch/sh/kernel/ptrace.c
parent20b0f65d35ae45d43f363ace3744a775a752d265 (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.c45
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
60static 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 */
67void ptrace_disable(struct task_struct *child) 82void ptrace_disable(struct task_struct *child)
68{ 83{
69 /* nothing to do.. */ 84 ptrace_disable_singlestep(child);
70} 85}
71 86
72long arch_ptrace(struct task_struct *child, long request, long addr, long data) 87long 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