[U-Boot] [PATCH] KGDB set / remove breakpoints

Tonny Tzeng tonny.tzeng at gmail.com
Sat Apr 17 19:20:11 CEST 2010


This patch extends the current KGDB logic to handle 'Z' and 'z'
GDB packets for setting or removing breakpoints.

Two weak functions have been added to the kgdb_stub.c:
arch_kgdb_set_sw_break() and arch_kgdb_remove_sw_break() could be
overrode by the arch implementations.

Please note, after applying this patch, those architectures, which
already enabled KGDB support, have to create a new asm/kgdb.h and
define the length of the break instruction (BREAK_INSTR_SIZE) in that
file.

Signed-off-by: Tonny Tzeng <tonny.tzeng at gmail.com>
---
 common/kgdb.c       |   85 +++++++++++++++++++++++++++++++++++++++++++++++++++
 common/kgdb_stubs.c |   12 +++++++
 include/kgdb.h      |   31 ++++++++++++++++++
 3 files changed, 128 insertions(+), 0 deletions(-)

diff --git a/common/kgdb.c b/common/kgdb.c
index 0531452..66378e5 100644
--- a/common/kgdb.c
+++ b/common/kgdb.c
@@ -220,6 +220,85 @@ hexToInt(char **ptr, int *intValue)
 	return (numChars);
 }
 
+/*
+ * Holds information about breakpoints in a kernel. These breakpoints are
+ * added and removed by gdb.
+ */
+static struct kgdb_bkpt	kgdb_break[KGDB_MAX_BREAKPOINTS];
+
+static int kgdb_set_sw_break(int addr)
+{
+	int i, breakno = -1;
+	struct kgdb_bkpt *bkpt;
+
+	for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+		if ((kgdb_break[i].state == BP_SET) &&
+		    (kgdb_break[i].bpt_addr == addr))
+			return -KGDBERR_BPEXIST;
+		if ((kgdb_break[i].state == BP_REMOVED) &&
+		    (kgdb_break[i].bpt_addr == addr)) {
+			breakno = i;
+			break;
+		}
+	}
+	if (breakno == -1)
+		for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+			if (kgdb_break[i].state == BP_UNDEFINED) {
+				breakno = i;
+				break;
+			}
+		}
+	if (breakno == -1)
+		return -KGDBERR_BPNOENT;
+
+	bkpt = kgdb_break + breakno;
+	bkpt->state = BP_SET;
+	bkpt->type = BP_BREAKPOINT;
+	bkpt->bpt_addr = addr;
+	arch_kgdb_set_sw_break(bkpt);
+
+	return 0;
+}
+
+static int kgdb_remove_sw_break(int addr)
+{
+	int i;
+	struct kgdb_bkpt *bkpt;
+
+	for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
+		bkpt = kgdb_break + i;
+		if ((bkpt->state == BP_SET) && (bkpt->bpt_addr == addr)) {
+			bkpt->state = BP_REMOVED;
+			arch_kgdb_remove_sw_break(bkpt);
+			return 0;
+		}
+	}
+	return -KGDBERR_BPNOENT;
+}
+
+/* Handle the 'z' or 'Z' breakpoint remove or set packets */
+static void gdb_cmd_break(kgdb_data *kdp)
+{
+	/*
+	 * Since GDB-5.3, it's been drafted that '0' is a software
+	 * breakpoint, '1' is a hardware breakpoint, so let's do that.
+	 */
+	char *bpt_type = &remcomInBuffer[1];
+	char *ptr = &remcomInBuffer[2];
+	int addr, length;
+
+	if (*bpt_type != '0')
+		return;		/* Unsupported. */
+	if (*ptr++ != ',' || !hexToInt(&ptr, &addr) ||
+	    *ptr++ != ',' || !hexToInt(&ptr, &length)) {
+		kgdb_error(KGDBERR_BADPARAMS);
+	}
+
+	if ((remcomInBuffer[0] == 'Z' && kgdb_set_sw_break(addr) == 0) ||
+	    (remcomInBuffer[0] == 'z' && kgdb_remove_sw_break(addr) == 0))
+		strcpy(remcomOutBuffer, "OK");
+}
+
 /* scan for the sequence $<data>#<checksum>     */
 static void
 getpacket(char *buffer)
@@ -341,7 +420,9 @@ handle_exception (struct pt_regs *regs)
 
 	kgdb_interruptible(0);
 
+#ifdef KGDB_DEBUG
 	printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs));
+#endif
 
 	if (kgdb_setjmp(error_jmp_buf) != 0)
 		panic("kgdb: error or fault in entry init!\n");
@@ -516,6 +597,10 @@ handle_exception (struct pt_regs *regs)
 				kgdb_error(KGDBERR_BADPARAMS);
 			}
 			break;
+		case 'Z': /* [Z|z]N,AA..AA,LLLL Set/Remove breakpoint type N */
+		case 'z': /*      LLLL bytes at address AA.AA return OK      */
+			gdb_cmd_break(&kd);
+			break;
 		}			/* switch */
 
 		if (errnum != 0)
diff --git a/common/kgdb_stubs.c b/common/kgdb_stubs.c
index 19b0c18..2b3f424 100644
--- a/common/kgdb_stubs.c
+++ b/common/kgdb_stubs.c
@@ -45,6 +45,18 @@ void kgdb_interruptible(int yes)
 }
 
 __attribute__((weak))
+void arch_kgdb_set_sw_break(struct kgdb_bkpt *bkpt)
+{
+	return;
+}
+
+__attribute__((weak))
+void arch_kgdb_remove_sw_break(struct kgdb_bkpt *bkpt)
+{
+	return;
+}
+
+__attribute__((weak))
 void kgdb_flush_cache_range(void *from, void *to)
 {
 	flush_cache((unsigned long)from, (unsigned long)(to - from));
diff --git a/include/kgdb.h b/include/kgdb.h
index f543cd6..82ae8ab 100644
--- a/include/kgdb.h
+++ b/include/kgdb.h
@@ -2,12 +2,41 @@
 #define __KGDB_H__
 
 #include <asm/ptrace.h>
+#include <asm/kgdb.h>
 
 #define KGDBERR_BADPARAMS	1
 #define KGDBERR_NOTHEXDIG	2
 #define KGDBERR_MEMFAULT	3
 #define KGDBERR_NOSPACE		4
 #define KGDBERR_ALIGNFAULT	5
+#define KGDBERR_BPEXIST		6
+#define KGDBERR_BPNOENT		7
+
+#ifndef KGDB_MAX_BREAKPOINTS
+#define KGDB_MAX_BREAKPOINTS	1000
+#endif
+
+enum kgdb_bptype {
+	BP_BREAKPOINT = 0,
+	BP_HARDWARE_BREAKPOINT,
+	BP_WRITE_WATCHPOINT,
+	BP_READ_WATCHPOINT,
+	BP_ACCESS_WATCHPOINT
+};
+
+enum kgdb_bpstate {
+	BP_UNDEFINED = 0,
+	BP_REMOVED,
+	BP_SET,
+	BP_ACTIVE
+};
+
+struct kgdb_bkpt {
+	unsigned long		bpt_addr;
+	unsigned char		saved_instr[BREAK_INSTR_SIZE];
+	enum kgdb_bptype	type;
+	enum kgdb_bpstate	state;
+};
 
 #define KGDBDATA_MAXREGS	8
 #define KGDBDATA_MAXPRIV	8
@@ -56,6 +85,8 @@ extern void kgdb_putreg(struct pt_regs *, int, char *, int);
 extern void kgdb_putregs(struct pt_regs *, char *, int);
 extern int kgdb_trap(struct pt_regs *);
 extern void kgdb_breakpoint(int argc, char *argv[]);
+extern void arch_kgdb_set_sw_break(struct kgdb_bkpt *);
+extern void arch_kgdb_remove_sw_break(struct kgdb_bkpt *);
 
 /* these functions are provided by the platform serial driver */
 extern void kgdb_serial_init(void);
-- 
1.6.0.6




More information about the U-Boot mailing list