From: Rodrigo Rubira Branco <rrbranco@br.ibm.com>
To: linuxppc-dev@ozlabs.org
Subject: 44x hardware watchpoints support
Date: Mon, 30 Jul 2007 16:38:17 -0300 [thread overview]
Message-ID: <1185824297.4414.4.camel@localhost.localdomain> (raw)
[-- Attachment #1.1: Type: text/plain, Size: 199 bytes --]
--
Rodrigo Rubira Branco
Software Engineer
Advanced Linux Response Team (ALRT) / Linux on Power Toolchain
IBM Linux Technology Center (IBM/LTC)
rrbranco@br.ibm.com
GPG KeyID: 1FCEDEA1
[-- Attachment #1.2: mcp-ppc-hwwatchpoint-latest-27-07-07.diff --]
[-- Type: text/x-patch, Size: 11393 bytes --]
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 = ¤t->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);
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
reply other threads:[~2007-07-30 19:35 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1185824297.4414.4.camel@localhost.localdomain \
--to=rrbranco@br.ibm.com \
--cc=linuxppc-dev@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).