I rolled up several patches along with my changes to fix HW breakpoints
on x86_64 and i386. I also deprecated some of the #defines in favor of
using the kgdb_ops to control the HW breakpoints for other non IA archs
at the point that these are implemented.
Until such time that there is a common interface for kernel HW
breakpoints, if a user space application uses HW breakpoints the kernel
breakpoints will disappear. I tested execution, data access and data
write breakpoints and all work with gdb 6.6.
If there are no objections, I am going to replace the i386.patch with
i386_hw_breakpoints.patch and x86_64_breakpoints.patch and merge the
core changes for arch breakpoints to core-lite.patch.
Signed-off-by: Jason Wessel <[EMAIL PROTECTED]>
---
arch/i386/kernel/kgdb.c | 92 +++++++++++++++++++++++++++++------
arch/x86_64/kernel/kgdb.c | 117 +++++++++++++++++++++++++++++++++++++++++++--
include/asm-generic/kgdb.h | 40 ---------------
include/linux/kgdb.h | 2
kernel/kgdb.c | 12 ++--
lib/Kconfig.kgdb | 8 ---
6 files changed, 199 insertions(+), 72 deletions(-)
Index: linux-2.6.21.1/lib/Kconfig.kgdb
===================================================================
--- linux-2.6.21.1.orig/lib/Kconfig.kgdb
+++ linux-2.6.21.1/lib/Kconfig.kgdb
@@ -21,14 +21,6 @@ config KGDB
at http://kgdb.sourceforge.net as well as in DocBook form
in Documentation/DocBook/. If unsure, say N.
-config KGDB_ARCH_HAS_HARDWARE_BREAKPOINTS
- bool
- depends on KGDB
- default n
- help
- If you say Y here, KGDB will make use of hardware breakpoints
- rather than software breakpoints. If unsure, say N.
-
config KGDB_ARCH_HAS_SHADOW_INFO
bool
Index: linux-2.6.21.1/arch/i386/kernel/kgdb.c
===================================================================
--- linux-2.6.21.1.orig/arch/i386/kernel/kgdb.c
+++ linux-2.6.21.1/arch/i386/kernel/kgdb.c
@@ -14,12 +14,14 @@
/*
* Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2007 Wind River Systems, Inc.
*/
/*
* Contributor: Lake Stevens Instrument Division$
* Written by: Glenn Engel $
* Updated by: Amit Kale<[EMAIL PROTECTED]>
* Updated by: Tom Rini <[EMAIL PROTECTED]>
+ * Updated by: Jason Wessel <[EMAIL PROTECTED]>
* Modified for 386 by Jim Kingdon, Cygnus Support.
* Origianl kgdb, compatibility with 2.1.xx kernel by
* David Grothe <[EMAIL PROTECTED]>
@@ -126,8 +128,53 @@ static struct hw_breakpoint {
{ .enabled = 0 },
};
-#ifdef CONFIG_KGDB_ARCH_HAS_HARDWARE_BREAKPOINTS
-int kgdb_remove_hw_break(unsigned long addr)
+static void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned long dr7;
+
+ get_debugreg(dr7, 7);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ set_debugreg(breakinfo[breakno].addr, 0);
+ break;
+
+ case 1:
+ set_debugreg(breakinfo[breakno].addr, 1);
+ break;
+
+ case 2:
+ set_debugreg(breakinfo[breakno].addr, 2);
+ break;
+
+ case 3:
+ set_debugreg(breakinfo[breakno].addr, 3);
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit)
+ set_debugreg(dr7, 7);
+}
+
+static int kgdb_remove_hw_break(unsigned long addr, int len,
+ enum kgdb_bptype bptype)
{
int i, idx = -1;
for (i = 0; i < 4; i++) {
@@ -143,20 +190,17 @@ int kgdb_remove_hw_break(unsigned long a
return 0;
}
-void kgdb_remove_all_hw_break(void)
+static void kgdb_remove_all_hw_break(void)
{
int i;
for (i = 0; i < 4; i++) {
- if (breakinfo[i].enabled) {
- /* Do what? */
- ;
- }
memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
}
}
-int kgdb_set_hw_break(unsigned long addr)
+static int kgdb_set_hw_break(unsigned long addr, int len,
+ enum kgdb_bptype bptype)
{
int i, idx = -1;
for (i = 0; i < 4; i++) {
@@ -167,19 +211,32 @@ int kgdb_set_hw_break(unsigned long addr
}
if (idx == -1)
return -1;
-
+ if (bptype == bp_hardware_breakpoint) {
+ breakinfo[idx].type = 0;
+ breakinfo[idx].len = 0;
+ } else if (bptype == bp_write_watchpoint) {
+ breakinfo[idx].type = 1;
+ if (len == 1 || len == 2 || len == 4)
+ breakinfo[idx].len = len - 1;
+ else
+ return -1;
+ } else if (bptype == bp_access_watchpoint) {
+ breakinfo[idx].type = 3;
+ if (len == 1 || len == 2 || len == 4)
+ breakinfo[idx].len = len - 1;
+ else
+ return -1;
+ } else
+ return -1;
breakinfo[idx].enabled = 1;
- breakinfo[idx].type = 1;
- breakinfo[idx].len = 1;
breakinfo[idx].addr = addr;
return 0;
}
-#endif /* CONFIG_KGDB_ARCH_HAS_HARDWARE_BREAKPOINTS */
void kgdb_disable_hw_debug(struct pt_regs *regs)
{
/* Disable hardware debugging while we are in kgdb */
- asm volatile ("movl %0,%%db7": /* no output */ :"r" (0));
+ set_debugreg(0, 7);
}
void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code)
@@ -225,7 +282,7 @@ int kgdb_arch_handle_exception(int e_vec
atomic_set(&cpu_doing_single_step,raw_smp_processor_id());
}
- asm volatile ("movl %%db6, %0\n":"=r" (dr6));
+ get_debugreg(dr6, 6);
if (!(dr6 & 0x4000)) {
long breakno;
for (breakno = 0; breakno < 4; ++breakno) {
@@ -237,7 +294,8 @@ int kgdb_arch_handle_exception(int e_vec
}
}
}
- asm volatile ("movl %0, %%db6\n"::"r" (0));
+ set_debugreg(0, 6);
+ kgdb_correct_hw_break();
return (0);
} /* switch */
@@ -322,4 +380,8 @@ unsigned long kgdb_arch_pc(int exception
struct kgdb_arch arch_kgdb_ops = {
.gdb_bpt_instr = {0xcc},
.flags = KGDB_HW_BREAKPOINT,
+ .set_hw_breakpoint = kgdb_set_hw_break,
+ .remove_hw_breakpoint = kgdb_remove_hw_break,
+ .remove_all_hw_break = kgdb_remove_all_hw_break,
+ .correct_hw_break = kgdb_correct_hw_break,
};
Index: linux-2.6.21.1/arch/x86_64/kernel/kgdb.c
===================================================================
--- linux-2.6.21.1.orig/arch/x86_64/kernel/kgdb.c
+++ linux-2.6.21.1/arch/x86_64/kernel/kgdb.c
@@ -17,6 +17,7 @@
* Copyright (C) 2000-2001 VERITAS Software Corporation.
* Copyright (C) 2002 Andi Kleen, SuSE Labs
* Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2007 Jason Wessel, Wind River Systems, Inc.
*/
/****************************************************************************
* Contributor: Lake Stevens Instrument Division$
@@ -136,10 +137,115 @@ enabled:0}, {
enabled:0}, {
enabled:0}};
+static void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned long dr7;
+
+ get_debugreg(dr7, 7);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ set_debugreg(breakinfo[breakno].addr, 0);
+ break;
+
+ case 1:
+ set_debugreg(breakinfo[breakno].addr, 1);
+ break;
+
+ case 2:
+ set_debugreg(breakinfo[breakno].addr, 2);
+ break;
+
+ case 3:
+ set_debugreg(breakinfo[breakno].addr, 3);
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit)
+ set_debugreg(dr7, 7);
+}
+
+static int kgdb_remove_hw_break(unsigned long addr, int len,
+ enum kgdb_bptype bptype)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (breakinfo[i].addr == addr && breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 0;
+ return 0;
+}
+
+static void kgdb_remove_all_hw_break(void)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+ }
+}
+
+static int kgdb_set_hw_break(unsigned long addr, int len,
+ enum kgdb_bptype bptype)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (!breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+ if (bptype == bp_hardware_breakpoint) {
+ breakinfo[idx].type = 0;
+ breakinfo[idx].len = 0;
+ } else if (bptype == bp_write_watchpoint) {
+ breakinfo[idx].type = 1;
+ if (len == 1 || len == 2 || len == 4)
+ breakinfo[idx].len = len - 1;
+ else
+ return -1;
+ } else if (bptype == bp_access_watchpoint) {
+ breakinfo[idx].type = 3;
+ if (len == 1 || len == 2 || len == 4)
+ breakinfo[idx].len = len - 1;
+ else
+ return -1;
+ } else
+ return -1;
+ breakinfo[idx].enabled = 1;
+ breakinfo[idx].addr = addr;
+ return 0;
+}
+
void kgdb_disable_hw_debug(struct pt_regs *regs)
{
/* Disable hardware debugging while we are in kgdb */
- asm volatile ("movq %0,%%db7": /* no output */ :"r" (0UL));
+ set_debugreg(0UL, 7);
}
void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code)
@@ -187,7 +293,7 @@ int kgdb_arch_handle_exception(int e_vec
}
- asm volatile ("movq %%db6, %0\n":"=r" (dr6));
+ get_debugreg(dr6, 6);
if (!(dr6 & 0x4000)) {
for (breakno = 0; breakno < 4; ++breakno) {
if (dr6 & (1 << breakno)) {
@@ -200,7 +306,8 @@ int kgdb_arch_handle_exception(int e_vec
}
}
}
- asm volatile ("movq %0, %%db6\n"::"r" (0UL));
+ set_debugreg(0UL, 6);
+ kgdb_correct_hw_break();
return (0);
} /* switch */
@@ -346,4 +453,8 @@ struct kgdb_arch arch_kgdb_ops = {
.gdb_bpt_instr = {0xcc},
.flags = KGDB_HW_BREAKPOINT,
.shadowth = 1,
+ .set_hw_breakpoint = kgdb_set_hw_break,
+ .remove_hw_breakpoint = kgdb_remove_hw_break,
+ .remove_all_hw_break = kgdb_remove_all_hw_break,
+ .correct_hw_break = kgdb_correct_hw_break,
};
Index: linux-2.6.21.1/include/asm-generic/kgdb.h
===================================================================
--- linux-2.6.21.1.orig/include/asm-generic/kgdb.h
+++ linux-2.6.21.1/include/asm-generic/kgdb.h
@@ -60,46 +60,6 @@ extern void kgdb_disable_hw_debug(struct
#define kgdb_post_master_code(regs, v, c) do { } while (0)
#endif
-#ifdef CONFIG_KGDB_ARCH_HAS_HARDWARE_BREAKPOINTS
-/**
- * kgdb_set_hw_break - Set a hardware breakpoint at @addr.
- * @addr: The address to set a hardware breakpoint at.
- */
-extern int kgdb_set_hw_break(unsigned long addr);
-
-/**
- * kgdb_remove_hw_break - Remove a hardware breakpoint at @addr.
- * @addr: The address to remove a hardware breakpoint from.
- */
-extern int kgdb_remove_hw_break(unsigned long addr);
-
-/**
- * kgdb_remove_all_hw_break - Clear all hardware breakpoints.
- */
-extern void kgdb_remove_all_hw_break(void);
-
-/**
- * kgdb_correct_hw_break - Correct hardware breakpoints.
- *
- * A hook to allow for changes to the hardware breakpoint, called
- * after a single step (s) or continue (c) packet, and once we're about
- * to let the kernel continue running.
- *
- * This is used to set the hardware breakpoint registers for all the
- * slave cpus on an SMP configuration. This must be called after any
- * changes are made to the hardware breakpoints (such as by a single
- * step (s) or continue (c) packet. This is only required on
- * architectures that support SMP and every processor has its own set
- * of breakpoint registers.
- */
-extern void kgdb_correct_hw_break(void);
-#else
-#define kgdb_set_hw_break(addr) 0
-#define kgdb_remove_hw_break(addr) 0
-#define kgdb_remove_all_hw_break() do { } while (0)
-#define kgdb_correct_hw_break() do { } while (0)
-#endif
-
#ifdef CONFIG_KGDB_ARCH_HAS_SHADOW_INFO
/**
* kgdb_shadowinfo - Get shadowed information on @threadid.
Index: linux-2.6.21.1/include/linux/kgdb.h
===================================================================
--- linux-2.6.21.1.orig/include/linux/kgdb.h
+++ linux-2.6.21.1/include/linux/kgdb.h
@@ -220,6 +220,8 @@ struct kgdb_arch {
int (*remove_breakpoint)(unsigned long, char *);
int (*set_hw_breakpoint)(unsigned long, int, enum kgdb_bptype);
int (*remove_hw_breakpoint)(unsigned long, int, enum kgdb_bptype);
+ void (*remove_all_hw_break)(void);
+ void (*correct_hw_break)(void);
};
/* Thread reference */
Index: linux-2.6.21.1/kernel/kgdb.c
===================================================================
--- linux-2.6.21.1.orig/kernel/kgdb.c
+++ linux-2.6.21.1/kernel/kgdb.c
@@ -609,6 +609,9 @@ static void kgdb_wait(struct pt_regs *re
kgdb_info[processor].debuggerinfo = NULL;
kgdb_info[processor].task = NULL;
+ /* fix up hardware debug registers on local cpu */
+ if (kgdb_ops->correct_hw_break)
+ kgdb_ops->correct_hw_break();
/* Signal the master processor that we are done */
atomic_set(&procindebug[processor], 0);
spin_unlock(&slavecpulocks[processor]);
@@ -782,7 +785,8 @@ int remove_all_break(void)
}
/* Clear hardware breakpoints. */
- kgdb_remove_all_hw_break();
+ if (kgdb_ops->remove_all_hw_break)
+ kgdb_ops->remove_all_hw_break();
return 0;
}
@@ -1313,7 +1317,7 @@ int kgdb_handle_exception(int ex_vector,
/* Test if this is a hardware breakpoint, and
* if we support it. */
if (*bpt_type == '1' &&
- !kgdb_ops->flags & KGDB_HW_BREAKPOINT)
+ !(kgdb_ops->flags & KGDB_HW_BREAKPOINT))
/* Unsupported. */
break;
@@ -1334,12 +1338,8 @@ int kgdb_handle_exception(int ex_vector,
if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0')
error = kgdb_set_sw_break(addr);
- else if (remcom_in_buffer[0] == 'Z' && *bpt_type == '1')
- error = kgdb_set_hw_break(addr);
else if (remcom_in_buffer[0] == 'z' && *bpt_type == '0')
error = kgdb_remove_sw_break(addr);
- else if (remcom_in_buffer[0] == 'z' && *bpt_type == '1')
- error = kgdb_remove_hw_break(addr);
else if (remcom_in_buffer[0] == 'Z')
error = kgdb_ops->set_hw_breakpoint(addr,
(int)length,
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Kgdb-bugreport mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kgdb-bugreport