Blackfin: add support for kgdb

Signed-off-by: Robin Getz <robin.getz@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
Robin Getz 2009-12-21 16:35:48 -05:00 committed by Mike Frysinger
parent 3869453f65
commit f19fd87e93
14 changed files with 982 additions and 12 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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 */

View file

@ -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

View file

@ -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

View file

@ -0,0 +1 @@
#include <asm-generic/signal.h>

View file

@ -83,6 +83,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__)

View file

@ -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

155
lib_blackfin/__kgdb.S Normal file
View file

@ -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)

View file

@ -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);

423
lib_blackfin/kgdb.c Normal file
View file

@ -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");
}

160
lib_blackfin/kgdb.h Normal file
View file

@ -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

View file

@ -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);
}