[U-Boot] [PATCH 5/5] Blackfin: add support for kgdb

Mike Frysinger vapier at gentoo.org
Tue Dec 22 00:40:46 CET 2009


From: Robin Getz <robin.getz at analog.com>

Signed-off-by: Robin Getz <robin.getz at analog.com>
Signed-off-by: Mike Frysinger <vapier at gentoo.org>
---
 cpu/blackfin/interrupt.S                     |  105 +++++++
 cpu/blackfin/traps.c                         |   57 ++++-
 include/asm-blackfin/config.h                |    7 +
 include/asm-blackfin/deferred.h              |   20 ++
 include/asm-blackfin/entry.h                 |   10 +
 include/asm-blackfin/mach-common/bits/core.h |    3 +
 include/asm-blackfin/signal.h                |    1 +
 include/configs/bfin_adi_common.h            |    1 +
 lib_blackfin/Makefile                        |    2 +
 lib_blackfin/__kgdb.S                        |  155 ++++++++++
 lib_blackfin/board.c                         |    6 +
 lib_blackfin/kgdb.c                          |  423 ++++++++++++++++++++++++++
 lib_blackfin/kgdb.h                          |  160 ++++++++++
 lib_blackfin/string.c                        |   44 +++-
 14 files changed, 982 insertions(+), 12 deletions(-)
 create mode 100644 include/asm-blackfin/deferred.h
 create mode 100644 include/asm-blackfin/signal.h
 create mode 100644 lib_blackfin/__kgdb.S
 create mode 100644 lib_blackfin/kgdb.c
 create mode 100644 lib_blackfin/kgdb.h

diff --git a/cpu/blackfin/interrupt.S b/cpu/blackfin/interrupt.S
index 8c7a262..69bba3f 100644
--- a/cpu/blackfin/interrupt.S
+++ b/cpu/blackfin/interrupt.S
@@ -8,6 +8,9 @@
 #include <config.h>
 #include <asm/blackfin.h>
 #include <asm/entry.h>
+#include <asm/ptrace.h>
+#include <asm/deferred.h>
+#include <asm/mach-common/bits/core.h>
 
 .text
 
@@ -19,10 +22,75 @@ ENTRY(_trap)
 	SAVE_ALL_SYS
 
 	r0 = sp;	/* stack frame pt_regs pointer argument ==> r0 */
+	r1 = 3;		/* EVT3 space */
 	sp += -12;
 	call _trap_c;
 	sp += 12;
 
+#ifdef CONFIG_EXCEPTION_DEFER
+	CC = R0 == 0;
+	IF CC JUMP .Lexit_trap;
+
+	/* To avoid double faults, lower our priority to IRQ5 */
+	p4.l = lo(COREMMR_BASE);
+	p4.h = hi(COREMMR_BASE);
+
+	r7.h = _exception_to_level5;
+	r7.l = _exception_to_level5;
+	[p4 + (EVT5 - COREMMR_BASE)] = r7;
+
+	/*
+	 * Save these registers, as they are only valid in exception context
+	 *  (where we are now - as soon as we defer to IRQ5, they can change)
+	 */
+	p5.l = _deferred_regs;
+	p5.h = _deferred_regs;
+	r6 = [p4 + (DCPLB_FAULT_ADDR - COREMMR_BASE)];
+	[p5 + (deferred_regs_DCPLB_FAULT_ADDR * 4)] = r6;
+
+	r6 = [p4 + (ICPLB_FAULT_ADDR - COREMMR_BASE)];
+	[p5 + (deferred_regs_ICPLB_FAULT_ADDR * 4)] = r6;
+
+	/* Save the state of single stepping */
+	r6 = SYSCFG;
+	[p5 + (deferred_regs_SYSCFG * 4)] = r6;
+	/* Clear it while we handle the exception in IRQ5 mode
+	 * RESTORE_ALL_SYS will load it, so all we need to do is store it
+	 * in the right place
+	 */
+	BITCLR(r6, SYSCFG_SSSTEP_P);
+	[SP + PT_SYSCFG] = r6;
+
+	/* Since we are going to clobber RETX, we need to save it */
+	r6 = retx;
+	[p5 + (deferred_regs_retx * 4)] = r6;
+
+	/* Save the current IMASK, since we change in order to jump to level 5 */
+	cli r6;
+	[p5 + (deferred_regs_IMASK * 4)] = r6;
+
+	/* Disable all interrupts, but make sure level 5 is enabled so
+	 * we can switch to that level.
+	 */
+	r6 = 0x3f;
+	sti r6;
+
+	/* Clobber RETX so we don't end up back at a faulting instruction */
+	[sp + PT_RETX] = r7;
+
+	/* In case interrupts are disabled IPEND[4] (global interrupt disable bit)
+	 * clear it (re-enabling interrupts again) by the special sequence of pushing
+	 * RETI onto the stack.  This way we can lower ourselves to IVG5 even if the
+	 * exception was taken after the interrupt handler was called but before it
+	 * got a chance to enable global interrupts itself.
+	 */
+	[--sp] = reti;
+	sp += 4;
+
+	RAISE 5;
+.Lexit_trap:
+#endif
+
 #if ANOMALY_05000257
 	R7  = LC0;
 	LC0 = R7;
@@ -35,6 +103,43 @@ ENTRY(_trap)
 	rtx;
 ENDPROC(_trap)
 
+#ifdef CONFIG_EXCEPTION_DEFER
+/* Deferred (IRQ5) exceptions */
+ENTRY(_exception_to_level5)
+	SAVE_ALL_SYS
+
+	/* Now we have to fix things up */
+	p4.l = lo(EVT5);
+	p4.h = hi(EVT5);
+	r0.l = _evt_default;
+	r0.h = _evt_default;
+	[p4] = r0;
+	csync;
+
+	p4.l = _deferred_regs;
+	p4.h = _deferred_regs;
+	r0 = [p4 + (deferred_regs_retx * 4)];
+	[sp + PT_PC] = r0;
+
+	r0 = [p4 + (deferred_regs_SYSCFG * 4)];
+	[sp + PT_SYSCFG] = r0;
+
+	r0 = sp;	/* stack frame pt_regs pointer argument ==> r0 */
+	r1 = 5;	/* EVT5 space */
+	sp += -12;
+	call _trap_c;
+	sp += 12;
+
+	/* Restore IMASK */
+	r0 = [p4 + (deferred_regs_IMASK * 4)];
+	sti r0;
+
+	RESTORE_ALL_SYS
+
+	rti;
+ENDPROC(_exception_to_level5)
+#endif
+
 /* default entry point for interrupts */
 ENTRY(_evt_default)
 	SAVE_ALL_SYS
diff --git a/cpu/blackfin/traps.c b/cpu/blackfin/traps.c
index becc36e..caaea94 100644
--- a/cpu/blackfin/traps.c
+++ b/cpu/blackfin/traps.c
@@ -18,6 +18,7 @@
  */
 
 #include <common.h>
+#include <kgdb.h>
 #include <linux/types.h>
 #include <asm/traps.h>
 #include <asm/cplb.h>
@@ -25,6 +26,7 @@
 #include <asm/mach-common/bits/core.h>
 #include <asm/mach-common/bits/mpu.h>
 #include <asm/mach-common/bits/trace.h>
+#include <asm/deferred.h>
 #include "cpu.h"
 
 #define trace_buffer_save(x) \
@@ -69,8 +71,16 @@ const struct memory_map const bfin_memory_map[] = {
 	}
 };
 
-void trap_c(struct pt_regs *regs)
+#ifdef CONFIG_EXCEPTION_DEFER
+unsigned int deferred_regs[deferred_regs_last];
+#endif
+
+/*
+ * Handle all exceptions while running in EVT3 or EVT5
+ */
+int trap_c(struct pt_regs *regs, uint32_t level)
 {
+	uint32_t ret = 0;
 	uint32_t trapnr = (regs->seqstat & EXCAUSE);
 	bool data = false;
 
@@ -87,7 +97,7 @@ void trap_c(struct pt_regs *regs)
 			 */
 			if (last_cplb_fault_retx != regs->retx) {
 				last_cplb_fault_retx = regs->retx;
-				return;
+				return ret;
 			}
 		}
 
@@ -102,6 +112,12 @@ void trap_c(struct pt_regs *regs)
 		size_t i;
 		unsigned long tflags;
 
+#ifdef CONFIG_EXCEPTION_DEFER
+		/* This should never happen */
+		if (level == 5)
+			bfin_panic(regs);
+#endif
+
 		/*
 		 * Keep the trace buffer so that a miss here points people
 		 * to the right place (their code).  Crashes here rarely
@@ -167,11 +183,40 @@ void trap_c(struct pt_regs *regs)
 		trace_buffer_restore(tflags);
 		break;
 	}
-
+#ifdef CONFIG_CMD_KGDB
+	/* Single step
+	 * if we are in IRQ5, just ignore, otherwise defer, and handle it in kgdb
+	 */
+	case VEC_STEP:
+		if (level == 3) {
+			/* If we just returned from an interrupt, the single step
+			 * event is for the RTI instruction.
+			 */
+			if (regs->retx == regs->pc)
+				break;
+			/* we just return if we are single stepping through IRQ5 */
+			if (regs->ipend & 0x20)
+				break;
+			/* Otherwise, turn single stepping off & fall through,
+			 * which defers to IRQ5
+			 */
+			regs->syscfg &= ~1;
+		}
+		/* fall through */
+#endif
 	default:
-		/* All traps come here */
+#ifdef CONFIG_CMD_KGDB
+		if (level == 3) {
+			/* We need to handle this at EVT5, so try again */
+			ret = 1;
+			break;
+		}
+		if (debugger_exception_handler && (*debugger_exception_handler)(regs))
+			return 0;
+#endif
 		bfin_panic(regs);
 	}
+	return ret;
 }
 
 #ifdef CONFIG_DEBUG_DUMP
@@ -251,8 +296,10 @@ void dump(struct pt_regs *fp)
 	if (!ENABLE_DUMP)
 		return;
 
-	/* fp->ipend is garbage, so load it ourself */
+#ifndef CONFIG_CMD_KGDB
+	/* fp->ipend is normally garbage, so load it ourself */
 	fp->ipend = bfin_read_IPEND();
+#endif
 
 	hwerrcause = (fp->seqstat & HWERRCAUSE) >> HWERRCAUSE_P;
 	excause = (fp->seqstat & EXCAUSE) >> EXCAUSE_P;
diff --git a/include/asm-blackfin/config.h b/include/asm-blackfin/config.h
index 0ae838a..7455685 100644
--- a/include/asm-blackfin/config.h
+++ b/include/asm-blackfin/config.h
@@ -66,6 +66,11 @@
 # error CONFIG_PLL_BYPASS: Invalid value: must be 0 or 1
 #endif
 
+/* If we are using KGDB, make sure we defer exceptions */
+#ifdef CONFIG_CMD_KGDB
+# define CONFIG_EXCEPTION_DEFER	1
+#endif
+
 /* Using L1 scratch pad makes sense for everyone by default. */
 #ifndef CONFIG_LINUX_CMDLINE_ADDR
 # define CONFIG_LINUX_CMDLINE_ADDR L1_SRAM_SCRATCH
@@ -138,6 +143,8 @@
 #endif
 #ifndef CONFIG_SYS_CBSIZE
 # define CONFIG_SYS_CBSIZE 1024
+#elif defined(CONFIG_CMD_KGDB) && CONFIG_SYS_CBSIZE < 1024
+# error "kgdb needs cbsize to be >= 1024"
 #endif
 #ifndef CONFIG_SYS_BARGSIZE
 # define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
diff --git a/include/asm-blackfin/deferred.h b/include/asm-blackfin/deferred.h
new file mode 100644
index 0000000..82ceda3
--- /dev/null
+++ b/include/asm-blackfin/deferred.h
@@ -0,0 +1,20 @@
+/*
+ * U-boot - deferred register layout
+ *
+ * Copyright 2004-2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _BLACKFIN_DEFER_H
+#define _BLACKFIN_DEFER_H
+
+#define deferred_regs_DCPLB_FAULT_ADDR	0
+#define deferred_regs_ICPLB_FAULT_ADDR	1
+#define deferred_regs_retx		2
+#define deferred_regs_SEQSTAT		3
+#define deferred_regs_SYSCFG		4
+#define deferred_regs_IMASK		5
+#define deferred_regs_last		6
+
+#endif /* _BLACKFIN_DEFER_H */
diff --git a/include/asm-blackfin/entry.h b/include/asm-blackfin/entry.h
index ef74d68..404144f 100644
--- a/include/asm-blackfin/entry.h
+++ b/include/asm-blackfin/entry.h
@@ -86,6 +86,11 @@
 	[--sp] = RETE;
 	[--sp] = SEQSTAT;
 	[--sp] = SYSCFG;
+#ifdef CONFIG_CMD_KGDB
+	p0.l = lo(IPEND)
+	p0.h = hi(IPEND)
+	r0 = [p0];
+#endif
 	[--sp] = r0;	/* Skip IPEND as well. */
 .endm
 
@@ -137,6 +142,11 @@
 	[--sp] = RETE;
 	[--sp] = SEQSTAT;
 	[--sp] = SYSCFG;
+#ifdef CONFIG_CMD_KGDB
+	p0.l = lo(IPEND)
+	p0.h = hi(IPEND)
+	r0 = [p0];
+#endif
 	[--sp] = r0;	/* Skip IPEND as well. */
 .endm
 
diff --git a/include/asm-blackfin/mach-common/bits/core.h b/include/asm-blackfin/mach-common/bits/core.h
index d8cee10..6db4f81 100644
--- a/include/asm-blackfin/mach-common/bits/core.h
+++ b/include/asm-blackfin/mach-common/bits/core.h
@@ -105,5 +105,8 @@
 #define SSSTEP			0x00000001	/* Supervisor Single Step */
 #define CCEN			0x00000002	/* Cycle Counter Enable */
 #define SNEN			0x00000004	/* Self-Nesting Interrupt Enable */
+#define SYSCFG_SSSTEP_P	0
+#define SYSCFG_CCEN_P	1
+#define SYSCFG_SCEN_P	2
 
 #endif
diff --git a/include/asm-blackfin/signal.h b/include/asm-blackfin/signal.h
new file mode 100644
index 0000000..7b1573c
--- /dev/null
+++ b/include/asm-blackfin/signal.h
@@ -0,0 +1 @@
+#include <asm-generic/signal.h>
diff --git a/include/configs/bfin_adi_common.h b/include/configs/bfin_adi_common.h
index 0da7940..91c5666 100644
--- a/include/configs/bfin_adi_common.h
+++ b/include/configs/bfin_adi_common.h
@@ -80,6 +80,7 @@
 # define CONFIG_CMD_CPLBINFO
 # define CONFIG_CMD_ELF
 # define CONFIG_ELF_SIMPLE_LOAD
+# define CONFIG_CMD_KGDB
 # define CONFIG_CMD_REGINFO
 # define CONFIG_CMD_STRINGS
 # if defined(__ADSPBF51x__) || defined(__ADSPBF52x__) || defined(__ADSPBF54x__)
diff --git a/lib_blackfin/Makefile b/lib_blackfin/Makefile
index eebb131..3bdba75 100644
--- a/lib_blackfin/Makefile
+++ b/lib_blackfin/Makefile
@@ -37,12 +37,14 @@ SOBJS-y	+= memcpy.o
 SOBJS-y	+= memmove.o
 SOBJS-y	+= memset.o
 SOBJS-y	+= outs.o
+SOBJS-$(CONFIG_CMD_KGDB) += __kgdb.o
 
 COBJS-y	+= board.o
 COBJS-y	+= boot.o
 COBJS-y	+= cache.o
 COBJS-y	+= clocks.o
 COBJS-$(CONFIG_CMD_CACHE_DUMP) += cmd_cache_dump.o
+COBJS-$(CONFIG_CMD_KGDB) += kgdb.o
 COBJS-y	+= muldi3.o
 COBJS-$(CONFIG_POST) += post.o tests.o
 COBJS-y	+= string.o
diff --git a/lib_blackfin/__kgdb.S b/lib_blackfin/__kgdb.S
new file mode 100644
index 0000000..cba4179
--- /dev/null
+++ b/lib_blackfin/__kgdb.S
@@ -0,0 +1,155 @@
+
+#include <asm/linkage.h>
+
+/* save stack context for non-local goto
+ * int kgdb_setjmp(long *buf)
+ */
+
+ENTRY(_kgdb_setjmp)
+	[--SP] = p0; 	/* Save P0 */
+	p0 = r0;
+	r0 = [SP++];	/* Load P0 into R0 */
+
+	[p0 + 0x00] = r0;       /* GP address registers */
+	[p0 + 0x04] = p1;
+	[p0 + 0x08] = p2;
+	[p0 + 0x0C] = p3;
+	[p0 + 0x10] = p4;
+	[p0 + 0x14] = p5;
+	[p0 + 0x18] = FP;       /* frame pointer */
+	[p0 + 0x1C] = SP;       /* stack pointer */
+
+	[p0 + 0x20] = p0;       /* data regs */
+	[p0 + 0x24] = r1;
+	[p0 + 0x28] = r2;
+	[p0 + 0x2C] = r3;
+	[p0 + 0x30] = r4;
+	[p0 + 0x34] = r5;
+	[p0 + 0x38] = r6;
+	[p0 + 0x3C] = r7;
+
+	r0 = ASTAT;	[p0 + 0x40] = r0;
+
+	/* loop counters */
+	r0 = LC0;	[p0 + 0x44] = r0;
+	r0 = LC1;	[p0 + 0x48] = r0;
+
+	/* Accumulator */
+	r0 = A0.w;	[p0 + 0x4C] = r0;
+	r0.l = A0.x;	[p0 + 0x50] = r0;
+	r0 = A1.w;	[p0 + 0x54] = r0;
+	r0.l = A1.x;	[p0 + 0x58] = r0;
+
+	/* index registers */
+	r0 = i0;	[p0 + 0x5C] = r0;
+	r0 = i1;	[p0 + 0x60] = r0;
+	r0 = i2;	[p0 + 0x64] = r0;
+	r0 = i3;	[p0 + 0x68] = r0;
+
+	/* modifier registers */
+	r0 = m0;	[p0 + 0x6C] = r0;
+	r0 = m1;	[p0 + 0x70] = r0;
+	r0 = m2;	[p0 + 0x74] = r0;
+	r0 = m3;	[p0 + 0x78] = r0;
+
+	/* length registers */
+	r0 = l0;	[p0 + 0x7C] = r0;
+	r0 = l1;	[p0 + 0x80] = r0;
+	r0 = l2;	[p0 + 0x84] = r0;
+	r0 = l3;	[p0 + 0x88] = r0;
+
+	/* base registers */
+	r0 = b0;	[p0 + 0x8C] = r0;
+	r0 = b1;	[p0 + 0x90] = r0;
+	r0 = b2;	[p0 + 0x94] = r0;
+	r0 = b3;	[p0 + 0x98] = r0;
+
+	/* store return address */
+	r0 = RETS;	[p0 + 0x9C] = r0;
+
+	R0 = 0;
+	RTS;
+ENDPROC(_kgdb_setjmp)
+
+/*
+ * non-local jump to a saved stack context
+ * longjmp(long *buf, int val)
+ */
+
+ENTRY(_kgdb_longjmp)
+	p0 = r0;
+	r0 = [p0 + 0x00];
+	[--sp] = r0;
+
+	/* GP address registers - skip p0 for now*/
+	p1 = [p0 + 0x04];
+	p2 = [p0 + 0x08];
+	p3 = [p0 + 0x0C];
+	p4 = [p0 + 0x10];
+	p5 = [p0 + 0x14];
+	/* frame pointer */
+	fp = [p0 + 0x18];
+	/* stack pointer */
+	r0 = [sp++];
+	sp = [p0 + 0x1C];
+	[--sp] = r0;
+	[--sp] = r1;
+
+	/* data regs */
+	r0 = [p0 + 0x20];
+	r1 = [p0 + 0x24];
+	r2 = [p0 + 0x28];
+	r3 = [p0 + 0x2C];
+	r4 = [p0 + 0x30];
+	r5 = [p0 + 0x34];
+	r6 = [p0 + 0x38];
+	r7 = [p0 + 0x3C];
+
+	r0 = [p0 + 0x40];	ASTAT = r0;
+
+	/* loop counters */
+	r0 = [p0 + 0x44];	LC0 = r0;
+	r0 = [p0 + 0x48];	LC1 = r0;
+
+	/* Accumulator */
+	r0 = [p0 + 0x4C];	A0.w = r0;
+	r0 = [p0 + 0x50];	A0.x = r0;
+	r0 = [p0 + 0x54];	A1.w = r0;
+	r0 = [p0 + 0x58];	A1.x = r0;
+
+	/* index registers */
+	r0 = [p0 + 0x5C];	i0 = r0;
+	r0 = [p0 + 0x60];	i1 = r0;
+	r0 = [p0 + 0x64];	i2 = r0;
+	r0 = [p0 + 0x68];	i3 = r0;
+
+	/* modifier registers */
+	r0 = [p0 + 0x6C];	m0 = r0;
+	r0 = [p0 + 0x70];	m1 = r0;
+	r0 = [p0 + 0x74];	m2 = r0;
+	r0 = [p0 + 0x78];	m3 = r0;
+
+	/* length registers */
+	r0 = [p0 + 0x7C];	l0 = r0;
+	r0 = [p0 + 0x80];	l1 = r0;
+	r0 = [p0 + 0x84];	l2 = r0;
+	r0 = [p0 + 0x88];	l3 = r0;
+
+	/* base registers */
+	r0 = [p0 + 0x8C];	b0 = r0;
+	r0 = [p0 + 0x90];	b1 = r0;
+	r0 = [p0 + 0x94];	b2 = r0;
+	r0 = [p0 + 0x98];	b3 = r0;
+
+	/* store return address */
+	r0 = [p0 + 0x9C];	RETS = r0;
+
+	/* fixup R0 & P0 */
+	r0 = [sp++];
+	p0 = [sp++];
+	CC = R0 == 0;
+	IF !CC JUMP .Lfinished;
+	R0 = 1;
+.Lfinished:
+	RTS;
+ENDPROC(_kgdb_longjmp)
diff --git a/lib_blackfin/board.c b/lib_blackfin/board.c
index 90da2b4..21fff33 100644
--- a/lib_blackfin/board.c
+++ b/lib_blackfin/board.c
@@ -22,6 +22,7 @@
 
 #include <asm/cplb.h>
 #include <asm/mach-common/bits/mpu.h>
+#include <kgdb.h>
 
 #ifdef CONFIG_CMD_NAND
 #include <nand.h>	/* cannot even include nand.h if it isnt configured */
@@ -356,6 +357,11 @@ void board_init_r(gd_t * id, ulong dest_addr)
 	/* Initialize the console (after the relocation and devices init) */
 	console_init_r();
 
+#ifdef CONFIG_CMD_KGDB
+	puts("KGDB:  ");
+	kgdb_init();
+#endif
+
 #ifdef CONFIG_STATUS_LED
 	status_led_set(STATUS_LED_BOOT, STATUS_LED_BLINKING);
 	status_led_set(STATUS_LED_CRASH, STATUS_LED_OFF);
diff --git a/lib_blackfin/kgdb.c b/lib_blackfin/kgdb.c
new file mode 100644
index 0000000..bd62d71
--- /dev/null
+++ b/lib_blackfin/kgdb.c
@@ -0,0 +1,423 @@
+/*
+ * U-boot - architecture specific kgdb code
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <common.h>
+#include <command.h>
+
+#include <kgdb.h>
+#include <asm/processor.h>
+#include <asm/mach-common/bits/core.h>
+#include "kgdb.h"
+#include <asm/deferred.h>
+#include <asm/traps.h>
+#include <asm/signal.h>
+
+void kgdb_enter(struct pt_regs *regs, kgdb_data *kdp)
+{
+	/* disable interrupts */
+	disable_interrupts();
+
+	/* reply to host that an exception has occurred */
+	kdp->sigval = kgdb_trap(regs);
+
+	/* send the PC and the Stack Pointer */
+	kdp->nregs = 2;
+	kdp->regs[0].num = BFIN_PC;
+	kdp->regs[0].val = regs->pc;
+
+	kdp->regs[1].num = BFIN_SP;
+	kdp->regs[1].val = (unsigned long)regs;
+
+}
+
+void kgdb_exit(struct pt_regs *regs, kgdb_data *kdp)
+{
+	if (kdp->extype & KGDBEXIT_WITHADDR)
+		printf("KGDBEXIT_WITHADDR\n");
+
+	switch (kdp->extype & KGDBEXIT_TYPEMASK) {
+	case KGDBEXIT_KILL:
+		printf("KGDBEXIT_KILL:\n");
+		break;
+	case KGDBEXIT_CONTINUE:
+		/* Make sure the supervisor single step bit is clear */
+		regs->syscfg &= ~1;
+		break;
+	case KGDBEXIT_SINGLE:
+		/* set the supervisor single step bit */
+		regs->syscfg |= 1;
+		break;
+	default:
+		printf("KGDBEXIT : %d\n", kdp->extype);
+	}
+
+	/* enable interrupts */
+	enable_interrupts();
+}
+
+int kgdb_trap(struct pt_regs *regs)
+{
+	/* ipend doesn't get filled in properly */
+	switch (regs->seqstat & EXCAUSE) {
+	case VEC_EXCPT01:
+		return SIGTRAP;
+	case VEC_EXCPT03:
+		return SIGSEGV;
+	case VEC_EXCPT02:
+		return SIGTRAP;
+	case VEC_EXCPT04 ... VEC_EXCPT15:
+		return SIGILL;
+	case VEC_STEP:
+		return SIGTRAP;
+	case VEC_OVFLOW:
+		return SIGTRAP;
+	case VEC_UNDEF_I:
+		return SIGILL;
+	case VEC_ILGAL_I:
+		return SIGILL;
+	case VEC_CPLB_VL:
+		return SIGSEGV;
+	case VEC_MISALI_D:
+		return SIGBUS;
+	case VEC_UNCOV:
+		return SIGILL;
+	case VEC_CPLB_MHIT:
+		return SIGSEGV;
+	case VEC_MISALI_I:
+		return SIGBUS;
+	case VEC_CPLB_I_VL:
+		return SIGBUS;
+	case VEC_CPLB_I_MHIT:
+		return SIGSEGV;
+	default:
+		return SIGBUS;
+	}
+}
+
+/*
+ * getregs - gets the pt_regs, and gives them to kgdb's buffer
+ */
+int kgdb_getregs(struct pt_regs *regs, char *buf, int max)
+{
+	unsigned long *gdb_regs = (unsigned long *)buf;
+
+	if (max < NUMREGBYTES)
+		kgdb_error(KGDBERR_NOSPACE);
+
+	if ((unsigned long)gdb_regs & 3)
+		kgdb_error(KGDBERR_ALIGNFAULT);
+
+	gdb_regs[BFIN_R0] = regs->r0;
+	gdb_regs[BFIN_R1] = regs->r1;
+	gdb_regs[BFIN_R2] = regs->r2;
+	gdb_regs[BFIN_R3] = regs->r3;
+	gdb_regs[BFIN_R4] = regs->r4;
+	gdb_regs[BFIN_R5] = regs->r5;
+	gdb_regs[BFIN_R6] = regs->r6;
+	gdb_regs[BFIN_R7] = regs->r7;
+	gdb_regs[BFIN_P0] = regs->p0;
+	gdb_regs[BFIN_P1] = regs->p1;
+	gdb_regs[BFIN_P2] = regs->p2;
+	gdb_regs[BFIN_P3] = regs->p3;
+	gdb_regs[BFIN_P4] = regs->p4;
+	gdb_regs[BFIN_P5] = regs->p5;
+	gdb_regs[BFIN_SP] = (unsigned long)regs;
+	gdb_regs[BFIN_FP] = regs->fp;
+	gdb_regs[BFIN_I0] = regs->i0;
+	gdb_regs[BFIN_I1] = regs->i1;
+	gdb_regs[BFIN_I2] = regs->i2;
+	gdb_regs[BFIN_I3] = regs->i3;
+	gdb_regs[BFIN_M0] = regs->m0;
+	gdb_regs[BFIN_M1] = regs->m1;
+	gdb_regs[BFIN_M2] = regs->m2;
+	gdb_regs[BFIN_M3] = regs->m3;
+	gdb_regs[BFIN_B0] = regs->b0;
+	gdb_regs[BFIN_B1] = regs->b1;
+	gdb_regs[BFIN_B2] = regs->b2;
+	gdb_regs[BFIN_B3] = regs->b3;
+	gdb_regs[BFIN_L0] = regs->l0;
+	gdb_regs[BFIN_L1] = regs->l1;
+	gdb_regs[BFIN_L2] = regs->l2;
+	gdb_regs[BFIN_L3] = regs->l3;
+	gdb_regs[BFIN_A0_DOT_X] = regs->a0x;
+	gdb_regs[BFIN_A0_DOT_W] = regs->a0w;
+	gdb_regs[BFIN_A1_DOT_X] = regs->a1x;
+	gdb_regs[BFIN_A1_DOT_W] = regs->a1w;
+	gdb_regs[BFIN_ASTAT] = regs->astat;
+	gdb_regs[BFIN_RETS] = regs->rets;
+	gdb_regs[BFIN_LC0] = regs->lc0;
+	gdb_regs[BFIN_LT0] = regs->lt0;
+	gdb_regs[BFIN_LB0] = regs->lb0;
+	gdb_regs[BFIN_LC1] = regs->lc1;
+	gdb_regs[BFIN_LT1] = regs->lt1;
+	gdb_regs[BFIN_LB1] = regs->lb1;
+	gdb_regs[BFIN_CYCLES] = 0;
+	gdb_regs[BFIN_CYCLES2] = 0;
+	gdb_regs[BFIN_USP] = regs->usp;
+	gdb_regs[BFIN_SEQSTAT] = regs->seqstat;
+	gdb_regs[BFIN_SYSCFG] = regs->syscfg;
+	gdb_regs[BFIN_RETI] = regs->pc;
+	gdb_regs[BFIN_RETX] = regs->retx;
+	gdb_regs[BFIN_RETN] = regs->retn;
+	gdb_regs[BFIN_RETE] = regs->rete;
+	gdb_regs[BFIN_PC] = regs->pc;
+	gdb_regs[BFIN_CC] = 0;
+	gdb_regs[BFIN_EXTRA1] = 0;
+	gdb_regs[BFIN_EXTRA2] = 0;
+	gdb_regs[BFIN_EXTRA3] = 0;
+	gdb_regs[BFIN_IPEND] = regs->ipend;
+
+	return NUMREGBYTES;
+}
+
+/*
+ * putreg - put kgdb's reg (regno) into the pt_regs
+ */
+void kgdb_putreg(struct pt_regs *regs, int regno, char *buf, int length)
+{
+	unsigned long *ptr = (unsigned long *)buf;
+
+	if (regno < 0 || regno > BFIN_NUM_REGS)
+		kgdb_error(KGDBERR_BADPARAMS);
+
+	if (length < 4)
+		kgdb_error(KGDBERR_NOSPACE);
+
+	if ((unsigned long)ptr & 3)
+		kgdb_error(KGDBERR_ALIGNFAULT);
+
+	switch (regno) {
+	case BFIN_R0:
+		regs->r0 = *ptr;
+		break;
+	case BFIN_R1:
+		regs->r1 = *ptr;
+		break;
+	case BFIN_R2:
+		regs->r2 = *ptr;
+		break;
+	case BFIN_R3:
+		regs->r3 = *ptr;
+		break;
+	case BFIN_R4:
+		regs->r4 = *ptr;
+		break;
+	case BFIN_R5:
+		regs->r5 = *ptr;
+		break;
+	case BFIN_R6:
+		regs->r6 = *ptr;
+		break;
+	case BFIN_R7:
+		regs->r7 = *ptr;
+		break;
+	case BFIN_P0:
+		regs->p0 = *ptr;
+		break;
+	case BFIN_P1:
+		regs->p1 = *ptr;
+		break;
+	case BFIN_P2:
+		regs->p2 = *ptr;
+		break;
+	case BFIN_P3:
+		regs->p3 = *ptr;
+		break;
+	case BFIN_P4:
+		regs->p4 = *ptr;
+		break;
+	case BFIN_P5:
+		regs->p5 = *ptr;
+		break;
+	case BFIN_SP:
+		regs->reserved = *ptr;
+		break;
+	case BFIN_FP:
+		regs->fp = *ptr;
+		break;
+	case BFIN_I0:
+		regs->i0 = *ptr;
+		break;
+	case BFIN_I1:
+		regs->i1 = *ptr;
+		break;
+	case BFIN_I2:
+		regs->i2 = *ptr;
+		break;
+	case BFIN_I3:
+		regs->i3 = *ptr;
+		break;
+	case BFIN_M0:
+		regs->m0 = *ptr;
+		break;
+	case BFIN_M1:
+		regs->m1 = *ptr;
+		break;
+	case BFIN_M2:
+		regs->m2 = *ptr;
+		break;
+	case BFIN_M3:
+		regs->m3 = *ptr;
+		break;
+	case BFIN_B0:
+		regs->b0 = *ptr;
+		break;
+	case BFIN_B1:
+		regs->b1 = *ptr;
+		break;
+	case BFIN_B2:
+		regs->b2 = *ptr;
+		break;
+	case BFIN_B3:
+		regs->b3 = *ptr;
+		break;
+	case BFIN_L0:
+		regs->l0 = *ptr;
+		break;
+	case BFIN_L1:
+		regs->l1 = *ptr;
+		break;
+	case BFIN_L2:
+		regs->l2 = *ptr;
+		break;
+	case BFIN_L3:
+		regs->l3 = *ptr;
+		break;
+	case BFIN_A0_DOT_X:
+		regs->a0x = *ptr;
+		break;
+	case BFIN_A0_DOT_W:
+		regs->a0w = *ptr;
+		break;
+	case BFIN_A1_DOT_X:
+		regs->a1x = *ptr;
+		break;
+	case BFIN_A1_DOT_W:
+		regs->a1w = *ptr;
+		break;
+	case BFIN_ASTAT:
+		regs->astat = *ptr;
+		break;
+	case BFIN_RETS:
+		regs->rets = *ptr;
+		break;
+	case BFIN_LC0:
+		regs->lc0 = *ptr;
+		break;
+	case BFIN_LT0:
+		regs->lt0 = *ptr;
+		break;
+	case BFIN_LB0:
+		regs->lb0 = *ptr;
+		break;
+	case BFIN_LC1:
+		regs->lc1 = *ptr;
+		break;
+	case BFIN_LT1:
+		regs->lt1 = *ptr;
+		break;
+	case BFIN_LB1:
+		regs->lb1 = *ptr;
+		break;
+/*
+  BFIN_CYCLES,
+  BFIN_CYCLES2,
+  BFIN_USP,
+  BFIN_SEQSTAT,
+  BFIN_SYSCFG,
+*/
+	case BFIN_RETX:
+		regs->retx = *ptr;
+		break;
+	case BFIN_RETN:
+		regs->retn = *ptr;
+		break;
+	case BFIN_RETE:
+		regs->rete = *ptr;
+		break;
+	case BFIN_PC:
+		regs->pc = *ptr;
+		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 != BFIN_NUM_REGS)
+		kgdb_error(KGDBERR_NOSPACE);
+
+	if ((unsigned long)gdb_regs & 3)
+		kgdb_error(KGDBERR_ALIGNFAULT);
+
+	regs->r0 = gdb_regs[BFIN_R0];
+	regs->r1 = gdb_regs[BFIN_R1];
+	regs->r2 = gdb_regs[BFIN_R2];
+	regs->r3 = gdb_regs[BFIN_R3];
+	regs->r4 = gdb_regs[BFIN_R4];
+	regs->r5 = gdb_regs[BFIN_R5];
+	regs->r6 = gdb_regs[BFIN_R6];
+	regs->r7 = gdb_regs[BFIN_R7];
+	regs->p0 = gdb_regs[BFIN_P0];
+	regs->p1 = gdb_regs[BFIN_P1];
+	regs->p2 = gdb_regs[BFIN_P2];
+	regs->p3 = gdb_regs[BFIN_P3];
+	regs->p4 = gdb_regs[BFIN_P4];
+	regs->p5 = gdb_regs[BFIN_P5];
+	regs->fp = gdb_regs[BFIN_FP];
+/*	regs->sp = gdb_regs[BFIN_ ]; */
+	regs->i0 = gdb_regs[BFIN_I0];
+	regs->i1 = gdb_regs[BFIN_I1];
+	regs->i2 = gdb_regs[BFIN_I2];
+	regs->i3 = gdb_regs[BFIN_I3];
+	regs->m0 = gdb_regs[BFIN_M0];
+	regs->m1 = gdb_regs[BFIN_M1];
+	regs->m2 = gdb_regs[BFIN_M2];
+	regs->m3 = gdb_regs[BFIN_M3];
+	regs->b0 = gdb_regs[BFIN_B0];
+	regs->b1 = gdb_regs[BFIN_B1];
+	regs->b2 = gdb_regs[BFIN_B2];
+	regs->b3 = gdb_regs[BFIN_B3];
+	regs->l0 = gdb_regs[BFIN_L0];
+	regs->l1 = gdb_regs[BFIN_L1];
+	regs->l2 = gdb_regs[BFIN_L2];
+	regs->l3 = gdb_regs[BFIN_L3];
+	regs->a0x = gdb_regs[BFIN_A0_DOT_X];
+	regs->a0w = gdb_regs[BFIN_A0_DOT_W];
+	regs->a1x = gdb_regs[BFIN_A1_DOT_X];
+	regs->a1w = gdb_regs[BFIN_A1_DOT_W];
+	regs->rets = gdb_regs[BFIN_RETS];
+	regs->lc0 = gdb_regs[BFIN_LC0];
+	regs->lt0 = gdb_regs[BFIN_LT0];
+	regs->lb0 = gdb_regs[BFIN_LB0];
+	regs->lc1 = gdb_regs[BFIN_LC1];
+	regs->lt1 = gdb_regs[BFIN_LT1];
+	regs->lb1 = gdb_regs[BFIN_LB1];
+	regs->usp = gdb_regs[BFIN_USP];
+	regs->syscfg = gdb_regs[BFIN_SYSCFG];
+	regs->retx = gdb_regs[BFIN_PC];
+	regs->retn = gdb_regs[BFIN_RETN];
+	regs->rete = gdb_regs[BFIN_RETE];
+	regs->pc = gdb_regs[BFIN_PC];
+
+#if 0	/* can't change these */
+	regs->astat = gdb_regs[BFIN_ASTAT];
+	regs->seqstat = gdb_regs[BFIN_SEQSTAT];
+	regs->ipend = gdb_regs[BFIN_IPEND];
+#endif
+
+}
+
+void kgdb_breakpoint(int argc, char *argv[])
+{
+	asm volatile ("excpt 0x1\n");
+}
diff --git a/lib_blackfin/kgdb.h b/lib_blackfin/kgdb.h
new file mode 100644
index 0000000..18f1f49
--- /dev/null
+++ b/lib_blackfin/kgdb.h
@@ -0,0 +1,160 @@
+/* Blackfin KGDB header
+ *
+ * Copyright 2005-2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __ASM_BLACKFIN_KGDB_H__
+#define __ASM_BLACKFIN_KGDB_H__
+
+/* gdb locks */
+#define KGDB_MAX_NO_CPUS 8
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers.
+ * At least NUMREGBYTES*2 are needed for register packets.
+ * Longer buffer is needed to list all threads.
+ */
+#define BUFMAX 2048
+
+enum regnames {
+  /* Core Registers */
+  BFIN_R0 = 0,
+  BFIN_R1,
+  BFIN_R2,
+  BFIN_R3,
+  BFIN_R4,
+  BFIN_R5,
+  BFIN_R6,
+  BFIN_R7,
+  BFIN_P0,
+  BFIN_P1,
+  BFIN_P2,
+  BFIN_P3,
+  BFIN_P4,
+  BFIN_P5,
+  BFIN_SP,
+  BFIN_FP,
+  BFIN_I0,
+  BFIN_I1,
+  BFIN_I2,
+  BFIN_I3,
+  BFIN_M0,
+  BFIN_M1,
+  BFIN_M2,
+  BFIN_M3,
+  BFIN_B0,
+  BFIN_B1,
+  BFIN_B2,
+  BFIN_B3,
+  BFIN_L0,
+  BFIN_L1,
+  BFIN_L2,
+  BFIN_L3,
+  BFIN_A0_DOT_X,
+  BFIN_A0_DOT_W,
+  BFIN_A1_DOT_X,
+  BFIN_A1_DOT_W,
+  BFIN_ASTAT,
+  BFIN_RETS,
+  BFIN_LC0,
+  BFIN_LT0,
+  BFIN_LB0,
+  BFIN_LC1,
+  BFIN_LT1,
+  BFIN_LB1,
+  BFIN_CYCLES,
+  BFIN_CYCLES2,
+  BFIN_USP,
+  BFIN_SEQSTAT,
+  BFIN_SYSCFG,
+  BFIN_RETI,
+  BFIN_RETX,
+  BFIN_RETN,
+  BFIN_RETE,
+
+  /* Pseudo Registers */
+  BFIN_PC,
+  BFIN_CC,
+  BFIN_EXTRA1,		/* Address of .text section.  */
+  BFIN_EXTRA2,		/* Address of .data section.  */
+  BFIN_EXTRA3,		/* Address of .bss section.  */
+  BFIN_FDPIC_EXEC,
+  BFIN_FDPIC_INTERP,
+
+  /* MMRs */
+  BFIN_IPEND,
+
+  /* LAST ENTRY SHOULD NOT BE CHANGED.  */
+  BFIN_NUM_REGS		/* The number of all registers.  */
+};
+
+/* Number of bytes of registers.  */
+#define NUMREGBYTES (BFIN_NUM_REGS * 4)
+
+static inline void arch_kgdb_breakpoint(void)
+{
+	asm volatile ("EXCPT 2;");
+}
+#define BREAK_INSTR_SIZE	2
+#define CACHE_FLUSH_IS_SAFE	1
+#define GDB_ADJUSTS_BREAK_OFFSET
+#define GDB_SKIP_HW_WATCH_TEST
+#define HW_INST_WATCHPOINT_NUM	6
+#define HW_WATCHPOINT_NUM	8
+#define TYPE_INST_WATCHPOINT	0
+#define TYPE_DATA_WATCHPOINT	1
+
+/* Instruction watchpoint address control register bits mask */
+#define WPPWR		0x1
+#define WPIREN01	0x2
+#define WPIRINV01	0x4
+#define WPIAEN0		0x8
+#define WPIAEN1		0x10
+#define WPICNTEN0	0x20
+#define WPICNTEN1	0x40
+#define EMUSW0		0x80
+#define EMUSW1		0x100
+#define WPIREN23	0x200
+#define WPIRINV23	0x400
+#define WPIAEN2		0x800
+#define WPIAEN3		0x1000
+#define WPICNTEN2	0x2000
+#define WPICNTEN3	0x4000
+#define EMUSW2		0x8000
+#define EMUSW3		0x10000
+#define WPIREN45	0x20000
+#define WPIRINV45	0x40000
+#define WPIAEN4		0x80000
+#define WPIAEN5		0x100000
+#define WPICNTEN4	0x200000
+#define WPICNTEN5	0x400000
+#define EMUSW4		0x800000
+#define EMUSW5		0x1000000
+#define WPAND		0x2000000
+
+/* Data watchpoint address control register bits mask */
+#define WPDREN01	0x1
+#define WPDRINV01	0x2
+#define WPDAEN0		0x4
+#define WPDAEN1		0x8
+#define WPDCNTEN0	0x10
+#define WPDCNTEN1	0x20
+
+#define WPDSRC0		0xc0
+#define WPDACC0_OFFSET	8
+#define WPDSRC1		0xc00
+#define WPDACC1_OFFSET	12
+
+/* Watchpoint status register bits mask */
+#define STATIA0		0x1
+#define STATIA1		0x2
+#define STATIA2		0x4
+#define STATIA3		0x8
+#define STATIA4		0x10
+#define STATIA5		0x20
+#define STATDA0		0x40
+#define STATDA1		0x80
+
+#endif
diff --git a/lib_blackfin/string.c b/lib_blackfin/string.c
index 12b6d24..e344d3b 100644
--- a/lib_blackfin/string.c
+++ b/lib_blackfin/string.c
@@ -230,15 +230,45 @@ void *memcpy(void *dst, const void *src, size_t count)
 	if (!count)
 		return dst;
 
-	if (addr_bfin_on_chip_mem(dst)) {
-		/* L1 is the destination */
-		return dma_memcpy(dst, src, count);
+#ifdef CONFIG_CMD_KGDB
+	if (src >= (void *)SYSMMR_BASE) {
+		if (count == 2 && (unsigned long)src % 2 == 0) {
+			u16 mmr = bfin_read16(src);
+			memcpy(dst, &mmr, sizeof(mmr));
+			return dst;
+		}
+		if (count == 4 && (unsigned long)src % 4 == 0) {
+			u32 mmr = bfin_read32(src);
+			memcpy(dst, &mmr, sizeof(mmr));
+			return dst;
+		}
+		/* Failed for some reason */
+		memset(dst, 0xad, count);
+		return dst;
+	}
+	if (dst >= (void *)SYSMMR_BASE) {
+		if (count == 2 && (unsigned long)dst % 2 == 0) {
+			u16 mmr;
+			memcpy(&mmr, src, sizeof(mmr));
+			bfin_write16(dst, mmr);
+			return dst;
+		}
+		if (count == 4 && (unsigned long)dst % 4 == 0) {
+			u32 mmr;
+			memcpy(&mmr, src, sizeof(mmr));
+			bfin_write32(dst, mmr);
+			return dst;
+		}
+		/* Failed for some reason */
+		memset(dst, 0xad, count);
+		return dst;
+	}
+#endif
 
-	} else if (addr_bfin_on_chip_mem(src)) {
-		/* L1 is the source */
+	/* if L1 is the source or dst, use DMA */
+	if (addr_bfin_on_chip_mem(dst) || addr_bfin_on_chip_mem(src))
 		return dma_memcpy(dst, src, count);
-
-	} else
+	else
 		/* No L1 is involved, so just call regular memcpy */
 		return memcpy_ASM(dst, src, count);
 }
-- 
1.6.5.4



More information about the U-Boot mailing list