aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/kernel/hw_breakpoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/hw_breakpoint.c')
-rw-r--r--arch/powerpc/kernel/hw_breakpoint.c88
1 files changed, 45 insertions, 43 deletions
diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index 5ecd0401cdb..0bb5918faaa 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -27,9 +27,7 @@
#include <linux/kprobes.h>
#include <linux/percpu.h>
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/sched.h>
-#include <linux/init.h>
#include <linux/smp.h>
#include <asm/hw_breakpoint.h>
@@ -74,7 +72,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_breakpoint(info);
return 0;
}
@@ -98,7 +96,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
}
*slot = NULL;
- set_dabr(0);
+ hw_breakpoint_disable();
}
/*
@@ -112,7 +110,7 @@ void arch_unregister_hw_breakpoint(struct perf_event *bp)
* and the single_step_dabr_instruction(), then cleanup the breakpoint
* restoration variables to prevent dangling pointers.
*/
- if (bp->ctx->task)
+ if (bp->ctx && bp->ctx->task)
bp->ctx->task->thread.last_hit_ubp = NULL;
}
@@ -128,19 +126,13 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
int arch_bp_generic_fields(int type, int *gen_bp_type)
{
- switch (type) {
- case DABR_DATA_READ:
- *gen_bp_type = HW_BREAKPOINT_R;
- break;
- case DABR_DATA_WRITE:
- *gen_bp_type = HW_BREAKPOINT_W;
- break;
- case (DABR_DATA_WRITE | DABR_DATA_READ):
- *gen_bp_type = (HW_BREAKPOINT_W | HW_BREAKPOINT_R);
- break;
- default:
+ *gen_bp_type = 0;
+ if (type & HW_BRK_TYPE_READ)
+ *gen_bp_type |= HW_BREAKPOINT_R;
+ if (type & HW_BRK_TYPE_WRITE)
+ *gen_bp_type |= HW_BREAKPOINT_W;
+ if (*gen_bp_type == 0)
return -EINVAL;
- }
return 0;
}
@@ -149,26 +141,26 @@ int arch_bp_generic_fields(int type, int *gen_bp_type)
*/
int arch_validate_hwbkpt_settings(struct perf_event *bp)
{
- int ret = -EINVAL;
+ int ret = -EINVAL, length_max;
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
if (!bp)
return ret;
- switch (bp->attr.bp_type) {
- case HW_BREAKPOINT_R:
- info->type = DABR_DATA_READ;
- break;
- case HW_BREAKPOINT_W:
- info->type = DABR_DATA_WRITE;
- break;
- case HW_BREAKPOINT_R | HW_BREAKPOINT_W:
- info->type = (DABR_DATA_READ | DABR_DATA_WRITE);
- break;
- default:
+ info->type = HW_BRK_TYPE_TRANSLATE;
+ if (bp->attr.bp_type & HW_BREAKPOINT_R)
+ info->type |= HW_BRK_TYPE_READ;
+ if (bp->attr.bp_type & HW_BREAKPOINT_W)
+ info->type |= HW_BRK_TYPE_WRITE;
+ if (info->type == HW_BRK_TYPE_TRANSLATE)
+ /* must set alteast read or write */
return ret;
- }
-
+ if (!(bp->attr.exclude_user))
+ info->type |= HW_BRK_TYPE_USER;
+ if (!(bp->attr.exclude_kernel))
+ info->type |= HW_BRK_TYPE_KERNEL;
+ if (!(bp->attr.exclude_hv))
+ info->type |= HW_BRK_TYPE_HYP;
info->address = bp->attr.bp_addr;
info->len = bp->attr.bp_len;
@@ -178,8 +170,16 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
* HW_BREAKPOINT_ALIGN by rounding off to the lower address, the
* 'symbolsize' should satisfy the check below.
*/
+ length_max = 8; /* DABR */
+ if (cpu_has_feature(CPU_FTR_DAWR)) {
+ length_max = 512 ; /* 64 doublewords */
+ /* DAWR region can't cross 512 boundary */
+ if ((bp->attr.bp_addr >> 10) !=
+ ((bp->attr.bp_addr + bp->attr.bp_len - 1) >> 10))
+ return -EINVAL;
+ }
if (info->len >
- (HW_BREAKPOINT_LEN - (info->address & HW_BREAKPOINT_ALIGN)))
+ (length_max - (info->address & HW_BREAKPOINT_ALIGN)))
return -EINVAL;
return 0;
}
@@ -198,7 +198,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_breakpoint(info);
tsk->thread.last_hit_ubp = NULL;
}
@@ -216,7 +216,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
unsigned long dar = regs->dar;
/* Disable breakpoints during exception handling */
- set_dabr(0);
+ hw_breakpoint_disable();
/*
* The counter may be concurrently released but that can only
@@ -249,12 +249,14 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
* we still need to single-step the instruction, but we don't
* generate an event.
*/
- info->extraneous_interrupt = !((bp->attr.bp_addr <= dar) &&
- (dar - bp->attr.bp_addr < bp->attr.bp_len));
+ info->type &= ~HW_BRK_TYPE_EXTRANEOUS_IRQ;
+ if (!((bp->attr.bp_addr <= dar) &&
+ (dar - bp->attr.bp_addr < bp->attr.bp_len)))
+ info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
/* Do not emulate user-space instructions, instead single-step them */
if (user_mode(regs)) {
- bp->ctx->task->thread.last_hit_ubp = bp;
+ current->thread.last_hit_ubp = bp;
regs->msr |= MSR_SE;
goto out;
}
@@ -279,10 +281,10 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
* As a policy, the callback is invoked in a 'trigger-after-execute'
* fashion
*/
- if (!info->extraneous_interrupt)
+ if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ))
perf_bp_event(bp, regs);
- set_dabr(info->address | info->type | DABR_TRANSLATION);
+ __set_breakpoint(info);
out:
rcu_read_unlock();
return rc;
@@ -295,7 +297,7 @@ int __kprobes single_step_dabr_instruction(struct die_args *args)
{
struct pt_regs *regs = args->regs;
struct perf_event *bp = NULL;
- struct arch_hw_breakpoint *bp_info;
+ struct arch_hw_breakpoint *info;
bp = current->thread.last_hit_ubp;
/*
@@ -305,16 +307,16 @@ int __kprobes single_step_dabr_instruction(struct die_args *args)
if (!bp)
return NOTIFY_DONE;
- bp_info = counter_arch_bp(bp);
+ info = counter_arch_bp(bp);
/*
* We shall invoke the user-defined callback function in the single
* stepping handler to confirm to 'trigger-after-execute' semantics
*/
- if (!bp_info->extraneous_interrupt)
+ if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ))
perf_bp_event(bp, regs);
- set_dabr(bp_info->address | bp_info->type | DABR_TRANSLATION);
+ __set_breakpoint(info);
current->thread.last_hit_ubp = NULL;
/*