diff options
Diffstat (limited to 'arch/powerpc/kernel/hw_breakpoint.c')
| -rw-r--r-- | arch/powerpc/kernel/hw_breakpoint.c | 88 | 
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;  	/*  | 
