Commit ec097c84 authored by Roland McGrath's avatar Roland McGrath Committed by Benjamin Herrenschmidt

powerpc: Add PTRACE_SINGLEBLOCK support

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: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent dac4ccfb
...@@ -135,7 +135,9 @@ do { \ ...@@ -135,7 +135,9 @@ do { \
* These are defined as per linux/ptrace.h, which see. * These are defined as per linux/ptrace.h, which see.
*/ */
#define arch_has_single_step() (1) #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_single_step(struct task_struct *);
extern void user_enable_block_step(struct task_struct *);
extern void user_disable_single_step(struct task_struct *); extern void user_disable_single_step(struct task_struct *);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
...@@ -288,4 +290,6 @@ extern void user_disable_single_step(struct task_struct *); ...@@ -288,4 +290,6 @@ extern void user_disable_single_step(struct task_struct *);
#define PPC_PTRACE_PEEKUSR_3264 0x91 #define PPC_PTRACE_PEEKUSR_3264 0x91
#define PPC_PTRACE_POKEUSR_3264 0x90 #define PPC_PTRACE_POKEUSR_3264 0x90
#define PTRACE_SINGLEBLOCK 0x100 /* resume execution until next branch */
#endif /* _ASM_POWERPC_PTRACE_H */ #endif /* _ASM_POWERPC_PTRACE_H */
...@@ -256,7 +256,7 @@ label: ...@@ -256,7 +256,7 @@ label:
* off DE in the DSRR1 value and clearing the debug status. \ * off DE in the DSRR1 value and clearing the debug status. \
*/ \ */ \
mfspr r10,SPRN_DBSR; /* check single-step/branch taken */ \ 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; \ beq+ 2f; \
\ \
lis r10,KERNELBASE@h; /* check if exception in vectors */ \ lis r10,KERNELBASE@h; /* check if exception in vectors */ \
...@@ -271,7 +271,7 @@ label: ...@@ -271,7 +271,7 @@ label:
\ \
/* here it looks like we got an inappropriate debug exception. */ \ /* here it looks like we got an inappropriate debug exception. */ \
1: rlwinm r9,r9,0,~MSR_DE; /* clear DE in the CDRR1 value */ \ 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; \ mtspr SPRN_DBSR,r10; \
/* restore state and get out */ \ /* restore state and get out */ \
lwz r10,_CCR(r11); \ lwz r10,_CCR(r11); \
...@@ -309,7 +309,7 @@ label: ...@@ -309,7 +309,7 @@ label:
* off DE in the CSRR1 value and clearing the debug status. \ * off DE in the CSRR1 value and clearing the debug status. \
*/ \ */ \
mfspr r10,SPRN_DBSR; /* check single-step/branch taken */ \ 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; \ beq+ 2f; \
\ \
lis r10,KERNELBASE@h; /* check if exception in vectors */ \ lis r10,KERNELBASE@h; /* check if exception in vectors */ \
...@@ -324,7 +324,7 @@ label: ...@@ -324,7 +324,7 @@ label:
\ \
/* here it looks like we got an inappropriate debug exception. */ \ /* here it looks like we got an inappropriate debug exception. */ \
1: rlwinm r9,r9,0,~MSR_DE; /* clear DE in the CSRR1 value */ \ 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; \ mtspr SPRN_DBSR,r10; \
/* restore state and get out */ \ /* restore state and get out */ \
lwz r10,_CCR(r11); \ lwz r10,_CCR(r11); \
......
...@@ -704,15 +704,34 @@ void user_enable_single_step(struct task_struct *task) ...@@ -704,15 +704,34 @@ void user_enable_single_step(struct task_struct *task)
if (regs != NULL) { if (regs != NULL) {
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
task->thread.dbcr0 &= ~DBCR0_BT;
task->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC; task->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC;
regs->msr |= MSR_DE; regs->msr |= MSR_DE;
#else #else
regs->msr &= ~MSR_BE;
regs->msr |= MSR_SE; regs->msr |= MSR_SE;
#endif #endif
} }
set_tsk_thread_flag(task, TIF_SINGLESTEP); 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) void user_disable_single_step(struct task_struct *task)
{ {
struct pt_regs *regs = task->thread.regs; struct pt_regs *regs = task->thread.regs;
...@@ -726,10 +745,10 @@ void user_disable_single_step(struct task_struct *task) ...@@ -726,10 +745,10 @@ void user_disable_single_step(struct task_struct *task)
if (regs != NULL) { if (regs != NULL) {
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) #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; regs->msr &= ~MSR_DE;
#else #else
regs->msr &= ~MSR_SE; regs->msr &= ~(MSR_SE | MSR_BE);
#endif #endif
} }
clear_tsk_thread_flag(task, TIF_SINGLESTEP); clear_tsk_thread_flag(task, TIF_SINGLESTEP);
......
...@@ -1041,7 +1041,34 @@ void SoftwareEmulation(struct pt_regs *regs) ...@@ -1041,7 +1041,34 @@ void SoftwareEmulation(struct pt_regs *regs)
void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) 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; regs->msr &= ~MSR_DE;
/* Disable instruction completion */ /* Disable instruction completion */
...@@ -1057,9 +1084,8 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status) ...@@ -1057,9 +1084,8 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
if (debugger_sstep(regs)) if (debugger_sstep(regs))
return; return;
if (user_mode(regs)) { if (user_mode(regs))
current->thread.dbcr0 &= ~DBCR0_IC; current->thread.dbcr0 &= ~(DBCR0_IC);
}
_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip); _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
} else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) { } else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment