diff options
Diffstat (limited to 'arch/powerpc/kernel/ptrace.c')
| -rw-r--r-- | arch/powerpc/kernel/ptrace.c | 90 | 
1 files changed, 82 insertions, 8 deletions
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 79d8e56470d..c4970004d44 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -952,6 +952,10 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,  		arch_bp_generic_fields(data &  					(DABR_DATA_WRITE | DABR_DATA_READ),  							&attr.bp_type); + +		/* Enable breakpoint */ +		attr.disabled = false; +  		ret =  modify_user_hw_breakpoint(bp, &attr);  		if (ret) {  			ptrace_put_breakpoints(task); @@ -1037,7 +1041,7 @@ void ptrace_disable(struct task_struct *child)  }  #ifdef CONFIG_PPC_ADV_DEBUG_REGS -static long set_intruction_bp(struct task_struct *child, +static long set_instruction_bp(struct task_struct *child,  			      struct ppc_hw_breakpoint *bp_info)  {  	int slot; @@ -1338,6 +1342,12 @@ static int set_dac_range(struct task_struct *child,  static long ppc_set_hwdebug(struct task_struct *child,  		     struct ppc_hw_breakpoint *bp_info)  { +#ifdef CONFIG_HAVE_HW_BREAKPOINT +	int len = 0; +	struct thread_struct *thread = &(child->thread); +	struct perf_event *bp; +	struct perf_event_attr attr; +#endif /* CONFIG_HAVE_HW_BREAKPOINT */  #ifndef CONFIG_PPC_ADV_DEBUG_REGS  	unsigned long dabr;  #endif @@ -1365,7 +1375,7 @@ static long ppc_set_hwdebug(struct task_struct *child,  		if ((bp_info->trigger_type != PPC_BREAKPOINT_TRIGGER_EXECUTE) ||  		    (bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE))  			return -EINVAL; -		return set_intruction_bp(child, bp_info); +		return set_instruction_bp(child, bp_info);  	}  	if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)  		return set_dac(child, bp_info); @@ -1381,13 +1391,9 @@ static long ppc_set_hwdebug(struct task_struct *child,  	 */  	if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||  	    (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 || -	    bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT ||  	    bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)  		return -EINVAL; -	if (child->thread.dabr) -		return -ENOSPC; -  	if ((unsigned long)bp_info->addr >= TASK_SIZE)  		return -EIO; @@ -1397,6 +1403,50 @@ static long ppc_set_hwdebug(struct task_struct *child,  		dabr |= DABR_DATA_READ;  	if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)  		dabr |= DABR_DATA_WRITE; +#ifdef CONFIG_HAVE_HW_BREAKPOINT +	if (ptrace_get_breakpoints(child) < 0) +		return -ESRCH; + +	/* +	 * Check if the request is for 'range' breakpoints. We can +	 * support it if range < 8 bytes. +	 */ +	if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE) { +		len = bp_info->addr2 - bp_info->addr; +	} else if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) { +		ptrace_put_breakpoints(child); +		return -EINVAL; +	} +	bp = thread->ptrace_bps[0]; +	if (bp) { +		ptrace_put_breakpoints(child); +		return -ENOSPC; +	} + +	/* Create a new breakpoint request if one doesn't exist already */ +	hw_breakpoint_init(&attr); +	attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN; +	attr.bp_len = len; +	arch_bp_generic_fields(dabr & (DABR_DATA_WRITE | DABR_DATA_READ), +								&attr.bp_type); + +	thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, +					       ptrace_triggered, NULL, child); +	if (IS_ERR(bp)) { +		thread->ptrace_bps[0] = NULL; +		ptrace_put_breakpoints(child); +		return PTR_ERR(bp); +	} + +	ptrace_put_breakpoints(child); +	return 1; +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ + +	if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) +		return -EINVAL; + +	if (child->thread.dabr) +		return -ENOSPC;  	child->thread.dabr = dabr;  	child->thread.dabrx = DABRX_ALL; @@ -1405,8 +1455,13 @@ static long ppc_set_hwdebug(struct task_struct *child,  #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */  } -static long ppc_del_hwdebug(struct task_struct *child, long addr, long data) +static long ppc_del_hwdebug(struct task_struct *child, long data)  { +#ifdef CONFIG_HAVE_HW_BREAKPOINT +	int ret = 0; +	struct thread_struct *thread = &(child->thread); +	struct perf_event *bp; +#endif /* CONFIG_HAVE_HW_BREAKPOINT */  #ifdef CONFIG_PPC_ADV_DEBUG_REGS  	int rc; @@ -1426,10 +1481,25 @@ static long ppc_del_hwdebug(struct task_struct *child, long addr, long data)  #else  	if (data != 1)  		return -EINVAL; + +#ifdef CONFIG_HAVE_HW_BREAKPOINT +	if (ptrace_get_breakpoints(child) < 0) +		return -ESRCH; + +	bp = thread->ptrace_bps[0]; +	if (bp) { +		unregister_hw_breakpoint(bp); +		thread->ptrace_bps[0] = NULL; +	} else +		ret = -ENOENT; +	ptrace_put_breakpoints(child); +	return ret; +#else /* CONFIG_HAVE_HW_BREAKPOINT */  	if (child->thread.dabr == 0)  		return -ENOENT;  	child->thread.dabr = 0; +#endif /* CONFIG_HAVE_HW_BREAKPOINT */  	return 0;  #endif @@ -1536,7 +1606,11 @@ long arch_ptrace(struct task_struct *child, long request,  		dbginfo.data_bp_alignment = 4;  #endif  		dbginfo.sizeof_condition = 0; +#ifdef CONFIG_HAVE_HW_BREAKPOINT +		dbginfo.features = PPC_DEBUG_FEATURE_DATA_BP_RANGE; +#else  		dbginfo.features = 0; +#endif /* CONFIG_HAVE_HW_BREAKPOINT */  #endif /* CONFIG_PPC_ADV_DEBUG_REGS */  		if (!access_ok(VERIFY_WRITE, datavp, @@ -1563,7 +1637,7 @@ long arch_ptrace(struct task_struct *child, long request,  	}  	case PPC_PTRACE_DELHWDEBUG: { -		ret = ppc_del_hwdebug(child, addr, data); +		ret = ppc_del_hwdebug(child, data);  		break;  	}  |