]> git.openfabrics.org - ~emulex/infiniband.git/commitdiff
powerpc, hw_breakpoint: Enable hw-breakpoints while handling intervening signals
authorK.Prasad <prasad@linux.vnet.ibm.com>
Tue, 15 Jun 2010 06:05:41 +0000 (11:35 +0530)
committerPaul Mackerras <paulus@samba.org>
Tue, 22 Jun 2010 09:40:50 +0000 (19:40 +1000)
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>
arch/powerpc/include/asm/hw_breakpoint.h
arch/powerpc/kernel/hw_breakpoint.c
arch/powerpc/kernel/signal.c

index b111713b593e8c91412906cbb33af5169c9864c9..6576bad1069c0d6ada17a99b97ed988f53696d8b 100644 (file)
@@ -65,9 +65,12 @@ static inline void hw_breakpoint_disable(void)
 {
        set_dabr(0);
 }
+extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
 
 #else  /* CONFIG_HAVE_HW_BREAKPOINT */
 static inline void hw_breakpoint_disable(void) { }
+static inline void thread_change_pc(struct task_struct *tsk,
+                                       struct pt_regs *regs) { }
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #endif /* __KERNEL__ */
 #endif /* _PPC_BOOK3S_64_HW_BREAKPOINT_H */
index 7a2ad5e84c16c6ebc3ecc988d83a54ddcd5466e0..7bd01a56d1944959e1acc9d3868a3bcdf17fdfaf 100644 (file)
@@ -174,6 +174,24 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
        return 0;
 }
 
+/*
+ * Restores the breakpoint on the debug registers.
+ * Invoke this function if it is known that the execution context is
+ * about to change to cause loss of MSR_SE settings.
+ */
+void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
+{
+       struct arch_hw_breakpoint *info;
+
+       if (likely(!tsk->thread.last_hit_ubp))
+               return;
+
+       info = counter_arch_bp(tsk->thread.last_hit_ubp);
+       regs->msr &= ~MSR_SE;
+       set_dabr(info->address | info->type | DABR_TRANSLATION);
+       tsk->thread.last_hit_ubp = NULL;
+}
+
 /*
  * Handle debug exception notifications.
  */
index a0afb555a7c98c28533e3fd48c55acb630cc142e..7109f5b1baa87bd63e36aa18910c7d0bdfa40b12 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/tracehook.h>
 #include <linux/signal.h>
+#include <asm/hw_breakpoint.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 
@@ -149,6 +150,8 @@ static int do_signal_pending(sigset_t *oldset, struct pt_regs *regs)
        if (current->thread.dabr)
                set_dabr(current->thread.dabr);
 #endif
+       /* Re-enable the breakpoints for the signal stack */
+       thread_change_pc(current, regs);
 
        if (is32) {
                if (ka.sa.sa_flags & SA_SIGINFO)