[U-Boot] [PATCH] Add KGDB support for ARM platforms

Tonny Tzeng tonny.tzeng at gmail.com
Wed Apr 7 19:19:51 CEST 2010


These patches add kgdb support for ARM platforms.

The add and invocation of the bad_restore_user_regs macro in
cpu/arm720t/start.S should be made to other cpu/arm*/start.S
files as well.

Signed-off-by: Tonny Tzeng <tonny.tzeng at gmail.com>
---
 common/kgdb.c          |   30 ++++++
 cpu/arm720t/start.S    |    7 ++
 include/asm-arm/kgdb.h |   67 ++++++++++++
 include/kgdb.h         |   33 ++++++
 lib_arm/Makefile       |    1 +
 lib_arm/board.c        |    6 +
 lib_arm/interrupts.c   |   10 ++
 lib_arm/kgdb.c         |  262 ++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 416 insertions(+), 0 deletions(-)
 create mode 100644 include/asm-arm/kgdb.h
 create mode 100644 lib_arm/kgdb.c

diff --git a/common/kgdb.c b/common/kgdb.c
index 0531452..15ff6e8 100644
--- a/common/kgdb.c
+++ b/common/kgdb.c
@@ -220,6 +220,29 @@ hexToInt(char **ptr, int *intValue)
 	return (numChars);
 }
 
+/* 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 (kdp->hook_break &&
+	    !(*kdp->hook_break)(remcomInBuffer[0] == 'Z', addr))
+		strcpy(remcomOutBuffer, "OK");
+}
+
 /* scan for the sequence $<data>#<checksum>     */
 static void
 getpacket(char *buffer)
@@ -341,11 +364,14 @@ 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");
 
+	memset(&kd, 0, sizeof(kgdb_data));
 	kgdb_enter(regs, &kd);
 
 	if (first_entry) {
@@ -516,6 +542,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 LLLL bytes at address AA.AA return OK */
+		case 'z':
+			gdb_cmd_break(&kd);
+			break;
 		}			/* switch */
 
 		if (errnum != 0)
diff --git a/cpu/arm720t/start.S b/cpu/arm720t/start.S
index 022b873..a59e186 100644
--- a/cpu/arm720t/start.S
+++ b/cpu/arm720t/start.S
@@ -455,6 +455,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
@@ -506,6 +512,7 @@ undefined_instruction:
 	get_bad_stack
 	bad_save_user_regs
 	bl	do_undefined_instruction
+	bad_restore_user_regs
 
 	.align	5
 software_interrupt:
diff --git a/include/asm-arm/kgdb.h b/include/asm-arm/kgdb.h
new file mode 100644
index 0000000..c33254b
--- /dev/null
+++ b/include/asm-arm/kgdb.h
@@ -0,0 +1,67 @@
+/*
+ * ARM KGDB support
+ *
+ * Author: Deepak Saxena <dsaxena at mvista.com>
+ *
+ * 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__ */
+
diff --git a/include/kgdb.h b/include/kgdb.h
index f543cd6..b4c5d81 100644
--- a/include/kgdb.h
+++ b/include/kgdb.h
@@ -8,6 +8,38 @@
 #define KGDBERR_MEMFAULT	3
 #define KGDBERR_NOSPACE		4
 #define KGDBERR_ALIGNFAULT	5
+#define KGDBERR_BPEXIST		6
+#define KGDBERR_BPNOENT		7
+
+#ifndef BREAK_INSTR_SIZE
+#define BREAK_INSTR_SIZE	4
+#endif
+
+#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
@@ -35,6 +67,7 @@ typedef
 		int nregs;
 		kgdb_reg regs[KGDBDATA_MAXREGS];
 		unsigned long private[KGDBDATA_MAXPRIV];
+		int (*hook_break)(int set, int addr);
 	}
 kgdb_data;
 
diff --git a/lib_arm/Makefile b/lib_arm/Makefile
index 0293348..36fe528 100644
--- a/lib_arm/Makefile
+++ b/lib_arm/Makefile
@@ -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)
diff --git a/lib_arm/board.c b/lib_arm/board.c
index f5660a9..edc2120 100644
--- a/lib_arm/board.c
+++ b/lib_arm/board.c
@@ -39,6 +39,7 @@
  */
 
 #include <common.h>
+#include <watchdog.h>
 #include <command.h>
 #include <malloc.h>
 #include <stdio_dev.h>
@@ -381,6 +382,11 @@ void start_armboot (void)
 	/* miscellaneous platform dependent initialisations */
 	misc_init_r ();
 #endif
+#if defined(CONFIG_CMD_KGDB)
+	WATCHDOG_RESET();
+	puts("KGDB:  ");
+	kgdb_init();
+#endif
 
 	/* enable exceptions */
 	enable_interrupts ();
diff --git a/lib_arm/interrupts.c b/lib_arm/interrupts.c
index 1f2b815..d62b355 100644
--- a/lib_arm/interrupts.c
+++ b/lib_arm/interrupts.c
@@ -37,6 +37,10 @@
 
 #include <common.h>
 #include <asm/proc-armv/ptrace.h>
+#include <kgdb.h>
+#ifdef CONFIG_CMD_KGDB
+#include <asm/kgdb.h>
+#endif
 
 #ifdef CONFIG_USE_IRQ
 DECLARE_GLOBAL_DATA_PTR;
@@ -137,6 +141,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 ();
diff --git a/lib_arm/kgdb.c b/lib_arm/kgdb.c
new file mode 100644
index 0000000..b76b8ed
--- /dev/null
+++ b/lib_arm/kgdb.c
@@ -0,0 +1,262 @@
+/*
+ * Functions in this file are excerpt from the Linux KGDB
+ * supports in the following sources
+ *
+ * (1) linux/kernel/kgdb.c, contributed by:
+ *     David Grothe <dave at gcom.com>,
+ *     Tigran Aivazian <tigran at sco.com>
+ *     Jason Wessel ( jason.wessel at windriver.com )
+ *     George Anzinger <george at mvista.com>
+ *     Anurekh Saxena (anurekh.saxena at timesys.com)
+ *     Lake Stevens Instrument Division (Glenn Engel)
+ *     Jim Kingdon, Cygnus Support.
+ *
+ * (2) linux/arch/arm/kernel/kgdb.c
+ *     Copyright (c) 2002-2004 MontaVista Software, Inc
+ *     Copyright (c) 2008 Wind River Systems, Inc.
+ *     Authors:  George Davis <davis_g at mvista.com>
+ *               Deepak Saxena <dsaxena at plexity.net>
+ */
+#include <common.h>
+#include <asm-generic/signal.h>
+#include <asm/kgdb.h>
+#include <kgdb.h>
+
+/*
+ * kgdb_breakpoint - generate breakpoint exception
+ *
+ * This function will generate a breakpoint exception.  It is used at the
+ * beginning of a program to sync up with a debugger and can be used
+ * otherwise as a quick means to stop program execution and "break" into
+ * the debugger.
+ */
+void kgdb_breakpoint (int argc, char *argv[])
+{
+	arch_kgdb_breakpoint();
+}
+
+/*
+ * 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;
+	memcpy(bkpt->saved_instr, addr, BREAK_INSTR_SIZE);
+	*(unsigned long *)addr = KGDB_COMPILED_BREAK;
+
+	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;
+			memcpy(addr, bkpt->saved_instr, BREAK_INSTR_SIZE);
+			return 0;
+		}
+	}
+	return -KGDBERR_BPNOENT;
+}
+
+static int kgdb_sw_break(int set, int addr)
+{
+	return (set) ? kgdb_set_sw_break(addr) : kgdb_remove_sw_break(addr);
+}
+
+int kgdb_setjmp(long *buf)
+{
+	asm("	stmia	r0, {r0-r14}\n	\
+		str	lr,[r0, #60]\n	\
+		mrs	r1,cpsr\n	\
+		str	r1,[r0,#64]\n	\
+		ldr	r1,[r0,#4]\n	\
+	    ");
+	return 0;
+}
+
+void kgdb_longjmp(long *buf, int val)
+{
+	asm("	str	r1,[r0]\n	\
+		ldr     r1,[r0, #64]\n	\
+		msr     spsr,r1\n	\
+		ldmia	r0,{r0-pc}^\n	\
+	    ");
+}
+
+int kgdb_trap(struct pt_regs *regs)
+{
+	return SIGTRAP;
+}
+
+void kgdb_enter(struct pt_regs *regs, kgdb_data *kdp)
+{
+	kdp->sigval = kgdb_trap(regs);
+	kdp->nregs = 0;
+	kdp->hook_break = kgdb_sw_break;
+}
+
+void kgdb_exit(struct pt_regs *regs, kgdb_data *kdp)
+{
+}
+
+int kgdb_getregs(struct pt_regs *regs, char *buf, int max)
+{
+	int i;
+	unsigned long *gdb_regs = (unsigned long *)buf;
+
+	if (max < (GDB_MAX_REGS * sizeof(unsigned long)))
+		kgdb_error(KGDBERR_NOSPACE);
+
+	/* Initialize all to zero. */
+	for (i = 0; i < GDB_MAX_REGS; i++)
+		gdb_regs[i] = 0;
+
+	gdb_regs[_R0]   = regs->ARM_r0;
+	gdb_regs[_R1]   = regs->ARM_r1;
+	gdb_regs[_R2]   = regs->ARM_r2;
+	gdb_regs[_R3]   = regs->ARM_r3;
+	gdb_regs[_R4]   = regs->ARM_r4;
+	gdb_regs[_R5]   = regs->ARM_r5;
+	gdb_regs[_R6]   = regs->ARM_r6;
+	gdb_regs[_R7]   = regs->ARM_r7;
+	gdb_regs[_R8]   = regs->ARM_r8;
+	gdb_regs[_R9]   = regs->ARM_r9;
+	gdb_regs[_R10]  = regs->ARM_r10;
+	gdb_regs[_FP]   = regs->ARM_fp;
+	gdb_regs[_IP]   = regs->ARM_ip;
+	gdb_regs[_SPT]  = regs->ARM_sp;
+	gdb_regs[_LR]   = regs->ARM_lr;
+	gdb_regs[_PC]   = regs->ARM_pc;
+	gdb_regs[_CPSR] = regs->ARM_cpsr;
+
+	return GDB_MAX_REGS *sizeof(unsigned long);
+}
+
+void kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length)
+{
+	unsigned long val, *ptr = (unsigned long *)buf;
+
+	if (regno < 0 || GDB_MAX_REGS <= regno)
+		kgdb_error(KGDBERR_BADPARAMS);
+	else {
+		if (length < 4)
+			kgdb_error(KGDBERR_NOSPACE);
+	}
+	if ((unsigned long)ptr & 3)
+		kgdb_error(KGDBERR_ALIGNFAULT);
+	else
+		val = *ptr;
+
+	switch (regno) {
+	case _R0:
+		regs->ARM_r0 = val; break;
+	case _R1:
+		regs->ARM_r1 = val; break;
+	case _R2:
+		regs->ARM_r2 = val; break;
+	case _R3:
+		regs->ARM_r3 = val; break;
+	case _R4:
+		regs->ARM_r4 = val; break;
+	case _R5:
+		regs->ARM_r5 = val; break;
+	case _R6:
+		regs->ARM_r6 = val; break;
+	case _R7:
+		regs->ARM_r7 = val; break;
+	case _R8:
+		regs->ARM_r8 = val; break;
+	case _R9:
+		regs->ARM_r9 = val; break;
+	case _R10:
+		regs->ARM_r10 = val; break;
+	case _FP:
+		regs->ARM_fp = val; break;
+	case _IP:
+		regs->ARM_ip = val; break;
+	case _SPT:
+		regs->ARM_sp = val; break;
+	case _LR:
+		regs->ARM_lr = val; break;
+	case _PC:
+		regs->ARM_pc = val; break;
+	case 0x19:
+		regs->ARM_cpsr = val; break;
+	default:
+		kgdb_error(KGDBERR_BADPARAMS);
+	}
+}
+
+void kgdb_putregs(struct pt_regs *regs, char *buf, int length)
+{
+	unsigned long *gdb_regs = (unsigned long *)buf;
+
+	if (length < (GDB_MAX_REGS *sizeof(unsigned long)))
+		kgdb_error(KGDBERR_NOSPACE);
+
+	if ((unsigned long)gdb_regs & 3)
+		kgdb_error(KGDBERR_ALIGNFAULT);
+
+	regs->ARM_r0	= gdb_regs[_R0];
+	regs->ARM_r1	= gdb_regs[_R1];
+	regs->ARM_r2	= gdb_regs[_R2];
+	regs->ARM_r3	= gdb_regs[_R3];
+	regs->ARM_r4	= gdb_regs[_R4];
+	regs->ARM_r5	= gdb_regs[_R5];
+	regs->ARM_r6	= gdb_regs[_R6];
+	regs->ARM_r7	= gdb_regs[_R7];
+	regs->ARM_r8	= gdb_regs[_R8];
+	regs->ARM_r9	= gdb_regs[_R9];
+	regs->ARM_r10	= gdb_regs[_R10];
+	regs->ARM_fp	= gdb_regs[_FP];
+	regs->ARM_ip	= gdb_regs[_IP];
+	regs->ARM_sp	= gdb_regs[_SPT];
+	regs->ARM_lr	= gdb_regs[_LR];
+	regs->ARM_pc	= gdb_regs[_PC];
+	regs->ARM_cpsr	= gdb_regs[_CPSR];
+}
+
+void kgdb_interruptible(int yes)
+{
+#ifdef CONFIG_USE_IRQ
+	if (yes)
+		enable_interrupts();
+	else
+		disable_interrupts();
+#endif
+}
+
-- 
1.6.0.6





More information about the U-Boot mailing list