forked from mirrors/linux
		
	powerpc: Hardware breakpoints rewrite to handle non DABR breakpoint registers
This is a rewrite so that we don't assume we are using the DABR throughout the code. We now use the arch_hw_breakpoint to store the breakpoint in a generic manner in the thread_struct, rather than storing the raw DABR value. The ptrace GET/SET_DEBUGREG interface currently passes the raw DABR in from userspace. We keep this functionality, so that future changes (like the POWER8 DAWR), will still fake the DABR to userspace. Signed-off-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
		
							parent
							
								
									a8190a59e7
								
							
						
					
					
						commit
						9422de3e95
					
				
					 14 changed files with 187 additions and 129 deletions
				
			
		| 
						 | 
					@ -4,6 +4,8 @@
 | 
				
			||||||
#ifndef _ASM_POWERPC_DEBUG_H
 | 
					#ifndef _ASM_POWERPC_DEBUG_H
 | 
				
			||||||
#define _ASM_POWERPC_DEBUG_H
 | 
					#define _ASM_POWERPC_DEBUG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/hw_breakpoint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct pt_regs;
 | 
					struct pt_regs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct dentry *powerpc_debugfs_root;
 | 
					extern struct dentry *powerpc_debugfs_root;
 | 
				
			||||||
| 
						 | 
					@ -15,7 +17,7 @@ extern int (*__debugger_ipi)(struct pt_regs *regs);
 | 
				
			||||||
extern int (*__debugger_bpt)(struct pt_regs *regs);
 | 
					extern int (*__debugger_bpt)(struct pt_regs *regs);
 | 
				
			||||||
extern int (*__debugger_sstep)(struct pt_regs *regs);
 | 
					extern int (*__debugger_sstep)(struct pt_regs *regs);
 | 
				
			||||||
extern int (*__debugger_iabr_match)(struct pt_regs *regs);
 | 
					extern int (*__debugger_iabr_match)(struct pt_regs *regs);
 | 
				
			||||||
extern int (*__debugger_dabr_match)(struct pt_regs *regs);
 | 
					extern int (*__debugger_break_match)(struct pt_regs *regs);
 | 
				
			||||||
extern int (*__debugger_fault_handler)(struct pt_regs *regs);
 | 
					extern int (*__debugger_fault_handler)(struct pt_regs *regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEBUGGER_BOILERPLATE(__NAME) \
 | 
					#define DEBUGGER_BOILERPLATE(__NAME) \
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,7 @@ DEBUGGER_BOILERPLATE(debugger_ipi)
 | 
				
			||||||
DEBUGGER_BOILERPLATE(debugger_bpt)
 | 
					DEBUGGER_BOILERPLATE(debugger_bpt)
 | 
				
			||||||
DEBUGGER_BOILERPLATE(debugger_sstep)
 | 
					DEBUGGER_BOILERPLATE(debugger_sstep)
 | 
				
			||||||
DEBUGGER_BOILERPLATE(debugger_iabr_match)
 | 
					DEBUGGER_BOILERPLATE(debugger_iabr_match)
 | 
				
			||||||
DEBUGGER_BOILERPLATE(debugger_dabr_match)
 | 
					DEBUGGER_BOILERPLATE(debugger_break_match)
 | 
				
			||||||
DEBUGGER_BOILERPLATE(debugger_fault_handler)
 | 
					DEBUGGER_BOILERPLATE(debugger_fault_handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
| 
						 | 
					@ -40,16 +42,17 @@ static inline int debugger_ipi(struct pt_regs *regs) { return 0; }
 | 
				
			||||||
static inline int debugger_bpt(struct pt_regs *regs) { return 0; }
 | 
					static inline int debugger_bpt(struct pt_regs *regs) { return 0; }
 | 
				
			||||||
static inline int debugger_sstep(struct pt_regs *regs) { return 0; }
 | 
					static inline int debugger_sstep(struct pt_regs *regs) { return 0; }
 | 
				
			||||||
static inline int debugger_iabr_match(struct pt_regs *regs) { return 0; }
 | 
					static inline int debugger_iabr_match(struct pt_regs *regs) { return 0; }
 | 
				
			||||||
static inline int debugger_dabr_match(struct pt_regs *regs) { return 0; }
 | 
					static inline int debugger_break_match(struct pt_regs *regs) { return 0; }
 | 
				
			||||||
static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
 | 
					static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern int set_dabr(unsigned long dabr, unsigned long dabrx);
 | 
					int set_break(struct arch_hw_breakpoint *brk);
 | 
				
			||||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
					#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
extern void do_send_trap(struct pt_regs *regs, unsigned long address,
 | 
					extern void do_send_trap(struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
			 unsigned long error_code, int signal_code, int brkpt);
 | 
								 unsigned long error_code, int signal_code, int brkpt);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
extern void do_dabr(struct pt_regs *regs, unsigned long address,
 | 
					
 | 
				
			||||||
 | 
					extern void do_break(struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
		     unsigned long error_code);
 | 
							     unsigned long error_code);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,16 +24,30 @@
 | 
				
			||||||
#define _PPC_BOOK3S_64_HW_BREAKPOINT_H
 | 
					#define _PPC_BOOK3S_64_HW_BREAKPOINT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef	__KERNEL__
 | 
					#ifdef	__KERNEL__
 | 
				
			||||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct arch_hw_breakpoint {
 | 
					struct arch_hw_breakpoint {
 | 
				
			||||||
	unsigned long	address;
 | 
						unsigned long	address;
 | 
				
			||||||
	unsigned long	dabrx;
 | 
						u16		type;
 | 
				
			||||||
	int		type;
 | 
						u16		len; /* length of the target data symbol */
 | 
				
			||||||
	u8		len; /* length of the target data symbol */
 | 
					 | 
				
			||||||
	bool		extraneous_interrupt;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Note: Don't change the the first 6 bits below as they are in the same order
 | 
				
			||||||
 | 
					 * as the dabr and dabrx.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_READ		0x01
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_WRITE		0x02
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_TRANSLATE		0x04
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_USER		0x08
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_KERNEL		0x10
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_HYP			0x20
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_EXTRANEOUS_IRQ	0x80
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* bits that overlap with the bottom 3 bits of the dabr */
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_RDWR	(HW_BRK_TYPE_READ | HW_BRK_TYPE_WRITE)
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_DABR	(HW_BRK_TYPE_RDWR | HW_BRK_TYPE_TRANSLATE)
 | 
				
			||||||
 | 
					#define HW_BRK_TYPE_PRIV_ALL	(HW_BRK_TYPE_USER | HW_BRK_TYPE_KERNEL | \
 | 
				
			||||||
 | 
									 HW_BRK_TYPE_HYP)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_HAVE_HW_BREAKPOINT
 | 
				
			||||||
#include <linux/kdebug.h>
 | 
					#include <linux/kdebug.h>
 | 
				
			||||||
#include <asm/reg.h>
 | 
					#include <asm/reg.h>
 | 
				
			||||||
#include <asm/debug.h>
 | 
					#include <asm/debug.h>
 | 
				
			||||||
| 
						 | 
					@ -62,7 +76,12 @@ extern void ptrace_triggered(struct perf_event *bp,
 | 
				
			||||||
			struct perf_sample_data *data, struct pt_regs *regs);
 | 
								struct perf_sample_data *data, struct pt_regs *regs);
 | 
				
			||||||
static inline void hw_breakpoint_disable(void)
 | 
					static inline void hw_breakpoint_disable(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	set_dabr(0, 0);
 | 
						struct arch_hw_breakpoint brk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						brk.address = 0;
 | 
				
			||||||
 | 
						brk.type = 0;
 | 
				
			||||||
 | 
						brk.len = 0;
 | 
				
			||||||
 | 
						set_break(&brk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
 | 
					extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@
 | 
				
			||||||
#include <linux/cache.h>
 | 
					#include <linux/cache.h>
 | 
				
			||||||
#include <asm/ptrace.h>
 | 
					#include <asm/ptrace.h>
 | 
				
			||||||
#include <asm/types.h>
 | 
					#include <asm/types.h>
 | 
				
			||||||
 | 
					#include <asm/hw_breakpoint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* We do _not_ want to define new machine types at all, those must die
 | 
					/* We do _not_ want to define new machine types at all, those must die
 | 
				
			||||||
 * in favor of using the device-tree
 | 
					 * in favor of using the device-tree
 | 
				
			||||||
| 
						 | 
					@ -225,8 +226,7 @@ struct thread_struct {
 | 
				
			||||||
	struct perf_event *last_hit_ubp;
 | 
						struct perf_event *last_hit_ubp;
 | 
				
			||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	unsigned long	dabr;		/* Data address breakpoint register */
 | 
						struct arch_hw_breakpoint hw_brk; /* info on the hardware breakpoint */
 | 
				
			||||||
	unsigned long	dabrx;		/*      ... extension  */
 | 
					 | 
				
			||||||
	unsigned long	trap_nr;	/* last trap # on this thread */
 | 
						unsigned long	trap_nr;	/* last trap # on this thread */
 | 
				
			||||||
#ifdef CONFIG_ALTIVEC
 | 
					#ifdef CONFIG_ALTIVEC
 | 
				
			||||||
	/* Complete AltiVec register set */
 | 
						/* Complete AltiVec register set */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -206,9 +206,6 @@
 | 
				
			||||||
#define   DAWRX_KERNEL	(1UL << 1)
 | 
					#define   DAWRX_KERNEL	(1UL << 1)
 | 
				
			||||||
#define   DAWRX_HYP	(1UL << 2)
 | 
					#define   DAWRX_HYP	(1UL << 2)
 | 
				
			||||||
#define SPRN_DABR	0x3F5	/* Data Address Breakpoint Register */
 | 
					#define SPRN_DABR	0x3F5	/* Data Address Breakpoint Register */
 | 
				
			||||||
#define   DABR_TRANSLATION	(1UL << 2)
 | 
					 | 
				
			||||||
#define   DABR_DATA_WRITE	(1UL << 1)
 | 
					 | 
				
			||||||
#define   DABR_DATA_READ	(1UL << 0)
 | 
					 | 
				
			||||||
#define SPRN_DABR2	0x13D	/* e300 */
 | 
					#define SPRN_DABR2	0x13D	/* e300 */
 | 
				
			||||||
#define SPRN_DABRX	0x3F7	/* Data Address Breakpoint Register Extension */
 | 
					#define SPRN_DABRX	0x3F7	/* Data Address Breakpoint Register Extension */
 | 
				
			||||||
#define   DABRX_USER	(1UL << 0)
 | 
					#define   DABRX_USER	(1UL << 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1251,7 +1251,7 @@ handle_dabr_fault:
 | 
				
			||||||
	ld      r4,_DAR(r1)
 | 
						ld      r4,_DAR(r1)
 | 
				
			||||||
	ld      r5,_DSISR(r1)
 | 
						ld      r5,_DSISR(r1)
 | 
				
			||||||
	addi    r3,r1,STACK_FRAME_OVERHEAD
 | 
						addi    r3,r1,STACK_FRAME_OVERHEAD
 | 
				
			||||||
	bl      .do_dabr
 | 
						bl      .do_break
 | 
				
			||||||
12:	b       .ret_from_except_lite
 | 
					12:	b       .ret_from_except_lite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 so, DABR will be populated in single_step_dabr_instruction().
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (current->thread.last_hit_ubp != bp)
 | 
						if (current->thread.last_hit_ubp != bp)
 | 
				
			||||||
		set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx);
 | 
							set_break(info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*slot = NULL;
 | 
						*slot = NULL;
 | 
				
			||||||
	set_dabr(0, 0);
 | 
						hw_breakpoint_disable();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -127,19 +127,13 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int arch_bp_generic_fields(int type, int *gen_bp_type)
 | 
					int arch_bp_generic_fields(int type, int *gen_bp_type)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (type) {
 | 
						*gen_bp_type = 0;
 | 
				
			||||||
	case DABR_DATA_READ:
 | 
						if (type & HW_BRK_TYPE_READ)
 | 
				
			||||||
		*gen_bp_type = HW_BREAKPOINT_R;
 | 
							*gen_bp_type |= HW_BREAKPOINT_R;
 | 
				
			||||||
		break;
 | 
						if (type & HW_BRK_TYPE_WRITE)
 | 
				
			||||||
	case DABR_DATA_WRITE:
 | 
							*gen_bp_type |= HW_BREAKPOINT_W;
 | 
				
			||||||
		*gen_bp_type = HW_BREAKPOINT_W;
 | 
						if (*gen_bp_type == 0)
 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case (DABR_DATA_WRITE | DABR_DATA_READ):
 | 
					 | 
				
			||||||
		*gen_bp_type = (HW_BREAKPOINT_W | HW_BREAKPOINT_R);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -154,29 +148,22 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
 | 
				
			||||||
	if (!bp)
 | 
						if (!bp)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (bp->attr.bp_type) {
 | 
						info->type = HW_BRK_TYPE_TRANSLATE;
 | 
				
			||||||
	case HW_BREAKPOINT_R:
 | 
						if (bp->attr.bp_type & HW_BREAKPOINT_R)
 | 
				
			||||||
		info->type = DABR_DATA_READ;
 | 
							info->type |= HW_BRK_TYPE_READ;
 | 
				
			||||||
		break;
 | 
						if (bp->attr.bp_type & HW_BREAKPOINT_W)
 | 
				
			||||||
	case HW_BREAKPOINT_W:
 | 
							info->type |= HW_BRK_TYPE_WRITE;
 | 
				
			||||||
		info->type = DABR_DATA_WRITE;
 | 
						if (info->type == HW_BRK_TYPE_TRANSLATE)
 | 
				
			||||||
		break;
 | 
							/* must set alteast read or write */
 | 
				
			||||||
	case HW_BREAKPOINT_R | HW_BREAKPOINT_W:
 | 
					 | 
				
			||||||
		info->type = (DABR_DATA_READ | DABR_DATA_WRITE);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return ret;
 | 
							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->address = bp->attr.bp_addr;
 | 
				
			||||||
	info->len = bp->attr.bp_len;
 | 
						info->len = bp->attr.bp_len;
 | 
				
			||||||
	info->dabrx = DABRX_ALL;
 | 
					 | 
				
			||||||
	if (bp->attr.exclude_user)
 | 
					 | 
				
			||||||
		info->dabrx &= ~DABRX_USER;
 | 
					 | 
				
			||||||
	if (bp->attr.exclude_kernel)
 | 
					 | 
				
			||||||
		info->dabrx &= ~DABRX_KERNEL;
 | 
					 | 
				
			||||||
	if (bp->attr.exclude_hv)
 | 
					 | 
				
			||||||
		info->dabrx &= ~DABRX_HYP;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Since breakpoint length can be a maximum of HW_BREAKPOINT_LEN(8)
 | 
						 * Since breakpoint length can be a maximum of HW_BREAKPOINT_LEN(8)
 | 
				
			||||||
| 
						 | 
					@ -204,7 +191,7 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	info = counter_arch_bp(tsk->thread.last_hit_ubp);
 | 
						info = counter_arch_bp(tsk->thread.last_hit_ubp);
 | 
				
			||||||
	regs->msr &= ~MSR_SE;
 | 
						regs->msr &= ~MSR_SE;
 | 
				
			||||||
	set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx);
 | 
						set_break(info);
 | 
				
			||||||
	tsk->thread.last_hit_ubp = NULL;
 | 
						tsk->thread.last_hit_ubp = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,7 +209,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
 | 
				
			||||||
	unsigned long dar = regs->dar;
 | 
						unsigned long dar = regs->dar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Disable breakpoints during exception handling */
 | 
						/* Disable breakpoints during exception handling */
 | 
				
			||||||
	set_dabr(0, 0);
 | 
						hw_breakpoint_disable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * The counter may be concurrently released but that can only
 | 
						 * The counter may be concurrently released but that can only
 | 
				
			||||||
| 
						 | 
					@ -255,8 +242,9 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
 | 
				
			||||||
	 * we still need to single-step the instruction, but we don't
 | 
						 * we still need to single-step the instruction, but we don't
 | 
				
			||||||
	 * generate an event.
 | 
						 * generate an event.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	info->extraneous_interrupt = !((bp->attr.bp_addr <= dar) &&
 | 
						if (!((bp->attr.bp_addr <= dar) &&
 | 
				
			||||||
			(dar - bp->attr.bp_addr < bp->attr.bp_len));
 | 
						      (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 */
 | 
						/* Do not emulate user-space instructions, instead single-step them */
 | 
				
			||||||
	if (user_mode(regs)) {
 | 
						if (user_mode(regs)) {
 | 
				
			||||||
| 
						 | 
					@ -285,10 +273,10 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
 | 
				
			||||||
	 * As a policy, the callback is invoked in a 'trigger-after-execute'
 | 
						 * As a policy, the callback is invoked in a 'trigger-after-execute'
 | 
				
			||||||
	 * fashion
 | 
						 * fashion
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!info->extraneous_interrupt)
 | 
						if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ))
 | 
				
			||||||
		perf_bp_event(bp, regs);
 | 
							perf_bp_event(bp, regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx);
 | 
						set_break(info);
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	rcu_read_unlock();
 | 
						rcu_read_unlock();
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
| 
						 | 
					@ -317,10 +305,10 @@ int __kprobes single_step_dabr_instruction(struct die_args *args)
 | 
				
			||||||
	 * We shall invoke the user-defined callback function in the single
 | 
						 * We shall invoke the user-defined callback function in the single
 | 
				
			||||||
	 * stepping handler to confirm to 'trigger-after-execute' semantics
 | 
						 * stepping handler to confirm to 'trigger-after-execute' semantics
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!info->extraneous_interrupt)
 | 
						if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ))
 | 
				
			||||||
		perf_bp_event(bp, regs);
 | 
							perf_bp_event(bp, regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx);
 | 
						set_break(info);
 | 
				
			||||||
	current->thread.last_hit_ubp = NULL;
 | 
						current->thread.last_hit_ubp = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,7 +198,7 @@ static int kgdb_iabr_match(struct pt_regs *regs)
 | 
				
			||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int kgdb_dabr_match(struct pt_regs *regs)
 | 
					static int kgdb_break_match(struct pt_regs *regs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (user_mode(regs))
 | 
						if (user_mode(regs))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -458,7 +458,7 @@ static void *old__debugger;
 | 
				
			||||||
static void *old__debugger_bpt;
 | 
					static void *old__debugger_bpt;
 | 
				
			||||||
static void *old__debugger_sstep;
 | 
					static void *old__debugger_sstep;
 | 
				
			||||||
static void *old__debugger_iabr_match;
 | 
					static void *old__debugger_iabr_match;
 | 
				
			||||||
static void *old__debugger_dabr_match;
 | 
					static void *old__debugger_break_match;
 | 
				
			||||||
static void *old__debugger_fault_handler;
 | 
					static void *old__debugger_fault_handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int kgdb_arch_init(void)
 | 
					int kgdb_arch_init(void)
 | 
				
			||||||
| 
						 | 
					@ -468,7 +468,7 @@ int kgdb_arch_init(void)
 | 
				
			||||||
	old__debugger_bpt = __debugger_bpt;
 | 
						old__debugger_bpt = __debugger_bpt;
 | 
				
			||||||
	old__debugger_sstep = __debugger_sstep;
 | 
						old__debugger_sstep = __debugger_sstep;
 | 
				
			||||||
	old__debugger_iabr_match = __debugger_iabr_match;
 | 
						old__debugger_iabr_match = __debugger_iabr_match;
 | 
				
			||||||
	old__debugger_dabr_match = __debugger_dabr_match;
 | 
						old__debugger_break_match = __debugger_break_match;
 | 
				
			||||||
	old__debugger_fault_handler = __debugger_fault_handler;
 | 
						old__debugger_fault_handler = __debugger_fault_handler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__debugger_ipi = kgdb_call_nmi_hook;
 | 
						__debugger_ipi = kgdb_call_nmi_hook;
 | 
				
			||||||
| 
						 | 
					@ -476,7 +476,7 @@ int kgdb_arch_init(void)
 | 
				
			||||||
	__debugger_bpt = kgdb_handle_breakpoint;
 | 
						__debugger_bpt = kgdb_handle_breakpoint;
 | 
				
			||||||
	__debugger_sstep = kgdb_singlestep;
 | 
						__debugger_sstep = kgdb_singlestep;
 | 
				
			||||||
	__debugger_iabr_match = kgdb_iabr_match;
 | 
						__debugger_iabr_match = kgdb_iabr_match;
 | 
				
			||||||
	__debugger_dabr_match = kgdb_dabr_match;
 | 
						__debugger_break_match = kgdb_break_match;
 | 
				
			||||||
	__debugger_fault_handler = kgdb_not_implemented;
 | 
						__debugger_fault_handler = kgdb_not_implemented;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -489,6 +489,6 @@ void kgdb_arch_exit(void)
 | 
				
			||||||
	__debugger_bpt = old__debugger_bpt;
 | 
						__debugger_bpt = old__debugger_bpt;
 | 
				
			||||||
	__debugger_sstep = old__debugger_sstep;
 | 
						__debugger_sstep = old__debugger_sstep;
 | 
				
			||||||
	__debugger_iabr_match = old__debugger_iabr_match;
 | 
						__debugger_iabr_match = old__debugger_iabr_match;
 | 
				
			||||||
	__debugger_dabr_match = old__debugger_dabr_match;
 | 
						__debugger_breakx_match = old__debugger_break_match;
 | 
				
			||||||
	__debugger_fault_handler = old__debugger_fault_handler;
 | 
						__debugger_fault_handler = old__debugger_fault_handler;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -271,7 +271,7 @@ void do_send_trap(struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
	force_sig_info(SIGTRAP, &info, current);
 | 
						force_sig_info(SIGTRAP, &info, current);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#else	/* !CONFIG_PPC_ADV_DEBUG_REGS */
 | 
					#else	/* !CONFIG_PPC_ADV_DEBUG_REGS */
 | 
				
			||||||
void do_dabr(struct pt_regs *regs, unsigned long address,
 | 
					void do_break (struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
		    unsigned long error_code)
 | 
							    unsigned long error_code)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	siginfo_t info;
 | 
						siginfo_t info;
 | 
				
			||||||
| 
						 | 
					@ -281,11 +281,11 @@ void do_dabr(struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
			11, SIGSEGV) == NOTIFY_STOP)
 | 
								11, SIGSEGV) == NOTIFY_STOP)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (debugger_dabr_match(regs))
 | 
						if (debugger_break_match(regs))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Clear the DABR */
 | 
						/* Clear the breakpoint */
 | 
				
			||||||
	set_dabr(0, 0);
 | 
						hw_breakpoint_disable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Deliver the signal to userspace */
 | 
						/* Deliver the signal to userspace */
 | 
				
			||||||
	info.si_signo = SIGTRAP;
 | 
						info.si_signo = SIGTRAP;
 | 
				
			||||||
| 
						 | 
					@ -296,7 +296,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif	/* CONFIG_PPC_ADV_DEBUG_REGS */
 | 
					#endif	/* CONFIG_PPC_ADV_DEBUG_REGS */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static DEFINE_PER_CPU(unsigned long, current_dabr);
 | 
					static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
					#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -364,39 +364,72 @@ static void switch_booke_debug_regs(struct thread_struct *new_thread)
 | 
				
			||||||
#ifndef CONFIG_HAVE_HW_BREAKPOINT
 | 
					#ifndef CONFIG_HAVE_HW_BREAKPOINT
 | 
				
			||||||
static void set_debug_reg_defaults(struct thread_struct *thread)
 | 
					static void set_debug_reg_defaults(struct thread_struct *thread)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (thread->dabr) {
 | 
						thread->hw_brk.address = 0;
 | 
				
			||||||
		thread->dabr = 0;
 | 
						thread->hw_brk.type = 0;
 | 
				
			||||||
		thread->dabrx = 0;
 | 
						set_break(&thread->hw_brk);
 | 
				
			||||||
		set_dabr(0, 0);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif /* !CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* !CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
#endif	/* CONFIG_PPC_ADV_DEBUG_REGS */
 | 
					#endif	/* CONFIG_PPC_ADV_DEBUG_REGS */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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, dabrx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* XXX should we have a CPU_FTR_HAS_DABR ? */
 | 
					 | 
				
			||||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
					#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
 | 
					static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
	mtspr(SPRN_DAC1, dabr);
 | 
						mtspr(SPRN_DAC1, dabr);
 | 
				
			||||||
#ifdef CONFIG_PPC_47x
 | 
					#ifdef CONFIG_PPC_47x
 | 
				
			||||||
	isync();
 | 
						isync();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#elif defined(CONFIG_PPC_BOOK3S)
 | 
					#elif defined(CONFIG_PPC_BOOK3S)
 | 
				
			||||||
 | 
					static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
	mtspr(SPRN_DABR, dabr);
 | 
						mtspr(SPRN_DABR, dabr);
 | 
				
			||||||
	mtspr(SPRN_DABRX, dabrx);
 | 
						mtspr(SPRN_DABRX, dabrx);
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					static inline int __set_dabr(unsigned long dabr, unsigned long dabrx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -EINVAL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int set_dabr(struct arch_hw_breakpoint *brk)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long dabr, dabrx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dabr = brk->address | (brk->type & HW_BRK_TYPE_DABR);
 | 
				
			||||||
 | 
						dabrx = ((brk->type >> 3) & 0x7);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ppc_md.set_dabr)
 | 
				
			||||||
 | 
							return ppc_md.set_dabr(dabr, dabrx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return __set_dabr(dabr, dabrx);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int set_break(struct arch_hw_breakpoint *brk)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__get_cpu_var(current_brk) = *brk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return set_dabr(brk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PPC64
 | 
					#ifdef CONFIG_PPC64
 | 
				
			||||||
DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
 | 
					DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool hw_brk_match(struct arch_hw_breakpoint *a,
 | 
				
			||||||
 | 
								      struct arch_hw_breakpoint *b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (a->address != b->address)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (a->type != b->type)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (a->len != b->len)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct task_struct *__switch_to(struct task_struct *prev,
 | 
					struct task_struct *__switch_to(struct task_struct *prev,
 | 
				
			||||||
	struct task_struct *new)
 | 
						struct task_struct *new)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -481,8 +514,8 @@ struct task_struct *__switch_to(struct task_struct *prev,
 | 
				
			||||||
 * schedule DABR
 | 
					 * schedule DABR
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#ifndef CONFIG_HAVE_HW_BREAKPOINT
 | 
					#ifndef CONFIG_HAVE_HW_BREAKPOINT
 | 
				
			||||||
	if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr))
 | 
						if (unlikely(hw_brk_match(&__get_cpu_var(current_brk), &new->thread.hw_brk)))
 | 
				
			||||||
		set_dabr(new->thread.dabr, new->thread.dabrx);
 | 
							set_break(&new->thread.hw_brk);
 | 
				
			||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -905,6 +905,9 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 | 
				
			||||||
	struct perf_event *bp;
 | 
						struct perf_event *bp;
 | 
				
			||||||
	struct perf_event_attr attr;
 | 
						struct perf_event_attr attr;
 | 
				
			||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
 | 
					#ifndef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
 | 
						struct arch_hw_breakpoint hw_brk;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* For ppc64 we support one DABR and no IABR's at the moment (ppc64).
 | 
						/* For ppc64 we support one DABR and no IABR's at the moment (ppc64).
 | 
				
			||||||
	 *  For embedded processors we support one DAC and no IAC's at the
 | 
						 *  For embedded processors we support one DAC and no IAC's at the
 | 
				
			||||||
| 
						 | 
					@ -931,14 +934,17 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Ensure breakpoint translation bit is set */
 | 
						/* Ensure breakpoint translation bit is set */
 | 
				
			||||||
	if (data && !(data & DABR_TRANSLATION))
 | 
						if (data && !(data & HW_BRK_TYPE_TRANSLATE))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						hw_brk.address = data & (~HW_BRK_TYPE_DABR);
 | 
				
			||||||
 | 
						hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
 | 
				
			||||||
 | 
						hw_brk.len = 8;
 | 
				
			||||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
 | 
					#ifdef CONFIG_HAVE_HW_BREAKPOINT
 | 
				
			||||||
	if (ptrace_get_breakpoints(task) < 0)
 | 
						if (ptrace_get_breakpoints(task) < 0)
 | 
				
			||||||
		return -ESRCH;
 | 
							return -ESRCH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bp = thread->ptrace_bps[0];
 | 
						bp = thread->ptrace_bps[0];
 | 
				
			||||||
	if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) {
 | 
						if ((!data) || !(hw_brk.type & HW_BRK_TYPE_RDWR)) {
 | 
				
			||||||
		if (bp) {
 | 
							if (bp) {
 | 
				
			||||||
			unregister_hw_breakpoint(bp);
 | 
								unregister_hw_breakpoint(bp);
 | 
				
			||||||
			thread->ptrace_bps[0] = NULL;
 | 
								thread->ptrace_bps[0] = NULL;
 | 
				
			||||||
| 
						 | 
					@ -948,10 +954,8 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (bp) {
 | 
						if (bp) {
 | 
				
			||||||
		attr = bp->attr;
 | 
							attr = bp->attr;
 | 
				
			||||||
		attr.bp_addr = data & ~HW_BREAKPOINT_ALIGN;
 | 
							attr.bp_addr = hw_brk.address;
 | 
				
			||||||
		arch_bp_generic_fields(data &
 | 
							arch_bp_generic_fields(hw_brk.type, &attr.bp_type);
 | 
				
			||||||
					(DABR_DATA_WRITE | DABR_DATA_READ),
 | 
					 | 
				
			||||||
							&attr.bp_type);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Enable breakpoint */
 | 
							/* Enable breakpoint */
 | 
				
			||||||
		attr.disabled = false;
 | 
							attr.disabled = false;
 | 
				
			||||||
| 
						 | 
					@ -963,15 +967,14 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		thread->ptrace_bps[0] = bp;
 | 
							thread->ptrace_bps[0] = bp;
 | 
				
			||||||
		ptrace_put_breakpoints(task);
 | 
							ptrace_put_breakpoints(task);
 | 
				
			||||||
		thread->dabr = data;
 | 
							thread->hw_brk = hw_brk;
 | 
				
			||||||
		thread->dabrx = DABRX_ALL;
 | 
					 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Create a new breakpoint request if one doesn't exist already */
 | 
						/* Create a new breakpoint request if one doesn't exist already */
 | 
				
			||||||
	hw_breakpoint_init(&attr);
 | 
						hw_breakpoint_init(&attr);
 | 
				
			||||||
	attr.bp_addr = data & ~HW_BREAKPOINT_ALIGN;
 | 
						attr.bp_addr = hw_brk.address;
 | 
				
			||||||
	arch_bp_generic_fields(data & (DABR_DATA_WRITE | DABR_DATA_READ),
 | 
						arch_bp_generic_fields(hw_brk.type,
 | 
				
			||||||
			       &attr.bp_type);
 | 
								       &attr.bp_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
 | 
						thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
 | 
				
			||||||
| 
						 | 
					@ -985,10 +988,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
 | 
				
			||||||
	ptrace_put_breakpoints(task);
 | 
						ptrace_put_breakpoints(task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
 | 
						task->thread.hw_brk = hw_brk;
 | 
				
			||||||
	/* Move contents to the DABR register */
 | 
					 | 
				
			||||||
	task->thread.dabr = data;
 | 
					 | 
				
			||||||
	task->thread.dabrx = DABRX_ALL;
 | 
					 | 
				
			||||||
#else /* CONFIG_PPC_ADV_DEBUG_REGS */
 | 
					#else /* CONFIG_PPC_ADV_DEBUG_REGS */
 | 
				
			||||||
	/* As described above, it was assumed 3 bits were passed with the data
 | 
						/* 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
 | 
						 *  address, but we will assume only the mode bits will be passed
 | 
				
			||||||
| 
						 | 
					@ -1349,7 +1349,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
 | 
				
			||||||
	struct perf_event_attr attr;
 | 
						struct perf_event_attr attr;
 | 
				
			||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
#ifndef CONFIG_PPC_ADV_DEBUG_REGS
 | 
					#ifndef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
	unsigned long dabr;
 | 
						struct arch_hw_breakpoint brk;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (bp_info->version != 1)
 | 
						if (bp_info->version != 1)
 | 
				
			||||||
| 
						 | 
					@ -1397,12 +1397,12 @@ static long ppc_set_hwdebug(struct task_struct *child,
 | 
				
			||||||
	if ((unsigned long)bp_info->addr >= TASK_SIZE)
 | 
						if ((unsigned long)bp_info->addr >= TASK_SIZE)
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dabr = (unsigned long)bp_info->addr & ~7UL;
 | 
						brk.address = bp_info->addr & ~7UL;
 | 
				
			||||||
	dabr |= DABR_TRANSLATION;
 | 
						brk.type = HW_BRK_TYPE_TRANSLATE;
 | 
				
			||||||
	if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
 | 
						if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
 | 
				
			||||||
		dabr |= DABR_DATA_READ;
 | 
							brk.type |= HW_BRK_TYPE_READ;
 | 
				
			||||||
	if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
 | 
						if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
 | 
				
			||||||
		dabr |= DABR_DATA_WRITE;
 | 
							brk.type |= HW_BRK_TYPE_WRITE;
 | 
				
			||||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
 | 
					#ifdef CONFIG_HAVE_HW_BREAKPOINT
 | 
				
			||||||
	if (ptrace_get_breakpoints(child) < 0)
 | 
						if (ptrace_get_breakpoints(child) < 0)
 | 
				
			||||||
		return -ESRCH;
 | 
							return -ESRCH;
 | 
				
			||||||
| 
						 | 
					@ -1427,8 +1427,7 @@ static long ppc_set_hwdebug(struct task_struct *child,
 | 
				
			||||||
	hw_breakpoint_init(&attr);
 | 
						hw_breakpoint_init(&attr);
 | 
				
			||||||
	attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN;
 | 
						attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN;
 | 
				
			||||||
	attr.bp_len = len;
 | 
						attr.bp_len = len;
 | 
				
			||||||
	arch_bp_generic_fields(dabr & (DABR_DATA_WRITE | DABR_DATA_READ),
 | 
						arch_bp_generic_fields(brk.type, &attr.bp_type);
 | 
				
			||||||
								&attr.bp_type);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
 | 
						thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
 | 
				
			||||||
					       ptrace_triggered, NULL, child);
 | 
										       ptrace_triggered, NULL, child);
 | 
				
			||||||
| 
						 | 
					@ -1445,11 +1444,10 @@ static long ppc_set_hwdebug(struct task_struct *child,
 | 
				
			||||||
	if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
 | 
						if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (child->thread.dabr)
 | 
						if (child->thread.hw_brk.address)
 | 
				
			||||||
		return -ENOSPC;
 | 
							return -ENOSPC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	child->thread.dabr = dabr;
 | 
						child->thread.hw_brk = brk;
 | 
				
			||||||
	child->thread.dabrx = DABRX_ALL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
#endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
 | 
					#endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */
 | 
				
			||||||
| 
						 | 
					@ -1495,10 +1493,11 @@ static long ppc_del_hwdebug(struct task_struct *child, long data)
 | 
				
			||||||
	ptrace_put_breakpoints(child);
 | 
						ptrace_put_breakpoints(child);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
#else /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#else /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
	if (child->thread.dabr == 0)
 | 
						if (child->thread.hw_brk.address == 0)
 | 
				
			||||||
		return -ENOENT;
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	child->thread.dabr = 0;
 | 
						child->thread.hw_brk.address = 0;
 | 
				
			||||||
 | 
						child->thread.hw_brk.type = 0;
 | 
				
			||||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
					#endif /* CONFIG_HAVE_HW_BREAKPOINT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -1642,6 +1641,9 @@ long arch_ptrace(struct task_struct *child, long request,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case PTRACE_GET_DEBUGREG: {
 | 
						case PTRACE_GET_DEBUGREG: {
 | 
				
			||||||
 | 
					#ifndef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
 | 
							unsigned long dabr_fake;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		ret = -EINVAL;
 | 
							ret = -EINVAL;
 | 
				
			||||||
		/* We only support one DABR and no IABRS at the moment */
 | 
							/* We only support one DABR and no IABRS at the moment */
 | 
				
			||||||
		if (addr > 0)
 | 
							if (addr > 0)
 | 
				
			||||||
| 
						 | 
					@ -1649,7 +1651,9 @@ long arch_ptrace(struct task_struct *child, long request,
 | 
				
			||||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
					#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
		ret = put_user(child->thread.dac1, datalp);
 | 
							ret = put_user(child->thread.dac1, datalp);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
		ret = put_user(child->thread.dabr, datalp);
 | 
							dabr_fake = ((child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) |
 | 
				
			||||||
 | 
								     (child->thread.hw_brk.type & HW_BRK_TYPE_DABR));
 | 
				
			||||||
 | 
							ret = put_user(dabr_fake, datalp);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -252,6 +252,9 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case PTRACE_GET_DEBUGREG: {
 | 
						case PTRACE_GET_DEBUGREG: {
 | 
				
			||||||
 | 
					#ifndef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
 | 
							unsigned long dabr_fake;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		ret = -EINVAL;
 | 
							ret = -EINVAL;
 | 
				
			||||||
		/* We only support one DABR and no IABRS at the moment */
 | 
							/* We only support one DABR and no IABRS at the moment */
 | 
				
			||||||
		if (addr > 0)
 | 
							if (addr > 0)
 | 
				
			||||||
| 
						 | 
					@ -259,7 +262,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 | 
				
			||||||
#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
					#ifdef CONFIG_PPC_ADV_DEBUG_REGS
 | 
				
			||||||
		ret = put_user(child->thread.dac1, (u32 __user *)data);
 | 
							ret = put_user(child->thread.dac1, (u32 __user *)data);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
		ret = put_user(child->thread.dabr, (u32 __user *)data);
 | 
							dabr_fake = (
 | 
				
			||||||
 | 
								(child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) |
 | 
				
			||||||
 | 
								(child->thread.hw_brk.type & HW_BRK_TYPE_DABR));
 | 
				
			||||||
 | 
							ret = put_user(dabr_fake, (u32 __user *)data);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,8 +130,9 @@ static int do_signal(struct pt_regs *regs)
 | 
				
			||||||
	 * user space. The DABR will have been cleared if it
 | 
						 * user space. The DABR will have been cleared if it
 | 
				
			||||||
	 * triggered inside the kernel.
 | 
						 * triggered inside the kernel.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (current->thread.dabr)
 | 
						if (current->thread.hw_brk.address &&
 | 
				
			||||||
		set_dabr(current->thread.dabr, current->thread.dabrx);
 | 
							current->thread.hw_brk.type)
 | 
				
			||||||
 | 
							set_break(¤t->thread.hw_brk);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	/* Re-enable the breakpoints for the signal stack */
 | 
						/* Re-enable the breakpoints for the signal stack */
 | 
				
			||||||
	thread_change_pc(current, regs);
 | 
						thread_change_pc(current, regs);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,7 +66,7 @@ int (*__debugger_ipi)(struct pt_regs *regs) __read_mostly;
 | 
				
			||||||
int (*__debugger_bpt)(struct pt_regs *regs) __read_mostly;
 | 
					int (*__debugger_bpt)(struct pt_regs *regs) __read_mostly;
 | 
				
			||||||
int (*__debugger_sstep)(struct pt_regs *regs) __read_mostly;
 | 
					int (*__debugger_sstep)(struct pt_regs *regs) __read_mostly;
 | 
				
			||||||
int (*__debugger_iabr_match)(struct pt_regs *regs) __read_mostly;
 | 
					int (*__debugger_iabr_match)(struct pt_regs *regs) __read_mostly;
 | 
				
			||||||
int (*__debugger_dabr_match)(struct pt_regs *regs) __read_mostly;
 | 
					int (*__debugger_break_match)(struct pt_regs *regs) __read_mostly;
 | 
				
			||||||
int (*__debugger_fault_handler)(struct pt_regs *regs) __read_mostly;
 | 
					int (*__debugger_fault_handler)(struct pt_regs *regs) __read_mostly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPORT_SYMBOL(__debugger);
 | 
					EXPORT_SYMBOL(__debugger);
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,7 @@ EXPORT_SYMBOL(__debugger_ipi);
 | 
				
			||||||
EXPORT_SYMBOL(__debugger_bpt);
 | 
					EXPORT_SYMBOL(__debugger_bpt);
 | 
				
			||||||
EXPORT_SYMBOL(__debugger_sstep);
 | 
					EXPORT_SYMBOL(__debugger_sstep);
 | 
				
			||||||
EXPORT_SYMBOL(__debugger_iabr_match);
 | 
					EXPORT_SYMBOL(__debugger_iabr_match);
 | 
				
			||||||
EXPORT_SYMBOL(__debugger_dabr_match);
 | 
					EXPORT_SYMBOL(__debugger_break_match);
 | 
				
			||||||
EXPORT_SYMBOL(__debugger_fault_handler);
 | 
					EXPORT_SYMBOL(__debugger_fault_handler);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -249,8 +249,8 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
 | 
				
			||||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \
 | 
					#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \
 | 
				
			||||||
			     defined(CONFIG_PPC_BOOK3S_64))
 | 
								     defined(CONFIG_PPC_BOOK3S_64))
 | 
				
			||||||
  	if (error_code & DSISR_DABRMATCH) {
 | 
					  	if (error_code & DSISR_DABRMATCH) {
 | 
				
			||||||
		/* DABR match */
 | 
							/* breakpoint match */
 | 
				
			||||||
		do_dabr(regs, address, error_code);
 | 
							do_break(regs, address, error_code);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@
 | 
				
			||||||
#include <asm/setjmp.h>
 | 
					#include <asm/setjmp.h>
 | 
				
			||||||
#include <asm/reg.h>
 | 
					#include <asm/reg.h>
 | 
				
			||||||
#include <asm/debug.h>
 | 
					#include <asm/debug.h>
 | 
				
			||||||
 | 
					#include <asm/hw_breakpoint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PPC64
 | 
					#ifdef CONFIG_PPC64
 | 
				
			||||||
#include <asm/hvcall.h>
 | 
					#include <asm/hvcall.h>
 | 
				
			||||||
| 
						 | 
					@ -607,7 +608,7 @@ static int xmon_sstep(struct pt_regs *regs)
 | 
				
			||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int xmon_dabr_match(struct pt_regs *regs)
 | 
					static int xmon_break_match(struct pt_regs *regs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
 | 
						if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -740,8 +741,14 @@ static void insert_bpts(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void insert_cpu_bpts(void)
 | 
					static void insert_cpu_bpts(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (dabr.enabled)
 | 
						struct arch_hw_breakpoint brk;
 | 
				
			||||||
		set_dabr(dabr.address | (dabr.enabled & 7), DABRX_ALL);
 | 
					
 | 
				
			||||||
 | 
						if (dabr.enabled) {
 | 
				
			||||||
 | 
							brk.address = dabr.address;
 | 
				
			||||||
 | 
							brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
 | 
				
			||||||
 | 
							brk.len = 8;
 | 
				
			||||||
 | 
							set_break(&brk);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (iabr && cpu_has_feature(CPU_FTR_IABR))
 | 
						if (iabr && cpu_has_feature(CPU_FTR_IABR))
 | 
				
			||||||
		mtspr(SPRN_IABR, iabr->address
 | 
							mtspr(SPRN_IABR, iabr->address
 | 
				
			||||||
			 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
 | 
								 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
 | 
				
			||||||
| 
						 | 
					@ -769,7 +776,7 @@ static void remove_bpts(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void remove_cpu_bpts(void)
 | 
					static void remove_cpu_bpts(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	set_dabr(0, 0);
 | 
						hw_breakpoint_disable();
 | 
				
			||||||
	if (cpu_has_feature(CPU_FTR_IABR))
 | 
						if (cpu_has_feature(CPU_FTR_IABR))
 | 
				
			||||||
		mtspr(SPRN_IABR, 0);
 | 
							mtspr(SPRN_IABR, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1138,7 +1145,7 @@ bpt_cmds(void)
 | 
				
			||||||
				printf(badaddr);
 | 
									printf(badaddr);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			dabr.address &= ~7;
 | 
								dabr.address &= ~HW_BRK_TYPE_DABR;
 | 
				
			||||||
			dabr.enabled = mode | BP_DABR;
 | 
								dabr.enabled = mode | BP_DABR;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -2917,7 +2924,7 @@ static void xmon_init(int enable)
 | 
				
			||||||
		__debugger_bpt = xmon_bpt;
 | 
							__debugger_bpt = xmon_bpt;
 | 
				
			||||||
		__debugger_sstep = xmon_sstep;
 | 
							__debugger_sstep = xmon_sstep;
 | 
				
			||||||
		__debugger_iabr_match = xmon_iabr_match;
 | 
							__debugger_iabr_match = xmon_iabr_match;
 | 
				
			||||||
		__debugger_dabr_match = xmon_dabr_match;
 | 
							__debugger_break_match = xmon_break_match;
 | 
				
			||||||
		__debugger_fault_handler = xmon_fault_handler;
 | 
							__debugger_fault_handler = xmon_fault_handler;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		__debugger = NULL;
 | 
							__debugger = NULL;
 | 
				
			||||||
| 
						 | 
					@ -2925,7 +2932,7 @@ static void xmon_init(int enable)
 | 
				
			||||||
		__debugger_bpt = NULL;
 | 
							__debugger_bpt = NULL;
 | 
				
			||||||
		__debugger_sstep = NULL;
 | 
							__debugger_sstep = NULL;
 | 
				
			||||||
		__debugger_iabr_match = NULL;
 | 
							__debugger_iabr_match = NULL;
 | 
				
			||||||
		__debugger_dabr_match = NULL;
 | 
							__debugger_break_match = NULL;
 | 
				
			||||||
		__debugger_fault_handler = NULL;
 | 
							__debugger_fault_handler = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue