-- Rodrigo Rubira Branco Software Engineer Advanced Linux Response Team (ALRT) / Linux on Power Toolchain IBM Linux Technology Center (IBM/LTC) [EMAIL PROTECTED]
GPG KeyID: 1FCEDEA1
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 <[EMAIL PROTECTED]> --- 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,[EMAIL PROTECTED] + 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,[EMAIL PROTECTED] + 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,[EMAIL PROTECTED] + 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);
signature.asc
Description: This is a digitally signed message part
_______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-dev