[RFC Patch 4/5] PPC64-HWBKPT: Enable hw-breakpoints while handling intervening signals
K.Prasad
prasad at linux.vnet.ibm.com
Mon May 24 14:03:42 EST 2010
A signal delivered between a hw_breakpoint_handler() and the
single_step_dabr_instruction() will not have the breakpoint active during
signal handling (since breakpoint will not be restored through single-stepping
due to absence of MSR_SE bit on the signal frame). Enable breakpoints before
signal delivery and clear them during sigreturn() syscall.
Limitation: Nested hw-breakpoint exceptions (where second exception is raised
inside signal context) will cause a 'double-hit' i.e. the first breakpoint
exception will be taken twice.
Restore hw-breakpoints if the user-context is altered in the signal handler
(causing loss of MSR_SE).
Side-effect: 'Double-hit' of breakpoint if the instruction pointer is
unaltered in the new context.
Signed-off-by: K.Prasad <prasad at linux.vnet.ibm.com>
---
arch/powerpc/include/asm/hw_breakpoint.h | 3 +++
arch/powerpc/kernel/hw_breakpoint.c | 28 ++++++++++++++++++++++++++++
arch/powerpc/kernel/signal.c | 8 ++++++++
arch/powerpc/kernel/signal_32.c | 10 ++++++++++
arch/powerpc/kernel/signal_64.c | 7 +++++++
5 files changed, 56 insertions(+)
Index: linux-2.6.ppc64_test/arch/powerpc/include/asm/hw_breakpoint.h
===================================================================
--- linux-2.6.ppc64_test.orig/arch/powerpc/include/asm/hw_breakpoint.h
+++ linux-2.6.ppc64_test/arch/powerpc/include/asm/hw_breakpoint.h
@@ -43,6 +43,9 @@ static inline void hw_breakpoint_disable
{
set_dabr(0);
}
+extern void sighandler_install_bp(struct task_struct *tsk);
+extern void sigreturn_uninstall_bp(struct task_struct *tsk);
+extern void thread_change_pc(struct task_struct *tsk, unsigned long msr);
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
#endif /* __KERNEL__ */
Index: linux-2.6.ppc64_test/arch/powerpc/kernel/hw_breakpoint.c
===================================================================
--- linux-2.6.ppc64_test.orig/arch/powerpc/kernel/hw_breakpoint.c
+++ linux-2.6.ppc64_test/arch/powerpc/kernel/hw_breakpoint.c
@@ -188,6 +188,34 @@ int arch_validate_hwbkpt_settings(struct
return 0;
}
+void sighandler_install_bp(struct task_struct *tsk)
+{
+ struct arch_hw_breakpoint *info;
+
+ if (likely(!tsk->thread.last_hit_ubp))
+ return;
+
+ info = counter_arch_bp(tsk->thread.last_hit_ubp);
+ set_dabr(info->address | info->type | DABR_TRANSLATION);
+}
+
+void sigreturn_uninstall_bp(struct task_struct *tsk)
+{
+ if (unlikely(tsk->thread.last_hit_ubp))
+ set_dabr(0);
+}
+
+void thread_change_pc(struct task_struct *tsk, unsigned long new_msr)
+{
+ /*
+ * Do not bother to restore breakpoints if single-stepping is not
+ * cleared. single_step_dabr_instruction() will handle it if MSR_SE
+ * is set.
+ */
+ if (!(new_msr & MSR_SE))
+ sighandler_install_bp(tsk);
+}
+
/*
* Handle debug exception notifications.
*/
Index: linux-2.6.ppc64_test/arch/powerpc/kernel/signal.c
===================================================================
--- linux-2.6.ppc64_test.orig/arch/powerpc/kernel/signal.c
+++ linux-2.6.ppc64_test/arch/powerpc/kernel/signal.c
@@ -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,13 @@ static int do_signal_pending(sigset_t *o
if (current->thread.dabr)
set_dabr(current->thread.dabr);
#endif
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ /*
+ * Re-enable the breakpoints (if it was previously cleared in
+ * hw_breakpoint_handler()) for the signal stack.
+ */
+ sighandler_install_bp(current);
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
if (is32) {
if (ka.sa.sa_flags & SA_SIGINFO)
Index: linux-2.6.ppc64_test/arch/powerpc/kernel/signal_64.c
===================================================================
--- linux-2.6.ppc64_test.orig/arch/powerpc/kernel/signal_64.c
+++ linux-2.6.ppc64_test/arch/powerpc/kernel/signal_64.c
@@ -33,6 +33,7 @@
#include <asm/cacheflush.h>
#include <asm/syscalls.h>
#include <asm/vdso.h>
+#include <asm/hw_breakpoint.h>
#include "signal.h"
@@ -312,6 +313,9 @@ int sys_swapcontext(struct ucontext __us
|| __copy_to_user(&old_ctx->uc_sigmask,
¤t->blocked, sizeof(sigset_t)))
return -EFAULT;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ thread_change_pc(current, new_msr);
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
}
if (new_ctx == NULL)
return 0;
@@ -364,6 +368,9 @@ int sys_rt_sigreturn(unsigned long r3, u
if (__copy_from_user(&set, &uc->uc_sigmask, sizeof(set)))
goto badframe;
restore_sigmask(&set);
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ sigreturn_uninstall_bp(current);
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
if (restore_sigcontext(regs, NULL, 1, &uc->uc_mcontext))
goto badframe;
Index: linux-2.6.ppc64_test/arch/powerpc/kernel/signal_32.c
===================================================================
--- linux-2.6.ppc64_test.orig/arch/powerpc/kernel/signal_32.c
+++ linux-2.6.ppc64_test/arch/powerpc/kernel/signal_32.c
@@ -42,6 +42,7 @@
#include <asm/syscalls.h>
#include <asm/sigcontext.h>
#include <asm/vdso.h>
+#include <asm/hw_breakpoint.h>
#ifdef CONFIG_PPC64
#include "ppc32.h"
#include <asm/unistd.h>
@@ -996,6 +997,9 @@ long sys_swapcontext(struct ucontext __u
|| put_sigset_t(&old_ctx->uc_sigmask, ¤t->blocked)
|| __put_user(to_user_ptr(mctx), &old_ctx->uc_regs))
return -EFAULT;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ thread_change_pc(current, new_msr);
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
}
if (new_ctx == NULL)
return 0;
@@ -1034,6 +1038,9 @@ long sys_rt_sigreturn(int r3, int r4, in
(regs->gpr[1] + __SIGNAL_FRAMESIZE + 16);
if (!access_ok(VERIFY_READ, rt_sf, sizeof(*rt_sf)))
goto bad;
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ sigreturn_uninstall_bp(current);
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
if (do_setcontext(&rt_sf->uc, regs, 1))
goto bad;
@@ -1279,6 +1286,9 @@ long sys_sigreturn(int r3, int r4, int r
#endif
restore_sigmask(&set);
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ sigreturn_uninstall_bp(current);
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
sr = (struct mcontext __user *)from_user_ptr(sigctx.regs);
addr = sr;
if (!access_ok(VERIFY_READ, sr, sizeof(*sr))
More information about the Linuxppc-dev
mailing list