Skip to contentSkip to conversation

KGDB set / remove breakpoints

PATCH New Tonny Tzeng 3 participants 11 messages Apr 17, 2010 – Jun 28, 2010 Arm (32-bit)

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 KGDB set / remove breakpoints

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.

---
 common/kgdb.c       |   85 +++++++++++++++++++++++++++++++++++++++++++++++++++
 common/kgdb_stubs.c |   12 +++++++
 include/kgdb.h      |   31 ++++++++++++++++++
 3 files changed, 128 insertions(+), 0 deletions(-)
common/kgdb.c +85 -0
index 0531452..66378e5 100644
@@ -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)
common/kgdb_stubs.c +12 -0
index 19b0c18..2b3f424 100644
@@ -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));
include/kgdb.h +31 -0
index f543cd6..82ae8ab 100644
@@ -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);
Signature · 1 line
1.6.0.6
#1 lore raw

PATCH 1/1 KGDB set / remove breakpoints

Quoted 11 lines · On Saturday 17 April 2010 13:20:11 Tonny Tzeng wrote:

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.

i dont think breaking build is a good idea. i would have the code simply disable itself if BREAK_INSTR_SIZE isnt set.

common/kgdb.c hexToInt(char **ptr, int *intValue) · line 227

Quoted 5 lines
common/kgdb.c +5 -0
+/*
+ * Holds information about breakpoints in a kernel. These breakpoints are
+ * added and removed by gdb.
+ */
+static struct kgdb_bkpt	kgdb_break[KGDB_MAX_BREAKPOINTS];

use a space between the type and the name, not a tab

Quoted 5 lines
common/kgdb.c +5 -0
+static int kgdb_set_sw_break(int addr)
+{
+	int i, breakno = -1;
+	struct kgdb_bkpt *bkpt;
+

common/kgdb.c hexToInt(char **ptr, int *intValue) · line 243

Quoted 10 lines
common/kgdb.c +10 -0
+	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;
+		}
+	}

there are a few places that loop over the structure to find a bpt by its addr. would probably be better to write a helper function:
static int kgdb_find_break_by_addr(int addr)

{
	int i;

	for (i = 0; i < KGDB_MAX_BREAKPOINTS; ++i)
		if (kgdb_break[i].bpt_addr == addr)
			return i;

	return -1;
}

then this function becomes a lot simpler:

	breakno = kgdb_find_break_by_addr(addr);
	if (breakno != -1) {
		if (kgdb_break[breakno].state == BP_SET)
			return .....
		....
	} else {
		....

include/kgdb.h line 39

Quoted 10 lines
include/kgdb.h +9 -0
+#ifndef KGDB_MAX_BREAKPOINTS
+#define KGDB_MAX_BREAKPOINTS	1000
+#endif
 
+struct kgdb_bkpt {
+	unsigned long		bpt_addr;
+	unsigned char		saved_instr[BREAK_INSTR_SIZE];
+	enum kgdb_bptype	type;
+	enum kgdb_bpstate	state;
+};

the kgdb_bkpt is going to be about 16 bytes, and you're setting a default of 1000 ? that seems like an excessively large number that will simply waste memory (16k of it). please use something much smaller like say 10 or 20.

also, for struct packing, it'd be better if the saved_instr was at the end of the struct so it doesnt force an alignment hole in the middle.

the define also needs a CONFIG_ type prefix on it.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100417/fd869d8e/attachment.pgp

#2 lore raw

PATCH 1/1 KGDB set / remove breakpoints

Thanks for the review, Mike.

Quoted 7 lines
Quoted 4 lines

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.

i dont think breaking build is a good idea. ?i would have the code simply disable itself if BREAK_INSTR_SIZE isnt set.

That's why I used ifndef in the previous patch, and set BREAK_INSTR_SIZE to 4 if it isn't set, but I thought you prefer "an undefined size should be used as a tip that the arch doesnt yet support break points"?

In Linux kgdb code, there is an arch independent header file include/linux/kgdb.h, which includes arch dependent header asm/kgdb.h.
I am trying to include this concept here, unfortunately current
u-boot code only has linux/kgdb.h, if we want to define arch specific code in asm/kgdb.h, we may need to include asm/kgdb.h from linux/kgdb.h, so it looks to me it will definitely break other arch, do you have any suggestion for that?

Best Regards,
Tonny

#3 lore raw

PATCH 1/1 KGDB set / remove breakpoints

Quoted 5 lines · On Monday 19 April 2010 04:54:50 Tonny Tzeng wrote:
Quoted 5 lines
Quoted 4 lines

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.

Quoted 7 lines
Quoted 2 lines

i dont think breaking build is a good idea. i would have the code simply disable itself if BREAK_INSTR_SIZE isnt set.

That's why I used ifndef in the previous patch, and set BREAK_INSTR_SIZE to 4 if it isn't set, but I thought you prefer "an undefined size should be used as a tip that the arch doesnt yet support break points"?

i meant have the functions always return an error. have the header do:
#ifndef BREAK_INSTR_SIZE
#define BREAK_INSTR_SIZE 0
#endif

and then in gdb_cmd_break(), do something at the top like:
if (BREAK_INSTR_SIZE == 0)

	return;
Quoted 7 lines

In Linux kgdb code, there is an arch independent header file include/linux/kgdb.h, which includes arch dependent header asm/kgdb.h.
I am trying to include this concept here, unfortunately current
u-boot code only has linux/kgdb.h, if we want to define arch specific code in asm/kgdb.h, we may need to include asm/kgdb.h from linux/kgdb.h, so it looks to me it will definitely break other arch, do you have any suggestion for that?

just post a patch to add asm/kgdb.h stubs for the few arches that you dont implement yourself. i think only blackfin/i386/ppc support kgdb atm. -mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100420/e316eefb/attachment.pgp

#4 lore raw

v2 cover Add KGDB support for ARM platforms

In reply to Tonny Tzeng

This patchset adds KGDB support for ARM platforms.

Since the KGDB support for ARM is through the undef instruction handler, we need to add code to the startup file to restore the user context after exception. Unfortunately, each ARM variant has his own start.S, so please reference the patch designed for arm720 to add the 'bad_restore_user_regs' logic to your ARM start-up file.

v1 - initial patchset
v2 - (a) move code, which should be common to all arch, to common/kgdb.c;
add two new weak funcs to common/kgdb_stubs.c, and separate these common code to the patch "KGDB set / remove breakpoints".
(b) remove the watchdog func during the board initialization.
(c) flush all cache after mangling instructions.

Best Regards,
Tonny

#5 lore raw

PATCH v2 1/4 Add KGDB support for ARM platforms

---
 arch/arm/lib/board.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
arch/arm/lib/board.c +4 -0
index f5660a9..f2b028c 100644
@@ -381,6 +381,10 @@ void start_armboot (void)
 	/* miscellaneous platform dependent initialisations */
 	misc_init_r ();
 #endif
+#if defined(CONFIG_CMD_KGDB)
+	puts("KGDB:  ");
+	kgdb_init();
+#endif
 
 	/* enable exceptions */
 	enable_interrupts ();
Signature · 1 line
1.6.0.6
#6 lore raw

PATCH v2 2/4 Add KGDB support for ARM platforms

---
 arch/arm/lib/interrupts.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)
arch/arm/lib/interrupts.c +9 -0
index 1f2b815..6ee4309 100644
@@ -37,6 +37,9 @@
 
 #include <common.h>
 #include <asm/proc-armv/ptrace.h>
+#ifdef CONFIG_CMD_KGDB
+#include <kgdb.h>
+#endif
 
 #ifdef CONFIG_USE_IRQ
 DECLARE_GLOBAL_DATA_PTR;
@@ -137,6 +140,12 @@ void show_regs (struct pt_regs *regs)
 
 void do_undefined_instruction (struct pt_regs *pt_regs)
 {
+#ifdef CONFIG_CMD_KGDB
+	if (*(unsigned long *)(instruction_pointer(pt_regs) - 4) == KGDB_COMPILED_BREAK) {
+		(*debugger_exception_handler)(pt_regs);
+		return;
+	}
+#endif
 	printf ("undefined instruction\n");
 	show_regs (pt_regs);
 	bad_mode ();
Signature · 1 line
1.6.0.6
#7 lore raw

PATCH v2 3/4 Add KGDB support for ARM platforms

---
 arch/arm/include/asm/kgdb.h |   66 +++++++++++++++
 arch/arm/lib/Makefile       |    1 +
 arch/arm/lib/kgdb.c         |  194 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 261 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/kgdb.h
 create mode 100644 arch/arm/lib/kgdb.c
arch/arm/include/asm/kgdb.h new file +66 -0
new file mode 100644
index 0000000..dfca31c
@@ -0,0 +1,66 @@
+/*
+ * ARM KGDB support
+ *
+ * Author: Deepak Saxena <[email protected]>
+ *
+ * Copyright (C) 2002 MontaVista Software Inc.
+ *
+ */
+#ifndef __ARM_KGDB_H__
+#define __ARM_KGDB_H__
+
+#define BREAK_INSTR_SIZE	4
+#define KGDB_COMPILED_BREAK	0xe7ffdeff
+
+#ifndef	__ASSEMBLY__
+
+static inline void arch_kgdb_breakpoint(void)
+{
+	asm(".word 0xe7ffdeff");
+}
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * From Kevin Hilman:
+ *
+ * gdb is expecting the following registers layout.
+ *
+ * r0-r15: 1 long word each
+ * f0-f7:  unused, 3 long words each !!
+ * fps:    unused, 1 long word
+ * cpsr:   1 long word
+ *
+ * Even though f0-f7 and fps are not used, they need to be
+ * present in the registers sent for correct processing in
+ * the host-side gdb.
+ *
+ * In particular, it is crucial that CPSR is in the right place,
+ * otherwise gdb will not be able to correctly interpret stepping over
+ * conditional branches.
+ */
+#define _GP_REGS		16
+#define _FP_REGS		8
+#define _EXTRA_REGS		2
+#define GDB_MAX_REGS		(_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS)
+
+#define _R0			0
+#define _R1			1
+#define _R2			2
+#define _R3			3
+#define _R4			4
+#define _R5			5
+#define _R6			6
+#define _R7			7
+#define _R8			8
+#define _R9			9
+#define _R10			10
+#define _FP			11
+#define _IP			12
+#define _SPT			13
+#define _LR			14
+#define _PC			15
+#define _CPSR			(GDB_MAX_REGS - 1)
+
+#endif /* __ASM_KGDB_H__ */
+
arch/arm/lib/Makefile +1 -0
index 0293348..36fe528 100644
@@ -44,6 +44,7 @@ COBJS-y	+= cache-cp15.o
 endif
 COBJS-y	+= interrupts.o
 COBJS-y	+= reset.o
+COBJS-$(CONFIG_CMD_KGDB) += kgdb.o
 
 SRCS	:= $(GLSOBJS:.o=.S) $(GLCOBJS:.o=.c) \
 	   $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
arch/arm/lib/kgdb.c new file +194 -0

194 lines not on this page · raw message

Signature · 1 line
1.6.0.6
#8 lore raw

PATCH v2 4/4 Add KGDB support for ARM platforms

In reply to Tonny Tzeng
---
 arch/arm/cpu/arm720t/start.S |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/cpu/arm720t/start.S +7 -0
index 90b7240..fd06298 100644
@@ -459,6 +459,12 @@ lock_loop:
 	mov	r0, sp
 	.endm
 
+	.macro	bad_restore_user_regs
+	ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
+	msr	spsr_cxsf, lr
+	ldmia	sp, {r0 - pc}^			@ Restore SVC registers
+	.endm
+
 	.macro	irq_save_user_regs
 	sub	sp, sp, #S_FRAME_SIZE
 	stmia	sp, {r0 - r12}			@ Calling r0-r12
@@ -510,6 +516,7 @@ undefined_instruction:
 	get_bad_stack
 	bad_save_user_regs
 	bl	do_undefined_instruction
+	bad_restore_user_regs
 
 	.align	5
 software_interrupt:
Signature · 1 line
1.6.0.6
#9 lore raw

PATCH v2 2/4 Add KGDB support for ARM platforms

In reply to Tonny Tzeng

I was looking at using this code for Marvell's ARM-like platform. Was this patch ever accepted in uboot? I don't see it in the current git tree.

Thanks!
Ben Baron
Oracle Development Engineer.