aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorK.Prasad <prasad@linux.vnet.ibm.com>2010-06-15 02:05:41 -0400
committerPaul Mackerras <paulus@samba.org>2010-06-22 05:40:50 -0400
commit06532a6743d83fac4b79389fc8c86c88cb4e3302 (patch)
tree2ca694f5cb71a2296c6b4e0b36f3ddd6e2033084
parent2538c2d08f46141550a1e68819efa8fe31c6e3dc (diff)
powerpc, hw_breakpoint: Enable hw-breakpoints while handling intervening signals
A signal delivered between a hw_breakpoint_handler() and the single_step_dabr_instruction() will not have the breakpoint active while the signal handler is running -- the signal delivery will set up a new MSR value which will not have MSR_SE set, so we won't get the signal step interrupt until and unless the signal handler returns (which it may never do). To fix this, we restore the breakpoint when delivering a signal -- we clear the MSR_SE bit and set the DABR again. If the signal handler returns, the DABR interrupt will occur again when the instruction that we were originally trying to single-step gets re-executed. [Paul Mackerras <paulus@samba.org> pointed out the need to do this.] Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/include/asm/hw_breakpoint.h3
-rw-r--r--arch/powerpc/kernel/hw_breakpoint.c18
-rw-r--r--arch/powerpc/kernel/signal.c3
3 files changed, 24 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h
index b111713b593e..6576bad1069c 100644
--- a/arch/powerpc/include/asm/hw_breakpoint.h
+++ b/arch/powerpc/include/asm/hw_breakpoint.h
@@ -65,9 +65,12 @@ static inline void hw_breakpoint_disable(void)
65{ 65{
66 set_dabr(0); 66 set_dabr(0);
67} 67}
68extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
68 69
69#else /* CONFIG_HAVE_HW_BREAKPOINT */ 70#else /* CONFIG_HAVE_HW_BREAKPOINT */
70static inline void hw_breakpoint_disable(void) { } 71static inline void hw_breakpoint_disable(void) { }
72static inline void thread_change_pc(struct task_struct *tsk,
73 struct pt_regs *regs) { }
71#endif /* CONFIG_HAVE_HW_BREAKPOINT */ 74#endif /* CONFIG_HAVE_HW_BREAKPOINT */
72#endif /* __KERNEL__ */ 75#endif /* __KERNEL__ */
73#endif /* _PPC_BOOK3S_64_HW_BREAKPOINT_H */ 76#endif /* _PPC_BOOK3S_64_HW_BREAKPOINT_H */
diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index 7a2ad5e84c16..7bd01a56d194 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -175,6 +175,24 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
175} 175}
176 176
177/* 177/*
178 * Restores the breakpoint on the debug registers.
179 * Invoke this function if it is known that the execution context is
180 * about to change to cause loss of MSR_SE settings.
181 */
182void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
183{
184 struct arch_hw_breakpoint *info;
185
186 if (likely(!tsk->thread.last_hit_ubp))
187 return;
188
189 info = counter_arch_bp(tsk->thread.last_hit_ubp);
190 regs->msr &= ~MSR_SE;
191 set_dabr(info->address | info->type | DABR_TRANSLATION);
192 tsk->thread.last_hit_ubp = NULL;
193}
194
195/*
178 * Handle debug exception notifications. 196 * Handle debug exception notifications.
179 */ 197 */
180int __kprobes hw_breakpoint_handler(struct die_args *args) 198int __kprobes hw_breakpoint_handler(struct die_args *args)
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
index a0afb555a7c9..7109f5b1baa8 100644
--- a/arch/powerpc/kernel/signal.c
+++ b/arch/powerpc/kernel/signal.c
@@ -11,6 +11,7 @@
11 11
12#include <linux/tracehook.h> 12#include <linux/tracehook.h>
13#include <linux/signal.h> 13#include <linux/signal.h>
14#include <asm/hw_breakpoint.h>
14#include <asm/uaccess.h> 15#include <asm/uaccess.h>
15#include <asm/unistd.h> 16#include <asm/unistd.h>
16 17
@@ -149,6 +150,8 @@ static int do_signal_pending(sigset_t *oldset, struct pt_regs *regs)
149 if (current->thread.dabr) 150 if (current->thread.dabr)
150 set_dabr(current->thread.dabr); 151 set_dabr(current->thread.dabr);
151#endif 152#endif
153 /* Re-enable the breakpoints for the signal stack */
154 thread_change_pc(current, regs);
152 155
153 if (is32) { 156 if (is32) {
154 if (ka.sa.sa_flags & SA_SIGINFO) 157 if (ka.sa.sa_flags & SA_SIGINFO)