From rrbranco@br.ibm.com Tue Jul 31 05:38:17 2007
From: Rodrigo Rubira Branco <rrbranco@br.ibm.com>
Subject: 44x hardware watchpoints support
Date: Tue, 31 Jul 2007 05:38:17 +1000
X-Patchwork-ID: 12713




---
Attached a patch to add Linux PPC 44x Hardware Watchpoints.  It's an updated version of the work done by Michel C. Darneille, also from IBM.

I need to redefine in include/asm-powerpc/reg_booke.h the values of DBCR_D1R and DBCR_D1W to 0x00080000 and 0x00040000.  I can't put ifdef to be CONFIG_4xx since some values are not valid in the 405 systems for example (tks Josh Boyer for spot me that).  What is the best way to do that?

Signed-off-by:  Rodrigo Rubira Branco <rrbranco@br.ibm.com>
---

diff -ruN linux.orig/arch/powerpc/kernel/entry_32.S linux/arch/powerpc/kernel/entry_32.S
--- linux.orig/arch/powerpc/kernel/entry_32.S	2007-07-12 17:04:21.000000000 -0300
+++ linux/arch/powerpc/kernel/entry_32.S	2007-07-27 10:33:46.000000000 -0300
@@ -112,7 +112,7 @@
 	/* Check to see if the dbcr0 register is set up to debug.  Use the
 	   single-step bit to do this. */
 	lwz	r12,THREAD_DBCR0(r12)
-	andis.	r12,r12,DBCR0_IC@h
+	andis.	r12,r12,(DBCR0_IC | DBCR_D1R | DBCR_D1W)@h
 	beq+	3f
 	/* From user and task is ptraced - load up global dbcr0 */
 	li	r12,-1			/* clear all pending debug events */
@@ -238,10 +238,13 @@
 	stw	r11,_CCR(r1)
 syscall_exit_cont:
 #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
-	/* If the process has its own DBCR0 value, load it up.  The single
-	   step bit tells us that dbcr0 should be loaded. */
+	/*
+	* If the process has its own DBCR0 value, load it up.  The single
+	* step bit tells us that dbcr0 should be loaded. GDB hw watchpoints
+	* may also be set
+	*/
 	lwz	r0,THREAD+THREAD_DBCR0(r2)
-	andis.	r10,r0,DBCR0_IC@h
+	andis.	r10,r0,(DBCR0_IC | DBCR_D1R | DBCR_D1W)@h
 	bnel-	load_dbcr0
 #endif
 	stwcx.	r0,0,r1			/* to clear the reservation */
@@ -646,10 +649,13 @@
 
 restore_user:
 #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
-	/* Check whether this process has its own DBCR0 value.  The single
-	   step bit tells us that dbcr0 should be loaded. */
+	/*
+	* Check whether this process has its own DBCR0 value.  The single
+	* step bit tells us that dbcr0 should be loaded. GDB hw watchpoints
+	* may also be set
+	*/
 	lwz	r0,THREAD+THREAD_DBCR0(r2)
-	andis.	r10,r0,DBCR0_IC@h
+	andis.	r10,r0,(DBCR0_IC | DBCR_D1R | DBCR_D1W)@h
 	bnel-	load_dbcr0
 #endif
 
diff -ruN linux.orig/arch/powerpc/kernel/process.c linux/arch/powerpc/kernel/process.c
--- linux.orig/arch/powerpc/kernel/process.c	2007-07-12 17:04:21.000000000 -0300
+++ linux/arch/powerpc/kernel/process.c	2007-07-27 10:14:04.000000000 -0300
@@ -197,6 +197,20 @@
 }
 #endif /* CONFIG_SPE */
 
+/* Add support for HW Debug breakpoint.   Use DAC register */
+void set_dac(unsigned long dac)
+{
+       mtspr(SPRN_DAC1, dac);
+}
+unsigned int get_dac()
+{
+       return mfspr(SPRN_DAC1);
+}
+void set_dbcr0(unsigned long dbcr)
+{
+       mtspr(SPRN_DBCR0, dbcr);
+}
+
 #ifndef CONFIG_SMP
 /*
  * If we are doing lazy switching of CPU state (FP, altivec or SPE),
@@ -308,6 +322,10 @@
 		__get_cpu_var(current_dabr) = new->thread.dabr;
 	}
 
+	/* If new thread dac (hw bp) is the same then leave it. */
+	if (new->thread.dac)
+		set_dac(new->thread.dac);
+
 	new_thread = &new->thread;
 	old_thread = &current->thread;
 
@@ -475,6 +493,13 @@
 
 	discard_lazy_cpu_state();
 
+	if (current->thread.dac) {
+		current->thread.dac = 0;
+		current->thread.dbcr0 &= ~(DBCR_D1R | DBCR_D1W);  /* clear
+								debug control */
+		set_dac(0);
+	}
+
 	if (current->thread.dabr) {
 		current->thread.dabr = 0;
 		set_dabr(0);
diff -ruN linux.orig/arch/powerpc/kernel/ptrace.c linux/arch/powerpc/kernel/ptrace.c
--- linux.orig/arch/powerpc/kernel/ptrace.c	2007-07-12 17:04:21.000000000 -0300
+++ linux/arch/powerpc/kernel/ptrace.c	2007-07-27 10:37:03.000000000 -0300
@@ -273,11 +273,14 @@
 
 static void clear_single_step(struct task_struct *task)
 {
+	if (task->thread.dac)   /* if dac then not single step, skip */
+		return;
+
 	struct pt_regs *regs = task->thread.regs;
 
 	if (regs != NULL) {
 #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
-		task->thread.dbcr0 = 0;
+		task->thread.dbcr0 &= ~(DBCR0_IC | DBCR0_IDM);
 		regs->msr &= ~MSR_DE;
 #else
 		regs->msr &= ~MSR_SE;
@@ -286,22 +289,48 @@
 	clear_tsk_thread_flag(task, TIF_SINGLESTEP);
 }
 
-static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
+static int ptrace_get_debugreg(struct task_struct *task, unsigned long data)
+{
+	#ifdef CONFIG_PPC64
+		ret = put_user(child->thread.dabr,
+				(unsigned long __user *)data);
+	#else
+		ret = put_user(child->thread.dac,
+				(unsigned long __user *)data);
+	#endif
+
+	return ret;
+}
+
+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 */
+	/* We only support one DAC 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.dac = data & ~0x3UL;
+
+	if (task->thread.dac == 0) {
+		task->thread.dbcr0 &= ~(DBCR_D1R | DBCR_D1W | DBCR_IDM);
+		task->thread.regs->msr &= ~MSR_DE;
+		return 0;
+	}
+
+	if (!(data & 0x3UL))            /* R or W must be set */
+		return -EINVAL;
+
+	/* Check data lsb for r/w flag:   1 = r   2 = w   then mask lower 2 bits
+	keeping watchpoint addr aligned to 32 bits. */
+	task->thread.dbcr0 = DBCR_IDM;
+
+	if (data & 1)
+		task->thread.dbcr0 |= DBCR_D1R;
+	if (data & 2)
+		task->thread.dbcr0 |= DBCR_D1W;
+
+	task->thread.regs->msr |= MSR_DE;
 
-	task->thread.dabr = data;
 	return 0;
 }
 
@@ -503,11 +532,10 @@
 
 	case PTRACE_GET_DEBUGREG: {
 		ret = -EINVAL;
-		/* We only support one DABR and no IABRS at the moment */
+		/* We only support one DAC at the moment */
 		if (addr > 0)
 			break;
-		ret = put_user(child->thread.dabr,
-			       (unsigned long __user *)data);
+		ret = ptrace_get_debugreg(child, data);
 		break;
 	}
 
diff -ruN linux.orig/arch/powerpc/kernel/signal.c linux/arch/powerpc/kernel/signal.c
--- linux.orig/arch/powerpc/kernel/signal.c	2007-07-12 17:04:21.000000000 -0300
+++ linux/arch/powerpc/kernel/signal.c	2007-07-27 10:30:47.000000000 -0300
@@ -142,6 +142,16 @@
 		set_dabr(current->thread.dabr);
 
 	if (is32) {
+		/*
+		* Reenable the DAC before delivering the signal to
+		* user space. The DAC will have been cleared if it
+		* triggered inside the kernel.
+		*/
+		if (current->thread.dac) {
+			set_dac(current->thread.dac);
+			set_dbcr0(current->thread.dbcr0);
+		}
+
         	if (ka.sa.sa_flags & SA_SIGINFO)
 			ret = handle_rt_signal32(signr, &ka, &info, oldset,
 					regs);
diff -ruN linux.orig/arch/powerpc/kernel/traps.c linux/arch/powerpc/kernel/traps.c
--- linux.orig/arch/powerpc/kernel/traps.c	2007-07-12 17:04:21.000000000 -0300
+++ linux/arch/powerpc/kernel/traps.c	2007-07-27 11:01:31.000000000 -0300
@@ -54,6 +54,10 @@
 #endif
 #include <asm/kexec.h>
 
+extern int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
+							unsigned long data);
+extern int get_dac();
+
 #ifdef CONFIG_DEBUGGER
 int (*__debugger)(struct pt_regs *regs);
 int (*__debugger_ipi)(struct pt_regs *regs);
@@ -61,6 +65,7 @@
 int (*__debugger_sstep)(struct pt_regs *regs);
 int (*__debugger_iabr_match)(struct pt_regs *regs);
 int (*__debugger_dabr_match)(struct pt_regs *regs);
+int (*__debugger_dac_match)(struct pt_regs *regs);
 int (*__debugger_fault_handler)(struct pt_regs *regs);
 
 EXPORT_SYMBOL(__debugger);
@@ -69,6 +74,7 @@
 EXPORT_SYMBOL(__debugger_sstep);
 EXPORT_SYMBOL(__debugger_iabr_match);
 EXPORT_SYMBOL(__debugger_dabr_match);
+EXPORT_SYMBOL(__debugger_dac_match);
 EXPORT_SYMBOL(__debugger_fault_handler);
 #endif
 
@@ -242,6 +248,25 @@
 }
 #endif
 
+static void do_dac(struct pt_regs *regs, unsigned long address,
+						unsigned long error_code)
+{
+	siginfo_t info;
+
+	/* Clear the DAC and struct entries.  One shot trigger */
+	mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~(DBCR_D1R | DBCR_D1W
+							| DBCR_IDM));
+	set_dac(0);
+	ptrace_set_debugreg(current, 0, 0);
+
+	/* Deliver the signal to userspace */
+	info.si_signo = SIGTRAP;
+	info.si_errno = 0;
+	info.si_code = TRAP_HWBKPT;
+	info.si_addr = (void __user *)address;
+	force_sig_info(SIGTRAP, &info, current);
+}
+
 /*
  * I/O accesses can cause machine checks on powermacs.
  * Check if the NIP corresponds to the address of a sync
@@ -978,6 +1003,20 @@
 				return;
 		}
 		_exception(SIGTRAP, regs, TRAP_TRACE, 0);
+	} else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) {
+		regs->msr &= ~MSR_DE;
+		if (user_mode(regs)) {
+			current->thread.dbcr0 &= ~(DBCR_D1R | DBCR_D1W |
+								DBCR_IDM);
+		} else {
+			/* Disable dac interupts */
+			mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~(DBCR_D1R |
+							DBCR_D1W | DBCR_IDM));
+			/* Clear the dac event */
+			mtspr(SPRN_DBSR, (DBSR_DAC1R | DBSR_DAC1W));
+		}
+		/* Setup and send the trap to the handler */
+		do_dac(regs, get_dac(), debug_status);
 	}
 }
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
diff -ruN linux.orig/include/asm-powerpc/processor.h linux/include/asm-powerpc/processor.h
--- linux.orig/include/asm-powerpc/processor.h	2007-07-12 17:04:56.000000000 -0300
+++ linux/include/asm-powerpc/processor.h	2007-07-27 10:47:34.000000000 -0300
@@ -129,6 +129,7 @@
 #if defined(CONFIG_4xx) || defined (CONFIG_BOOKE)
 	unsigned long	dbcr0;		/* debug control register values */
 	unsigned long	dbcr1;
+	unsigned long   dac;            /* Data Address Compare register */
 #endif
 	double		fpr[32];	/* Complete floating point set */
 	struct {			/* fpr ... fpscr must be contiguous */
diff -ruN linux.orig/include/asm-powerpc/system.h linux/include/asm-powerpc/system.h
--- linux.orig/include/asm-powerpc/system.h	2007-07-12 17:04:56.000000000 -0300
+++ linux/include/asm-powerpc/system.h	2007-07-27 11:05:14.000000000 -0300
@@ -72,6 +72,7 @@
 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_dac_match)(struct pt_regs *regs);
 extern int (*__debugger_fault_handler)(struct pt_regs *regs);
 
 #define DEBUGGER_BOILERPLATE(__NAME) \
@@ -88,6 +89,7 @@
 DEBUGGER_BOILERPLATE(debugger_sstep)
 DEBUGGER_BOILERPLATE(debugger_iabr_match)
 DEBUGGER_BOILERPLATE(debugger_dabr_match)
+DEBUGGER_BOILERPLATE(debugger_dac_match)
 DEBUGGER_BOILERPLATE(debugger_fault_handler)
 
 #else
@@ -97,10 +99,13 @@
 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_dac_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);
+extern void set_dac(unsigned long dac);
+extern void set_dbcr0(unsigned long dbcr);
 extern void print_backtrace(unsigned long *);
 extern void show_regs(struct pt_regs * regs);
 extern void flush_instruction_cache(void);

