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: Roland McGrath <roland@redhat.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
committed by
Benjamin Herrenschmidt
parent
dac4ccfb64
commit
ec097c84df
@@ -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 *);
|
|||||||
#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:
|
|||||||
* 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:
|
|||||||
\
|
\
|
||||||
/* 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:
|
|||||||
* 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 */ \
|
||||||
@@ -317,14 +317,14 @@ label:
|
|||||||
cmplw r12,r10; \
|
cmplw r12,r10; \
|
||||||
blt+ 2f; /* addr below exception vectors */ \
|
blt+ 2f; /* addr below exception vectors */ \
|
||||||
\
|
\
|
||||||
lis r10,DebugCrit@h; \
|
lis r10,DebugCrit@h; \
|
||||||
ori r10,r10,DebugCrit@l; \
|
ori r10,r10,DebugCrit@l; \
|
||||||
cmplw r12,r10; \
|
cmplw r12,r10; \
|
||||||
bgt+ 2f; /* addr above exception vectors */ \
|
bgt+ 2f; /* addr above exception vectors */ \
|
||||||
\
|
\
|
||||||
/* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)) {
|
||||||
|
Reference in New Issue
Block a user