Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9422de3e authored by Michael Neuling's avatar Michael Neuling Committed by Benjamin Herrenschmidt
Browse files

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: default avatarMichael Neuling <mikey@neuling.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent a8190a59
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -4,6 +4,8 @@
#ifndef _ASM_POWERPC_DEBUG_H
#define _ASM_POWERPC_DEBUG_H

#include <asm/hw_breakpoint.h>

struct pt_regs;

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_sstep)(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);

#define DEBUGGER_BOILERPLATE(__NAME) \
@@ -31,7 +33,7 @@ DEBUGGER_BOILERPLATE(debugger_ipi)
DEBUGGER_BOILERPLATE(debugger_bpt)
DEBUGGER_BOILERPLATE(debugger_sstep)
DEBUGGER_BOILERPLATE(debugger_iabr_match)
DEBUGGER_BOILERPLATE(debugger_dabr_match)
DEBUGGER_BOILERPLATE(debugger_break_match)
DEBUGGER_BOILERPLATE(debugger_fault_handler)

#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_sstep(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; }
#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
extern void do_send_trap(struct pt_regs *regs, unsigned long address,
			 unsigned long error_code, int signal_code, int brkpt);
#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);
#endif

+26 −7
Original line number Diff line number Diff line
@@ -24,16 +24,30 @@
#define _PPC_BOOK3S_64_HW_BREAKPOINT_H

#ifdef	__KERNEL__
#ifdef CONFIG_HAVE_HW_BREAKPOINT

struct arch_hw_breakpoint {
	unsigned long	address;
	unsigned long	dabrx;
	int		type;
	u8		len; /* length of the target data symbol */
	bool		extraneous_interrupt;
	u16		type;
	u16		len; /* length of the target data symbol */
};

/* 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 <asm/reg.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);
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);

+2 −2
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <linux/cache.h>
#include <asm/ptrace.h>
#include <asm/types.h>
#include <asm/hw_breakpoint.h>

/* We do _not_ want to define new machine types at all, those must die
 * in favor of using the device-tree
@@ -225,8 +226,7 @@ struct thread_struct {
	struct perf_event *last_hit_ubp;
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
#endif
	unsigned long	dabr;		/* Data address breakpoint register */
	unsigned long	dabrx;		/*      ... extension  */
	struct arch_hw_breakpoint hw_brk; /* info on the hardware breakpoint */
	unsigned long	trap_nr;	/* last trap # on this thread */
#ifdef CONFIG_ALTIVEC
	/* Complete AltiVec register set */
+0 −3
Original line number Diff line number Diff line
@@ -206,9 +206,6 @@
#define   DAWRX_KERNEL	(1UL << 1)
#define   DAWRX_HYP	(1UL << 2)
#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_DABRX	0x3F7	/* Data Address Breakpoint Register Extension */
#define   DABRX_USER	(1UL << 0)
+1 −1
Original line number Diff line number Diff line
@@ -1251,7 +1251,7 @@ handle_dabr_fault:
	ld      r4,_DAR(r1)
	ld      r5,_DSISR(r1)
	addi    r3,r1,STACK_FRAME_OVERHEAD
	bl      .do_dabr
	bl      .do_break
12:	b       .ret_from_except_lite


Loading