mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	sh: Convert ptrace to hw_breakpoint API.
This is the initial step for converting singlestep handling via ptrace over to hw_breakpoints. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
		
							parent
							
								
									2264873540
								
							
						
					
					
						commit
						34d0b5af50
					
				
					 3 changed files with 71 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -124,6 +124,12 @@ struct task_struct;
 | 
			
		|||
extern void user_enable_single_step(struct task_struct *);
 | 
			
		||||
extern void user_disable_single_step(struct task_struct *);
 | 
			
		||||
 | 
			
		||||
struct perf_event;
 | 
			
		||||
struct perf_sample_data;
 | 
			
		||||
 | 
			
		||||
extern void ptrace_triggered(struct perf_event *bp, int nmi,
 | 
			
		||||
		      struct perf_sample_data *data, struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
#define task_pt_regs(task) \
 | 
			
		||||
	((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
#include <linux/io.h>
 | 
			
		||||
#include <asm/hw_breakpoint.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
 | 
			
		||||
struct ubc_context {
 | 
			
		||||
	unsigned long pc;
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +373,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
 | 
			
		|||
		rcu_read_unlock();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bp) {
 | 
			
		||||
	if (bp && bp->overflow_handler != ptrace_triggered) {
 | 
			
		||||
		struct arch_hw_breakpoint *info = counter_arch_bp(bp);
 | 
			
		||||
 | 
			
		||||
		__raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0);
 | 
			
		||||
| 
						 | 
				
			
			@ -387,9 +388,19 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
 | 
			
		|||
BUILD_TRAP_HANDLER(breakpoint)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long ex = lookup_exception_vector();
 | 
			
		||||
	siginfo_t info;
 | 
			
		||||
	int err;
 | 
			
		||||
	TRAP_HANDLER_DECL;
 | 
			
		||||
 | 
			
		||||
	notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP);
 | 
			
		||||
	err = notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP);
 | 
			
		||||
	if (err == NOTIFY_STOP)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Deliver the signal to userspace */
 | 
			
		||||
	info.si_signo = SIGTRAP;
 | 
			
		||||
	info.si_errno = 0;
 | 
			
		||||
	info.si_code = TRAP_HWBKPT;
 | 
			
		||||
	force_sig_info(SIGTRAP, &info, current);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
 * SuperH process tracing
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
 | 
			
		||||
 * Copyright (C) 2002 - 2008  Paul Mundt
 | 
			
		||||
 * Copyright (C) 2002 - 2009  Paul Mundt
 | 
			
		||||
 *
 | 
			
		||||
 * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +26,7 @@
 | 
			
		|||
#include <linux/tracehook.h>
 | 
			
		||||
#include <linux/elf.h>
 | 
			
		||||
#include <linux/regset.h>
 | 
			
		||||
#include <linux/hw_breakpoint.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -63,9 +64,59 @@ static inline int put_stack_long(struct task_struct *task, int offset,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ptrace_triggered(struct perf_event *bp, int nmi,
 | 
			
		||||
		      struct perf_sample_data *data, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	struct perf_event_attr attr;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Disable the breakpoint request here since ptrace has defined a
 | 
			
		||||
	 * one-shot behaviour for breakpoint exceptions.
 | 
			
		||||
	 */
 | 
			
		||||
	attr = bp->attr;
 | 
			
		||||
	attr.disabled = true;
 | 
			
		||||
	modify_user_hw_breakpoint(bp, &attr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int set_single_step(struct task_struct *tsk, unsigned long addr)
 | 
			
		||||
{
 | 
			
		||||
	struct thread_struct *thread = &tsk->thread;
 | 
			
		||||
	struct perf_event *bp;
 | 
			
		||||
	struct perf_event_attr attr;
 | 
			
		||||
 | 
			
		||||
	bp = thread->ptrace_bps[0];
 | 
			
		||||
	if (!bp) {
 | 
			
		||||
		hw_breakpoint_init(&attr);
 | 
			
		||||
 | 
			
		||||
		attr.bp_addr = addr;
 | 
			
		||||
		attr.bp_len = HW_BREAKPOINT_LEN_2;
 | 
			
		||||
		attr.bp_type = HW_BREAKPOINT_R;
 | 
			
		||||
 | 
			
		||||
		bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
 | 
			
		||||
		if (IS_ERR(bp))
 | 
			
		||||
			return PTR_ERR(bp);
 | 
			
		||||
 | 
			
		||||
		thread->ptrace_bps[0] = bp;
 | 
			
		||||
	} else {
 | 
			
		||||
		int err;
 | 
			
		||||
 | 
			
		||||
		attr = bp->attr;
 | 
			
		||||
		attr.bp_addr = addr;
 | 
			
		||||
		err = modify_user_hw_breakpoint(bp, &attr);
 | 
			
		||||
		if (unlikely(err))
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void user_enable_single_step(struct task_struct *child)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc));
 | 
			
		||||
 | 
			
		||||
	set_tsk_thread_flag(child, TIF_SINGLESTEP);
 | 
			
		||||
 | 
			
		||||
	set_single_step(child, pc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void user_disable_single_step(struct task_struct *child)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue