Re: [Patch 4/4] PPC64-HWBKPT: Enable hw-breakpoints while handling intervening signals

2010-05-26 Thread Paul Mackerras
On Tue, May 25, 2010 at 02:45:05PM +0530, K.Prasad wrote:

> 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.
> 
> Restore hw-breakpoints if the user-context is altered in the signal handler.

...

>  /*
> + * 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 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);
> +}

I think this function needs to clear current->thread.last_hit_ubp.
You also need to pass the regs to it so it can clear MSR_SE from
regs->msr.

> 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 
>  #include 
>  #include 
> +#include 
>  
>  #include "signal.h"
>  
> @@ -312,6 +313,7 @@ int sys_swapcontext(struct ucontext __us
>   || __copy_to_user(&old_ctx->uc_sigmask,
> ¤t->blocked, sizeof(sigset_t)))
>   return -EFAULT;
> + thread_change_pc(current);

This is in the part of the code that is saving the old context, after
the old context has been saved.  We need to do it before saving the
old context so that the saved context doesn't have the MSR_SE bit set
in its MSR image.  And similarly in the 32-bit version.

In fact, we probably don't need to do anything here.  We should never
get into a system call (such as sys_swapcontext or sys_sigreturn) when
single-stepping a load or store that caused a DABR match.  If we do,
something very strange is happening.  So I would leave out the
swapcontext changes for this version.

Paul.
___
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev


[Patch 4/4] PPC64-HWBKPT: Enable hw-breakpoints while handling intervening signals

2010-05-25 Thread K.Prasad
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.

Restore hw-breakpoints if the user-context is altered in the signal handler.

Signed-off-by: K.Prasad 
---
 arch/powerpc/include/asm/hw_breakpoint.h |2 ++
 arch/powerpc/kernel/hw_breakpoint.c  |   16 
 arch/powerpc/kernel/signal.c |3 +++
 arch/powerpc/kernel/signal_32.c  |2 ++
 arch/powerpc/kernel/signal_64.c  |2 ++
 5 files changed, 25 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
@@ -65,9 +65,11 @@ static inline void hw_breakpoint_disable
 {
set_dabr(0);
 }
+extern void thread_change_pc(struct task_struct *tsk);
 
 #else  /* CONFIG_HAVE_HW_BREAKPOINT */
 static inline void hw_breakpoint_disable(void) { }
+static inline void thread_change_pc(struct task_struct *tsk) { }
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
 #endif /* __KERNEL__ */
 #endif /* _PPC_BOOK3S_64_HW_BREAKPOINT_H */
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
@@ -176,6 +176,22 @@ int arch_validate_hwbkpt_settings(struct
 }
 
 /*
+ * 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 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);
+}
+
+/*
  * Handle debug exception notifications.
  */
 int __kprobes hw_breakpoint_handler(struct die_args *args)
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 
 #include 
+#include 
 #include 
 #include 
 
@@ -149,6 +150,8 @@ static int do_signal_pending(sigset_t *o
if (current->thread.dabr)
set_dabr(current->thread.dabr);
 #endif
+/* Re-enable the breakpoints for the signal stack */
+   thread_change_pc(current);
 
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 
 #include 
 #include 
+#include 
 
 #include "signal.h"
 
@@ -312,6 +313,7 @@ int sys_swapcontext(struct ucontext __us
|| __copy_to_user(&old_ctx->uc_sigmask,
  ¤t->blocked, sizeof(sigset_t)))
return -EFAULT;
+   thread_change_pc(current);
}
if (new_ctx == NULL)
return 0;
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 
 #include 
 #include 
+#include 
 #ifdef CONFIG_PPC64
 #include "ppc32.h"
 #include 
@@ -996,6 +997,7 @@ 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;
+   thread_change_pc(current);
}
if (new_ctx == NULL)
return 0;

___
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev


[Patch 4/4] PPC64-HWBKPT: Enable hw-breakpoints while handling intervening signals

2010-05-24 Thread K.Prasad
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 
---
 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
@@ -175,6 +175,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 
 #include 
+#include 
 #include 
 #include 
 
@@ -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 
 #include 
 #include 
+#include 
 
 #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
=

Re: [Patch 4/4] PPC64-HWBKPT: Enable hw-breakpoints while handling intervening signals

2010-05-24 Thread Paul Mackerras
On Mon, May 24, 2010 at 04:04:19PM +0530, K.Prasad wrote:

> 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.

I don't think this will actually cause a problem.  In the case of a
perf_event breakpoint, the semantics are trigger-after-execute, so the
first hit won't cause a trigger, and perf_event won't double-count it.
In the case of ptrace-style breakpoints, we don't single-step (it's up
to the ptracer to do the single-stepping if needed) so the problem
doesn't arise.

In fact I don't think we even need to do anything on sigreturn.  Yes,
we are changing the NIP but we are changing it to a previous value as
a result of an explicit action by the program, which is a bit
different to what signal delivery and ptrace do.

On signal delivery I was imagining that we would clear the MSR_SE bit
before saving the MSR value in the signal frame, and reinstall the
DABR value at the same time, and then essentially forget that we had
already hit the breakpoint once and just wait for it to hit again.

Paul.
___
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev