powerpc: Rework set_dabr so it can take a DABRX value as well
Rework set_dabr to take a DABRX value as well. Both the pseries and PS3 hypervisors do some checks on the DABRX values that are passed in the hcall. This patch stops bogus values from being passed to hypervisor. Also, in the case where we are clearing the breakpoint, where DABR and DABRX are zero, we modify the DABRX value to make it valid so that the hcall won't fail. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
这个提交包含在:

提交者
Benjamin Herrenschmidt

父节点
3ab96a02e8
当前提交
4474ef055c
@@ -73,7 +73,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
|
||||
* If so, DABR will be populated in single_step_dabr_instruction().
|
||||
*/
|
||||
if (current->thread.last_hit_ubp != bp)
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION);
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -97,7 +97,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
|
||||
}
|
||||
|
||||
*slot = NULL;
|
||||
set_dabr(0);
|
||||
set_dabr(0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -197,7 +197,7 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
|
||||
|
||||
info = counter_arch_bp(tsk->thread.last_hit_ubp);
|
||||
regs->msr &= ~MSR_SE;
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION);
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
|
||||
tsk->thread.last_hit_ubp = NULL;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
|
||||
unsigned long dar = regs->dar;
|
||||
|
||||
/* Disable breakpoints during exception handling */
|
||||
set_dabr(0);
|
||||
set_dabr(0, 0);
|
||||
|
||||
/*
|
||||
* The counter may be concurrently released but that can only
|
||||
@@ -281,7 +281,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
|
||||
if (!info->extraneous_interrupt)
|
||||
perf_bp_event(bp, regs);
|
||||
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION);
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return rc;
|
||||
@@ -313,7 +313,7 @@ int __kprobes single_step_dabr_instruction(struct die_args *args)
|
||||
if (!info->extraneous_interrupt)
|
||||
perf_bp_event(bp, regs);
|
||||
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION);
|
||||
set_dabr(info->address | info->type | DABR_TRANSLATION, DABRX_ALL);
|
||||
current->thread.last_hit_ubp = NULL;
|
||||
|
||||
/*
|
||||
|
@@ -285,7 +285,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address,
|
||||
return;
|
||||
|
||||
/* Clear the DABR */
|
||||
set_dabr(0);
|
||||
set_dabr(0, 0);
|
||||
|
||||
/* Deliver the signal to userspace */
|
||||
info.si_signo = SIGTRAP;
|
||||
@@ -366,18 +366,19 @@ static void set_debug_reg_defaults(struct thread_struct *thread)
|
||||
{
|
||||
if (thread->dabr) {
|
||||
thread->dabr = 0;
|
||||
set_dabr(0);
|
||||
thread->dabrx = 0;
|
||||
set_dabr(0, 0);
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_HAVE_HW_BREAKPOINT */
|
||||
#endif /* CONFIG_PPC_ADV_DEBUG_REGS */
|
||||
|
||||
int set_dabr(unsigned long dabr)
|
||||
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);
|
||||
return ppc_md.set_dabr(dabr, dabrx);
|
||||
|
||||
/* XXX should we have a CPU_FTR_HAS_DABR ? */
|
||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
|
||||
@@ -387,9 +388,8 @@ int set_dabr(unsigned long dabr)
|
||||
#endif
|
||||
#elif defined(CONFIG_PPC_BOOK3S)
|
||||
mtspr(SPRN_DABR, dabr);
|
||||
mtspr(SPRN_DABRX, dabrx);
|
||||
#endif
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
|
||||
*/
|
||||
#ifndef CONFIG_HAVE_HW_BREAKPOINT
|
||||
if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr))
|
||||
set_dabr(new->thread.dabr);
|
||||
set_dabr(new->thread.dabr, new->thread.dabrx);
|
||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
||||
#endif
|
||||
|
||||
|
@@ -960,6 +960,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
|
||||
thread->ptrace_bps[0] = bp;
|
||||
ptrace_put_breakpoints(task);
|
||||
thread->dabr = data;
|
||||
thread->dabrx = DABRX_ALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -983,6 +984,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
|
||||
|
||||
/* Move contents to the DABR register */
|
||||
task->thread.dabr = data;
|
||||
task->thread.dabrx = DABRX_ALL;
|
||||
#else /* CONFIG_PPC_ADV_DEBUG_REGS */
|
||||
/* As described above, it was assumed 3 bits were passed with the data
|
||||
* address, but we will assume only the mode bits will be passed
|
||||
@@ -1397,6 +1399,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
|
||||
dabr |= DABR_DATA_WRITE;
|
||||
|
||||
child->thread.dabr = dabr;
|
||||
child->thread.dabrx = DABRX_ALL;
|
||||
|
||||
return 1;
|
||||
#endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
|
||||
|
@@ -131,7 +131,7 @@ static int do_signal(struct pt_regs *regs)
|
||||
* triggered inside the kernel.
|
||||
*/
|
||||
if (current->thread.dabr)
|
||||
set_dabr(current->thread.dabr);
|
||||
set_dabr(current->thread.dabr, current->thread.dabrx);
|
||||
#endif
|
||||
/* Re-enable the breakpoints for the signal stack */
|
||||
thread_change_pc(current, regs);
|
||||
|
在新工单中引用
屏蔽一个用户