Esteemed kernel hackers, To follow is my third pass at MIPS watch register support. This version has been tested on: * MIPS 4KEc (mips32) with a single set of watch registers watchhi not reporting I, R, and W conditions. * MIPS 4KEc (mips32r2) with four sets of watch registers. * R5000 SGI O2 (mips4 64bit) with no watch register support. The patches are against 2.6.27-rc4 Changes from the previous version: * Agreement from gdb maintainers that the ptrace interface is workable. * Work around for watchhi registers that do not report the I, R, and W condition bits. * General cleanup, including making much less code conditionally compiled. To really test the patch you will need a patched gdb. My current gdb patch is here: http://sourceware.org/ml/gdb-patches/2008-08/msg00650.html If you have any comments or questions please let me know. David Daney --
This is automatically set for all MIPS32 and MIPS64 processors. Signed-off-by: David Daney <ddaney@avtrex.com> --- arch/mips/Kconfig | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4da736e..f5b3fca 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1272,6 +1272,13 @@ config CPU_SUPPORTS_32BIT_KERNEL config CPU_SUPPORTS_64BIT_KERNEL bool +# +# Set to y for ptrace access to watch registers. +# +config HARDWARE_WATCHPOINTS + bool + default y if CPU_MIPS32 || CPU_MIPS64 + menu "Kernel type" choice -- 1.5.5.1 --
This is the support code for watch register support. It is not hooked
up to anything yet, that comes next.
There are two new files (watch.c, watch.h) the rest is additions.
Signed-off-by: David Daney <ddaney@avtrex.com>
---
arch/mips/kernel/Makefile | 2 +-
arch/mips/kernel/watch.c | 163 ++++++++++++++++++++++++++++++++++++++++
include/asm-mips/cpu-info.h | 4 +
include/asm-mips/processor.h | 21 +++++
include/asm-mips/thread_info.h | 2 +
include/asm-mips/watch.h | 32 ++++++++
6 files changed, 223 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/kernel/watch.c
create mode 100644 include/asm-mips/watch.h
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 706f939..2504f15 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -6,7 +6,7 @@ extra-y := head.o init_task.o vmlinux.lds
obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \
ptrace.o reset.o setup.o signal.o syscall.o \
- time.o topology.o traps.o unaligned.o
+ time.o topology.o traps.o unaligned.o watch.o
obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o
diff --git a/arch/mips/kernel/watch.c b/arch/mips/kernel/watch.c
new file mode 100644
index 0000000..14ce610
--- /dev/null
+++ b/arch/mips/kernel/watch.c
@@ -0,0 +1,163 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2008 David Daney
+ */
+
+#include <linux/sched.h>
+
+#include <asm/processor.h>
+#include <asm/watch.h>
+
+/*
+ * Install the watch registers for the current thread. A maximum of
+ * four registers are installed although the machine may have more.
+ */
+void mips_install_watch_registers(void)
+{
+ struct mips3264_watch_reg_state *watches =
+ &current->thread.watch.mips3264;
+ switch ...Probe for watch register characteristics, and report them in
/proc/cpuinfo.
Signed-off-by: David Daney <ddaney@avtrex.com>
---
arch/mips/kernel/cpu-probe.c | 2 ++
arch/mips/kernel/proc.c | 10 ++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index 335a6ae..d0d07b8 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -21,6 +21,7 @@
#include <asm/fpu.h>
#include <asm/mipsregs.h>
#include <asm/system.h>
+#include <asm/watch.h>
/*
* Not all of the MIPS CPUs have the "wait" instruction available. Moreover,
@@ -685,6 +686,7 @@ static inline void spram_config(void) {}
static inline void cpu_probe_mips(struct cpuinfo_mips *c)
{
decode_configs(c);
+ mips_probe_watch_registers(c);
switch (c->processor_id & 0xff00) {
case PRID_IMP_4KC:
c->cputype = CPU_4KC;
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index 36f0653..11402f5 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -50,8 +50,14 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "tlb_entries\t\t: %d\n", cpu_data[n].tlbsize);
seq_printf(m, "extra interrupt vector\t: %s\n",
cpu_has_divec ? "yes" : "no");
- seq_printf(m, "hardware watchpoint\t: %s\n",
- cpu_has_watch ? "yes" : "no");
+ seq_printf(m, "hardware watchpoint\t: %s",
+ cpu_has_watch ? "yes, " : "no\n");
+ if (cpu_has_watch)
+ seq_printf(m,
+ "count: %d, address mask: 0x%04x, irw mask 0x%02x\n",
+ cpu_data[n].watch_reg_count,
+ cpu_data[n].watch_reg_mask,
+ cpu_data[n].watch_reg_irw);
seq_printf(m, "ASEs implemented\t:%s%s%s%s%s%s\n",
cpu_has_mips16 ? " mips16" : "",
cpu_has_mdmx ? " mdmx" : "",
--
1.5.5.1
--
Here we hook up the watch exception handler so that it sends SIGTRAP
when the hardware watch registers are triggered.
Signed-off-by: David Daney <ddaney@avtrex.com>
---
arch/mips/kernel/genex.S | 4 ++++
arch/mips/kernel/traps.c | 14 +++++++++-----
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index c6ada98..15a9bde 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -416,7 +416,11 @@ NESTED(nmi_handler, PT_SIZE, sp)
BUILD_HANDLER tr tr sti silent /* #13 */
BUILD_HANDLER fpe fpe fpe silent /* #15 */
BUILD_HANDLER mdmx mdmx sti silent /* #22 */
+#ifdef CONFIG_HARDWARE_WATCHPOINTS
+ BUILD_HANDLER watch watch sti silent /* #23 */
+#else
BUILD_HANDLER watch watch sti verbose /* #23 */
+#endif
BUILD_HANDLER mcheck mcheck cli verbose /* #24 */
BUILD_HANDLER mt mt sti silent /* #25 */
BUILD_HANDLER dsp dsp sti silent /* #26 */
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 6bee290..5fbf591 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -42,6 +42,7 @@
#include <asm/tlbdebug.h>
#include <asm/traps.h>
#include <asm/uaccess.h>
+#include <asm/watch.h>
#include <asm/mmu_context.h>
#include <asm/types.h>
#include <asm/stacktrace.h>
@@ -908,12 +909,15 @@ asmlinkage void do_mdmx(struct pt_regs *regs)
asmlinkage void do_watch(struct pt_regs *regs)
{
/*
- * We use the watch exception where available to detect stack
- * overflows.
+ * If the current thread has the watch registers loaded, save
+ * their values and send SIGTRAP. Otherwise another thread
+ * left the registers set, clear them and continue.
*/
- dump_tlb_all();
- show_regs(regs);
- panic("Caught WATCH exception - probably caused by stack overflow.");
+ if (test_tsk_thread_flag(current, TIF_LOAD_WATCH)) {
+ mips_read_watch_registers();
+ force_sig(SIGTRAP, current);
+ } else
+ mips_clear_watch_registers();
}
...Here we hook up the scheduler. Whenever we switch to a new process,
we check to see if the watch registers should be installed, and do it
if needed.
Signed-off-by: David Daney <ddaney@avtrex.com>
---
include/asm-mips/system.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
index a944eda..cd30f83 100644
--- a/include/asm-mips/system.h
+++ b/include/asm-mips/system.h
@@ -20,6 +20,7 @@
#include <asm/cmpxchg.h>
#include <asm/cpu-features.h>
#include <asm/dsp.h>
+#include <asm/watch.h>
#include <asm/war.h>
@@ -76,6 +77,7 @@ do { \
__restore_dsp(current); \
if (cpu_has_userlocal) \
write_c0_userlocal(current_thread_info()->tp_value); \
+ __restore_watch(); \
} while (0)
static inline unsigned long __xchg_u32(volatile int * m, unsigned int val)
--
1.5.5.1
--
This is the final part of the watch register patch. Here we hook up
ptrace so that the user space debugger (gdb), can set and read the
registers.
Signed-off-by: David Daney <ddaney@avtrex.com>
---
arch/mips/kernel/ptrace.c | 97 ++++++++++++++++++++++++++++++++++++++++++-
arch/mips/kernel/ptrace32.c | 15 +++++++
include/asm-mips/ptrace.h | 31 ++++++++++++++
3 files changed, 142 insertions(+), 1 deletions(-)
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 35234b9..d12f2d4 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -46,7 +46,8 @@
*/
void ptrace_disable(struct task_struct *child)
{
- /* Nothing to do.. */
+ /* Don't load the watchpoint registers for the ex-child. */
+ clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
}
/*
@@ -167,6 +168,90 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
return 0;
}
+int ptrace_get_watch_regs(struct task_struct *child,
+ struct pt_watch_regs __user *addr)
+{
+ enum pt_watch_style style;
+ int i;
+
+ if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0)
+ return -EIO;
+ if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs)))
+ return -EIO;
+
+#ifdef CONFIG_32BIT
+ style = pt_watch_style_mips32;
+#define WATCH_STYLE mips32
+#else
+ style = pt_watch_style_mips64;
+#define WATCH_STYLE mips64
+#endif
+
+ __put_user(style, &addr->style);
+ __put_user(current_cpu_data.watch_reg_use_cnt,
+ &addr->WATCH_STYLE.num_valid);
+ __put_user(current_cpu_data.watch_reg_mask,
+ &addr->WATCH_STYLE.reg_mask);
+ __put_user(current_cpu_data.watch_reg_irw,
+ &addr->WATCH_STYLE.irw_mask);
+ for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+ __put_user(child->thread.watch.mips3264.watchlo[i],
+ &addr->WATCH_STYLE.watchlo[i]);
+ __put_user(child->thread.watch.mips3264.watchhi[i] & 0xfff,
+ &addr->WATCH_STYLE.watchhi[i]);
+ }
+
+ return 0;
+}
+
+int ptrace_set_watch_regs(struct ...I think there will have to be at least one more pass at this. The current design assumes that all debug registers support an identical set of the I, R, and W bits and Mask. However sections 8.23 and 8.24 of my MIPS32® Architecture For Programmers Volume III: The MIPS32® Privileged Resource Architecture indicate that they do not have to uniform. I will have to augment the ptrace structures to report the values for each register instead of a single global value. David Daney --
