powerpc: Add PTRACE_SINGLEBLOCK support
authorRoland McGrath <roland@redhat.com>
Thu, 28 May 2009 21:26:38 +0000 (21:26 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 9 Jun 2009 03:29:25 +0000 (13:29 +1000)
Reworked by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This adds block-step support on powerpc, including a PTRACE_SINGLEBLOCK
request for ptrace.

The BookE implementation is tweaked to fire a single step after a
block step in order to mimmic the server behaviour.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/ptrace.h
arch/powerpc/kernel/head_booke.h
arch/powerpc/kernel/ptrace.c
arch/powerpc/kernel/traps.c

index c9c678fb2538d9e4f1c17cb54476663d8305485b..8c341490cfc5a9843719bb50a4590d3df24b024c 100644 (file)
@@ -135,7 +135,9 @@ do {                                                                              \
  * These are defined as per linux/ptrace.h, which see.
  */
 #define arch_has_single_step() (1)
+#define arch_has_block_step()  (!cpu_has_feature(CPU_FTR_601))
 extern void user_enable_single_step(struct task_struct *);
+extern void user_enable_block_step(struct task_struct *);
 extern void user_disable_single_step(struct task_struct *);
 
 #endif /* __ASSEMBLY__ */
@@ -288,4 +290,6 @@ extern void user_disable_single_step(struct task_struct *);
 #define PPC_PTRACE_PEEKUSR_3264  0x91
 #define PPC_PTRACE_POKEUSR_3264  0x90
 
+#define PTRACE_SINGLEBLOCK     0x100   /* resume execution until next branch */
+
 #endif /* _ASM_POWERPC_PTRACE_H */
index 95f39f1e68d449036b0206735936513c60533cdc..5f9febc8d1431c4daebb711f1496f9e1069a1b7e 100644 (file)
@@ -256,7 +256,7 @@ label:
         * off DE in the DSRR1 value and clearing the debug status.           \
         */                                                                   \
        mfspr   r10,SPRN_DBSR;          /* check single-step/branch taken */  \
-       andis.  r10,r10,DBSR_IC@h;                                            \
+       andis.  r10,r10,(DBSR_IC|DBSR_BT)@h;                                  \
        beq+    2f;                                                           \
                                                                              \
        lis     r10,KERNELBASE@h;       /* check if exception in vectors */   \
@@ -271,7 +271,7 @@ label:
                                                                              \
        /* here it looks like we got an inappropriate debug exception. */     \
 1:     rlwinm  r9,r9,0,~MSR_DE;        /* clear DE in the CDRR1 value */     \
-       lis     r10,DBSR_IC@h;          /* clear the IC event */              \
+       lis     r10,(DBSR_IC|DBSR_BT)@h;        /* clear the IC event */      \
        mtspr   SPRN_DBSR,r10;                                                \
        /* restore state and get out */                                       \
        lwz     r10,_CCR(r11);                                                \
@@ -309,7 +309,7 @@ label:
         * off DE in the CSRR1 value and clearing the debug status.           \
         */                                                                   \
        mfspr   r10,SPRN_DBSR;          /* check single-step/branch taken */  \
-       andis.  r10,r10,DBSR_IC@h;                                            \
+       andis.  r10,r10,(DBSR_IC|DBSR_BT)@h;                                  \
        beq+    2f;                                                           \
                                                                              \
        lis     r10,KERNELBASE@h;       /* check if exception in vectors */   \
@@ -317,14 +317,14 @@ label:
        cmplw   r12,r10;                                                      \
        blt+    2f;                     /* addr below exception vectors */    \
                                                                              \
-       lis     r10,DebugCrit@h;                                                      \
+       lis     r10,DebugCrit@h;                                              \
        ori     r10,r10,DebugCrit@l;                                          \
        cmplw   r12,r10;                                                      \
        bgt+    2f;                     /* addr above exception vectors */    \
                                                                              \
        /* here it looks like we got an inappropriate debug exception. */     \
 1:     rlwinm  r9,r9,0,~MSR_DE;        /* clear DE in the CSRR1 value */     \
-       lis     r10,DBSR_IC@h;          /* clear the IC event */              \
+       lis     r10,(DBSR_IC|DBSR_BT)@h;        /* clear the IC event */      \
        mtspr   SPRN_DBSR,r10;                                                \
        /* restore state and get out */                                       \
        lwz     r10,_CCR(r11);                                                \
index 3635be61f89995bc43e5dfdbc6e0acc7327f1399..9fa2c7dcd05a4501034395e6f587523cffb969d0 100644 (file)
@@ -704,15 +704,34 @@ void user_enable_single_step(struct task_struct *task)
 
        if (regs != NULL) {
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
+               task->thread.dbcr0 &= ~DBCR0_BT;
                task->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC;
                regs->msr |= MSR_DE;
 #else
+               regs->msr &= ~MSR_BE;
                regs->msr |= MSR_SE;
 #endif
        }
        set_tsk_thread_flag(task, TIF_SINGLESTEP);
 }
 
+void user_enable_block_step(struct task_struct *task)
+{
+       struct pt_regs *regs = task->thread.regs;
+
+       if (regs != NULL) {
+#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
+               task->thread.dbcr0 &= ~DBCR0_IC;
+               task->thread.dbcr0 = DBCR0_IDM | DBCR0_BT;
+               regs->msr |= MSR_DE;
+#else
+               regs->msr &= ~MSR_SE;
+               regs->msr |= MSR_BE;
+#endif
+       }
+       set_tsk_thread_flag(task, TIF_SINGLESTEP);
+}
+
 void user_disable_single_step(struct task_struct *task)
 {
        struct pt_regs *regs = task->thread.regs;
@@ -726,10 +745,10 @@ void user_disable_single_step(struct task_struct *task)
 
        if (regs != NULL) {
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
-               task->thread.dbcr0 &= ~(DBCR0_IC | DBCR0_IDM);
+               task->thread.dbcr0 &= ~(DBCR0_IC | DBCR0_BT | DBCR0_IDM);
                regs->msr &= ~MSR_DE;
 #else
-               regs->msr &= ~MSR_SE;
+               regs->msr &= ~(MSR_SE | MSR_BE);
 #endif
        }
        clear_tsk_thread_flag(task, TIF_SINGLESTEP);
index 6a5b2b731f43da18e8add90f3742003dd27475be..6f0ae1a9bfae6d78b31afdf15f1c22d554e5900d 100644 (file)
@@ -1041,7 +1041,34 @@ void SoftwareEmulation(struct pt_regs *regs)
 
 void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
 {
-       if (debug_status & DBSR_IC) {   /* instruction completion */
+       /* Hack alert: On BookE, Branch Taken stops on the branch itself, while
+        * on server, it stops on the target of the branch. In order to simulate
+        * the server behaviour, we thus restart right away with a single step
+        * instead of stopping here when hitting a BT
+        */
+       if (debug_status & DBSR_BT) {
+               regs->msr &= ~MSR_DE;
+
+               /* Disable BT */
+               mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_BT);
+               /* Clear the BT event */
+               mtspr(SPRN_DBSR, DBSR_BT);
+
+               /* Do the single step trick only when coming from userspace */
+               if (user_mode(regs)) {
+                       current->thread.dbcr0 &= ~DBCR0_BT;
+                       current->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC;
+                       regs->msr |= MSR_DE;
+                       return;
+               }
+
+               if (notify_die(DIE_SSTEP, "block_step", regs, 5,
+                              5, SIGTRAP) == NOTIFY_STOP) {
+                       return;
+               }
+               if (debugger_sstep(regs))
+                       return;
+       } else if (debug_status & DBSR_IC) {    /* Instruction complete */
                regs->msr &= ~MSR_DE;
 
                /* Disable instruction completion */
@@ -1057,9 +1084,8 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
                if (debugger_sstep(regs))
                        return;
 
-               if (user_mode(regs)) {
-                       current->thread.dbcr0 &= ~DBCR0_IC;
-               }
+               if (user_mode(regs))
+                       current->thread.dbcr0 &= ~(DBCR0_IC);
 
                _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
        } else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) {