This is the final part of the patch. Here we add ptrace support so that gdb can read and set the watch register values. We add two new ptrace requests PTRACE_GET_WATCH_REGS, and PTRACE_SET_WATCH_REGS to access the watch registers. Since MIPS has more than one format for watch registers, the data structure is a union, the first structure member is an enum that tells us which layout is in use. For this first cut at the patch, I only support mips32 style registers. Signed-off-by: David Daney <ddaney@xxxxxxxxxx> --- arch/mips/kernel/ptrace.c | 81 +++++++++++++++++++++++++++++++++++++++++++++ include/asm-mips/ptrace.h | 23 +++++++++++++ 2 files changed, 104 insertions(+), 0 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 35234b9..d6dece8 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -46,7 +46,12 @@ */ void ptrace_disable(struct task_struct *child) { +#ifdef CONFIG_HARDWARE_WATCHPOINTS + /* Don't load the watchpoint registers for the ex-child. */ + clear_tsk_thread_flag(child, TIF_LOAD_WATCH); +#else /* Nothing to do.. */ +#endif } /* @@ -167,6 +172,72 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) return 0; } +#ifdef CONFIG_HARDWARE_WATCHPOINTS +int ptrace_get_watch_regs(struct task_struct *child, + struct pt_watch_regs __user *addr) +{ + int i; + + if (!cpu_has_watch) + return -EIO; + if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs))) + return -EIO; + + __put_user (pt_watch_style_mips32, &addr->style); + __put_user (current_cpu_data.watch_reg_count, &addr->mips32.num_valid); + __put_user (current_cpu_data.watch_reg_mask, &addr->mips32.reg_mask); + __put_user (current_cpu_data.watch_reg_irw, &addr->mips32.irw_mask); + for (i = 0; i < current_cpu_data.watch_reg_count; i++) { + __put_user (child->thread.watch.mips32.watchlo[i], + &addr->mips32.watchlo[i]); + __put_user (child->thread.watch.mips32.watchhi[i] & 0xfff, + &addr->mips32.watchhi[i]); + } + + return 0; +} + +int ptrace_set_watch_regs(struct task_struct *child, + struct pt_watch_regs __user *addr) +{ + int i; + int watch_active = 0; + unsigned long lt[NUM_WATCH_REGS]; + unsigned int ht[NUM_WATCH_REGS]; + + if (!cpu_has_watch) + return -EIO; + if (!access_ok(VERIFY_READ, addr, sizeof(struct pt_watch_regs))) + return -EIO; + /* Check the values. */ + for (i = 0; i < NUM_WATCH_REGS; i++) { + __get_user(lt[i], &addr->mips32.watchlo[i]); + if (lt[i] & __UA_LIMIT) + return -EINVAL; + + __get_user(ht[i], &addr->mips32.watchhi[i]); + if (ht[i] & ~0xff8) + return -EINVAL; + } + /* Install them. */ + for (i = 0; i < NUM_WATCH_REGS; i++) { + if (lt[i] & 7) + watch_active = 1; + child->thread.watch.mips32.watchlo[i] = lt[i]; + /* Set the G bit. */ + child->thread.watch.mips32.watchhi[i] = ht[i] | 0x40000000; + } + + if (watch_active) + set_tsk_thread_flag(child, TIF_LOAD_WATCH); + else + clear_tsk_thread_flag(child, TIF_LOAD_WATCH); + + return 0; +} + +#endif + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; @@ -439,7 +510,17 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = put_user(task_thread_info(child)->tp_value, (unsigned long __user *) data); break; +#ifdef CONFIG_HARDWARE_WATCHPOINTS + case PTRACE_GET_WATCH_REGS: + ret = ptrace_get_watch_regs(child, + (struct pt_watch_regs __user *) addr); + break; + case PTRACE_SET_WATCH_REGS: + ret = ptrace_set_watch_regs(child, + (struct pt_watch_regs __user *) addr); + break; +#endif default: ret = ptrace_request(child, request, addr, data); break; diff --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h index 786f7e3..8fb1bcc 100644 --- a/include/asm-mips/ptrace.h +++ b/include/asm-mips/ptrace.h @@ -71,6 +71,29 @@ struct pt_regs { #define PTRACE_POKEDATA_3264 0xc3 #define PTRACE_GET_THREAD_AREA_3264 0xc4 +/* Read and write watchpoint registers. */ +enum pt_watch_style { + pt_watch_style_mips32, + pt_watch_style_mips64 +}; +struct mips32_watch_regs { + unsigned int num_valid; + unsigned int reg_mask; + unsigned int irw_mask; + unsigned long watchlo[8]; + unsigned int watchhi[8]; +}; + +struct pt_watch_regs { + enum pt_watch_style style; + union { + struct mips32_watch_regs mips32; + }; +}; + +#define PTRACE_GET_WATCH_REGS 0xd0 +#define PTRACE_SET_WATCH_REGS 0xd1 + #ifdef __KERNEL__ #include <linux/linkage.h> -- 1.5.5