[patch 5/5] ppc64: Add ptrace data breakpoint support

Anton Blanchard anton at samba.org
Sat Sep 10 16:01:11 EST 2005


Add hardware data breakpoint support.

Signed-off-by: Anton Blanchard <anton at samba.org>
---

Index: build/arch/ppc64/kernel/process.c
===================================================================
--- build.orig/arch/ppc64/kernel/process.c	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/kernel/process.c	2005-09-10 15:52:32.000000000 +1000
@@ -54,6 +54,7 @@
 #include <asm/sections.h>
 #include <asm/tlbflush.h>
 #include <asm/time.h>
+#include <asm/plpar_wrappers.h>
 
 #ifndef CONFIG_SMP
 struct task_struct *last_task_used_math = NULL;
@@ -163,7 +164,30 @@
 
 #endif /* CONFIG_ALTIVEC */
 
+static void set_dabr_spr(unsigned long val)
+{
+	mtspr(SPRN_DABR, val);
+}
+
+int set_dabr(unsigned long dabr)
+{
+	int ret = 0;
+
+	if (firmware_has_feature(FW_FEATURE_XDABR)) {
+		/* We want to catch accesses from kernel and userspace */
+		unsigned long flags = H_DABRX_KERNEL|H_DABRX_USER;
+		ret = plpar_set_xdabr(dabr, flags);
+	} else if (firmware_has_feature(FW_FEATURE_DABR)) {
+		ret = plpar_set_dabr(dabr);
+	} else {
+		set_dabr_spr(dabr);
+	}
+
+	return ret;
+}
+
 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
+static DEFINE_PER_CPU(unsigned long, current_dabr);
 
 struct task_struct *__switch_to(struct task_struct *prev,
 				struct task_struct *new)
@@ -198,6 +222,11 @@
 		new->thread.regs->msr |= MSR_VEC;
 #endif /* CONFIG_ALTIVEC */
 
+	if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) {
+		set_dabr(new->thread.dabr);
+		__get_cpu_var(current_dabr) = new->thread.dabr;
+	}
+
 	flush_tlb_pending();
 
 	new_thread = &new->thread;
@@ -334,6 +363,11 @@
 		last_task_used_altivec = NULL;
 #endif /* CONFIG_ALTIVEC */
 #endif /* CONFIG_SMP */
+
+	if (current->thread.dabr) {
+		current->thread.dabr = 0;
+		set_dabr(0);
+	}
 }
 
 void
Index: build/arch/ppc64/kernel/ptrace.c
===================================================================
--- build.orig/arch/ppc64/kernel/ptrace.c	2005-09-10 15:36:01.000000000 +1000
+++ build/arch/ppc64/kernel/ptrace.c	2005-09-10 15:36:05.000000000 +1000
@@ -207,6 +207,19 @@
 		break;
 	}
 
+	case PTRACE_GET_DEBUGREG: {
+		ret = -EINVAL;
+		/* We only support one DABR and no IABRS at the moment */
+		if (addr > 0)
+			break;
+		ret = put_user(child->thread.dabr,
+			       (unsigned long __user *)data);
+		break;
+	}
+
+	case PTRACE_SET_DEBUGREG:
+		ret = ptrace_set_debugreg(child, addr, data);
+
 	case PTRACE_DETACH:
 		ret = ptrace_detach(child, data);
 		break;
Index: build/arch/ppc64/kernel/ptrace32.c
===================================================================
--- build.orig/arch/ppc64/kernel/ptrace32.c	2005-09-10 15:36:02.000000000 +1000
+++ build/arch/ppc64/kernel/ptrace32.c	2005-09-10 15:36:05.000000000 +1000
@@ -338,6 +338,19 @@
 		break;
 	}
 
+	case PTRACE_GET_DEBUGREG: {
+		ret = -EINVAL;
+		/* We only support one DABR and no IABRS at the moment */
+		if (addr > 0)
+			break;
+		ret = put_user(child->thread.dabr, (u32 __user *)data);
+		break;
+	}
+
+	case PTRACE_SET_DEBUGREG:
+		ret = ptrace_set_debugreg(child, addr, data);
+		break;
+
 	case PTRACE_DETACH:
 		ret = ptrace_detach(child, data);
 		break;
Index: build/include/asm-ppc64/processor.h
===================================================================
--- build.orig/include/asm-ppc64/processor.h	2005-09-10 15:35:41.000000000 +1000
+++ build/include/asm-ppc64/processor.h	2005-09-10 15:36:02.000000000 +1000
@@ -433,6 +433,7 @@
 	unsigned long	start_tb;	/* Start purr when proc switched in */
 	unsigned long	accum_tb;	/* Total accumilated purr for process */
 	unsigned long	vdso_base;	/* base of the vDSO library */
+	unsigned long	dabr;		/* Data address breakpoint register */
 #ifdef CONFIG_ALTIVEC
 	/* Complete AltiVec register set */
 	vector128	vr[32] __attribute((aligned(16)));
Index: build/arch/ppc64/mm/fault.c
===================================================================
--- build.orig/arch/ppc64/mm/fault.c	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/mm/fault.c	2005-09-10 15:36:02.000000000 +1000
@@ -77,6 +77,28 @@
 	return 0;
 }
 
+static void do_dabr(struct pt_regs *regs, unsigned long error_code)
+{
+	siginfo_t info;
+
+	if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
+			11, SIGSEGV) == NOTIFY_STOP)
+		return;
+
+	if (debugger_dabr_match(regs))
+		return;
+
+	/* Clear the DABR */
+	set_dabr(0);
+
+	/* Deliver the signal to userspace */
+	info.si_signo = SIGTRAP;
+	info.si_errno = 0;
+	info.si_code = TRAP_HWBKPT;
+	info.si_addr = (void __user *)regs->nip;
+	force_sig_info(SIGTRAP, &info, current);
+}
+
 /*
  * The error_code parameter is
  *  - DSISR for a non-SLB data access fault,
@@ -111,12 +133,9 @@
 	if (!user_mode(regs) && (address >= TASK_SIZE))
 		return SIGSEGV;
 
-	if (error_code & DSISR_DABRMATCH) {
-		if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
-					11, SIGSEGV) == NOTIFY_STOP)
-			return 0;
-		if (debugger_dabr_match(regs))
-			return 0;
+  	if (error_code & DSISR_DABRMATCH) {
+		do_dabr(regs, error_code);
+		return 0;
 	}
 
 	if (in_atomic() || mm == NULL) {
Index: build/arch/ppc64/kernel/ras.c
===================================================================
--- build.orig/arch/ppc64/kernel/ras.c	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/kernel/ras.c	2005-09-10 15:36:02.000000000 +1000
@@ -59,8 +59,6 @@
 /* This is true if we are using the firmware NMI handler (typically LPAR) */
 extern int fwnmi_active;
 
-extern void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr);
-
 static int ras_get_sensor_state_token;
 static int ras_check_exception_token;
 
Index: build/include/asm-ppc64/system.h
===================================================================
--- build.orig/include/asm-ppc64/system.h	2005-09-10 15:35:41.000000000 +1000
+++ build/include/asm-ppc64/system.h	2005-09-10 15:36:02.000000000 +1000
@@ -101,6 +101,9 @@
 static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
 #endif
 
+extern int set_dabr(unsigned long dabr);
+extern void _exception(int signr, struct pt_regs *regs, int code,
+		       unsigned long addr);
 extern int fix_alignment(struct pt_regs *regs);
 extern void bad_page_fault(struct pt_regs *regs, unsigned long address,
 			   int sig);
Index: build/include/asm-ppc64/ptrace-common.h
===================================================================
--- build.orig/include/asm-ppc64/ptrace-common.h	2005-09-10 15:36:01.000000000 +1000
+++ build/include/asm-ppc64/ptrace-common.h	2005-09-10 15:36:05.000000000 +1000
@@ -13,6 +13,7 @@
 #define _PPC64_PTRACE_COMMON_H
 
 #include <linux/config.h>
+#include <asm/system.h>
 
 /*
  * Set of msr bits that gdb can change on behalf of a process.
@@ -141,4 +142,23 @@
 }
 #endif
 
+static inline int ptrace_set_debugreg(struct task_struct *task,
+				      unsigned long addr, unsigned long data)
+{
+	/* We only support one DABR and no IABRS at the moment */
+	if (addr > 0)
+		return -EINVAL;
+
+	/* The bottom 3 bits are flags */
+	if ((data & ~0x7UL) >= TASK_SIZE)
+		return -EIO;
+
+	/* Ensure translation is on */
+	if (data && !(data & DABR_TRANSLATION))
+		return -EIO;
+
+	task->thread.dabr = data;
+	return 0;
+}
+
 #endif /* _PPC64_PTRACE_COMMON_H */
Index: build/arch/ppc64/xmon/privinst.h
===================================================================
--- build.orig/arch/ppc64/xmon/privinst.h	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/xmon/privinst.h	2005-09-10 15:36:02.000000000 +1000
@@ -46,7 +46,6 @@
 GSETSPR(1008, hid0)
 GSETSPR(1009, hid1)
 GSETSPR(1010, iabr)
-GSETSPR(1013, dabr)
 GSETSPR(1023, pir)
 
 static inline void store_inst(void *p)
Index: build/arch/ppc64/xmon/xmon.c
===================================================================
--- build.orig/arch/ppc64/xmon/xmon.c	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/xmon/xmon.c	2005-09-10 15:36:02.000000000 +1000
@@ -586,6 +586,8 @@
 {
 	if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
 		return 0;
+	if (dabr.enabled == 0)
+		return 0;
 	xmon_core(regs, 0);
 	return 1;
 }
@@ -628,20 +630,6 @@
 	return 0;
 }
 
-/* On systems with a hypervisor, we can't set the DABR
-   (data address breakpoint register) directly. */
-static void set_controlled_dabr(unsigned long val)
-{
-#ifdef CONFIG_PPC_PSERIES
-	if (systemcfg->platform == PLATFORM_PSERIES_LPAR) {
-		int rc = plpar_hcall_norets(H_SET_DABR, val);
-		if (rc != H_Success)
-			xmon_printf("Warning: setting DABR failed (%d)\n", rc);
-	} else
-#endif
-		set_dabr(val);
-}
-
 static struct bpt *at_breakpoint(unsigned long pc)
 {
 	int i;
@@ -728,7 +716,7 @@
 static void insert_cpu_bpts(void)
 {
 	if (dabr.enabled)
-		set_controlled_dabr(dabr.address | (dabr.enabled & 7));
+		set_dabr(dabr.address | (dabr.enabled & 7));
 	if (iabr && cpu_has_feature(CPU_FTR_IABR))
 		set_iabr(iabr->address
 			 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
@@ -756,7 +744,7 @@
 
 static void remove_cpu_bpts(void)
 {
-	set_controlled_dabr(0);
+	set_dabr(0);
 	if (cpu_has_feature(CPU_FTR_IABR))
 		set_iabr(0);
 }
Index: build/include/asm-ppc64/hvcall.h
===================================================================
--- build.orig/include/asm-ppc64/hvcall.h	2005-09-10 15:35:41.000000000 +1000
+++ build/include/asm-ppc64/hvcall.h	2005-09-10 15:36:02.000000000 +1000
@@ -56,6 +56,11 @@
 #define H_PP1			(1UL<<(63-62))
 #define H_PP2			(1UL<<(63-63))
 
+/* DABRX flags */
+#define H_DABRX_HYPERVISOR	(1UL<<(63-61))
+#define H_DABRX_KERNEL		(1UL<<(63-62))
+#define H_DABRX_USER		(1UL<<(63-63))
+
 /* pSeries hypervisor opcodes */
 #define H_REMOVE		0x04
 #define H_ENTER			0x08
@@ -101,6 +106,7 @@
 #define H_VIO_SIGNAL		0x104
 #define H_SEND_CRQ		0x108
 #define H_COPY_RDMA             0x110
+#define H_SET_XDABR		0x134
 #define H_STUFF_TCE		0x138
 #define H_PUT_TCE_INDIRECT	0x13C
 #define H_VTERM_PARTNER_INFO	0x150
Index: build/include/asm-ppc64/plpar_wrappers.h
===================================================================
--- build.orig/include/asm-ppc64/plpar_wrappers.h	2005-09-10 15:35:41.000000000 +1000
+++ build/include/asm-ppc64/plpar_wrappers.h	2005-09-10 15:36:02.000000000 +1000
@@ -107,5 +107,14 @@
 				  lbuf[1]);
 }
 
+static inline long plpar_set_xdabr(unsigned long address, unsigned long flags)
+{
+	return plpar_hcall_norets(H_SET_XDABR, address, flags);
+}
+
+static inline long plpar_set_dabr(unsigned long val)
+{
+	return plpar_hcall_norets(H_SET_DABR, val);
+}
 
 #endif /* _PPC64_PLPAR_WRAPPERS_H */
Index: build/arch/ppc64/kernel/signal.c
===================================================================
--- build.orig/arch/ppc64/kernel/signal.c	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/kernel/signal.c	2005-09-10 15:36:02.000000000 +1000
@@ -550,6 +550,15 @@
 		/* Whee!  Actually deliver the signal.  */
 		if (TRAP(regs) == 0x0C00)
 			syscall_restart(regs, &ka);
+
+		/*
+		 * Reenable the DABR before delivering the signal to
+		 * user space. The DABR will have been cleared if it
+		 * triggered inside the kernel.
+		 */
+		if (current->thread.dabr)
+			set_dabr(current->thread.dabr);
+
 		return handle_signal(signr, &ka, &info, oldset, regs);
 	}
 
Index: build/arch/ppc64/kernel/signal32.c
===================================================================
--- build.orig/arch/ppc64/kernel/signal32.c	2005-09-10 15:35:41.000000000 +1000
+++ build/arch/ppc64/kernel/signal32.c	2005-09-10 15:36:02.000000000 +1000
@@ -970,6 +970,14 @@
 		newsp = regs->gpr[1];
 	newsp &= ~0xfUL;
 
+	/*
+	 * Reenable the DABR before delivering the signal to
+	 * user space. The DABR will have been cleared if it
+	 * triggered inside the kernel.
+	 */
+	if (current->thread.dabr)
+		set_dabr(current->thread.dabr);
+
 	/* Whee!  Actually deliver the signal.  */
 	if (ka.sa.sa_flags & SA_SIGINFO)
 		ret = handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp);

--




More information about the Linuxppc64-dev mailing list