diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 34 |
1 files changed, 30 insertions, 4 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 6a5b2b731f4..6f0ae1a9bfa 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -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)) { |