powerpc: Hardware breakpoints rewrite to handle non DABR breakpoint registers
This is a rewrite so that we don't assume we are using the DABR throughout the code. We now use the arch_hw_breakpoint to store the breakpoint in a generic manner in the thread_struct, rather than storing the raw DABR value. The ptrace GET/SET_DEBUGREG interface currently passes the raw DABR in from userspace. We keep this functionality, so that future changes (like the POWER8 DAWR), will still fake the DABR to userspace. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:

committed by
Benjamin Herrenschmidt

parent
a8190a59e7
commit
9422de3e95
@@ -271,7 +271,7 @@ void do_send_trap(struct pt_regs *regs, unsigned long address,
|
||||
force_sig_info(SIGTRAP, &info, current);
|
||||
}
|
||||
#else /* !CONFIG_PPC_ADV_DEBUG_REGS */
|
||||
void do_dabr(struct pt_regs *regs, unsigned long address,
|
||||
void do_break (struct pt_regs *regs, unsigned long address,
|
||||
unsigned long error_code)
|
||||
{
|
||||
siginfo_t info;
|
||||
@@ -281,11 +281,11 @@ void do_dabr(struct pt_regs *regs, unsigned long address,
|
||||
11, SIGSEGV) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
if (debugger_dabr_match(regs))
|
||||
if (debugger_break_match(regs))
|
||||
return;
|
||||
|
||||
/* Clear the DABR */
|
||||
set_dabr(0, 0);
|
||||
/* Clear the breakpoint */
|
||||
hw_breakpoint_disable();
|
||||
|
||||
/* Deliver the signal to userspace */
|
||||
info.si_signo = SIGTRAP;
|
||||
@@ -296,7 +296,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address,
|
||||
}
|
||||
#endif /* CONFIG_PPC_ADV_DEBUG_REGS */
|
||||
|
||||
static DEFINE_PER_CPU(unsigned long, current_dabr);
|
||||
static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk);
|
||||
|
||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
|
||||
/*
|
||||
@@ -364,39 +364,72 @@ static void switch_booke_debug_regs(struct thread_struct *new_thread)
|
||||
#ifndef CONFIG_HAVE_HW_BREAKPOINT
|
||||
static void set_debug_reg_defaults(struct thread_struct *thread)
|
||||
{
|
||||
if (thread->dabr) {
|
||||
thread->dabr = 0;
|
||||
thread->dabrx = 0;
|
||||
set_dabr(0, 0);
|
||||
}
|
||||
thread->hw_brk.address = 0;
|
||||
thread->hw_brk.type = 0;
|
||||
set_break(&thread->hw_brk);
|
||||
}
|
||||
#endif /* !CONFIG_HAVE_HW_BREAKPOINT */
|
||||
#endif /* CONFIG_PPC_ADV_DEBUG_REGS */
|
||||
|
||||
int set_dabr(unsigned long dabr, unsigned long dabrx)
|
||||
{
|
||||
__get_cpu_var(current_dabr) = dabr;
|
||||
|
||||
if (ppc_md.set_dabr)
|
||||
return ppc_md.set_dabr(dabr, dabrx);
|
||||
|
||||
/* XXX should we have a CPU_FTR_HAS_DABR ? */
|
||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
|
||||
static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
|
||||
{
|
||||
mtspr(SPRN_DAC1, dabr);
|
||||
#ifdef CONFIG_PPC_47x
|
||||
isync();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#elif defined(CONFIG_PPC_BOOK3S)
|
||||
static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
|
||||
{
|
||||
mtspr(SPRN_DABR, dabr);
|
||||
mtspr(SPRN_DABRX, dabrx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int set_dabr(struct arch_hw_breakpoint *brk)
|
||||
{
|
||||
unsigned long dabr, dabrx;
|
||||
|
||||
dabr = brk->address | (brk->type & HW_BRK_TYPE_DABR);
|
||||
dabrx = ((brk->type >> 3) & 0x7);
|
||||
|
||||
if (ppc_md.set_dabr)
|
||||
return ppc_md.set_dabr(dabr, dabrx);
|
||||
|
||||
return __set_dabr(dabr, dabrx);
|
||||
}
|
||||
|
||||
int set_break(struct arch_hw_breakpoint *brk)
|
||||
{
|
||||
__get_cpu_var(current_brk) = *brk;
|
||||
|
||||
return set_dabr(brk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
|
||||
#endif
|
||||
|
||||
static inline bool hw_brk_match(struct arch_hw_breakpoint *a,
|
||||
struct arch_hw_breakpoint *b)
|
||||
{
|
||||
if (a->address != b->address)
|
||||
return false;
|
||||
if (a->type != b->type)
|
||||
return false;
|
||||
if (a->len != b->len)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct task_struct *__switch_to(struct task_struct *prev,
|
||||
struct task_struct *new)
|
||||
{
|
||||
@@ -481,8 +514,8 @@ struct task_struct *__switch_to(struct task_struct *prev,
|
||||
* schedule DABR
|
||||
*/
|
||||
#ifndef CONFIG_HAVE_HW_BREAKPOINT
|
||||
if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr))
|
||||
set_dabr(new->thread.dabr, new->thread.dabrx);
|
||||
if (unlikely(hw_brk_match(&__get_cpu_var(current_brk), &new->thread.hw_brk)))
|
||||
set_break(&new->thread.hw_brk);
|
||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
||||
#endif
|
||||
|
||||
|
Reference in New Issue
Block a user