mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
Blackfin arch: kgdb specific code
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> Signed-off-by: Bryan Wu <bryan.wu@analog.com>
This commit is contained in:
parent
1c5d2265a8
commit
474f1a667d
155
Documentation/blackfin/kgdb.txt
Normal file
155
Documentation/blackfin/kgdb.txt
Normal file
@ -0,0 +1,155 @@
|
||||
A Simple Guide to Configure KGDB
|
||||
|
||||
Sonic Zhang <sonic.zhang@analog.com>
|
||||
Aug. 24th 2006
|
||||
|
||||
|
||||
This KGDB patch enables the kernel developer to do source level debugging on
|
||||
the kernel for the Blackfin architecture. The debugging works over either the
|
||||
ethernet interface or one of the uarts. Both software breakpoints and
|
||||
hardware breakpoints are supported in this version.
|
||||
http://docs.blackfin.uclinux.org/doku.php?id=kgdb
|
||||
|
||||
|
||||
2 known issues:
|
||||
1. This bug:
|
||||
http://blackfin.uclinux.org/tracker/index.php?func=detail&aid=544&group_id=18&atid=145
|
||||
The GDB client for Blackfin uClinux causes incorrect values of local
|
||||
variables to be displayed when the user breaks the running of kernel in GDB.
|
||||
2. Because of a hardware bug in Blackfin 533 v1.0.3:
|
||||
05000067 - Watchpoints (Hardware Breakpoints) are not supported
|
||||
Hardware breakpoints cannot be set properly.
|
||||
|
||||
|
||||
Debug over Ethernet:
|
||||
|
||||
1. Compile and install the cross platform version of gdb for blackfin, which
|
||||
can be found at $(BINROOT)/bfin-elf-gdb.
|
||||
|
||||
2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under
|
||||
"Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
|
||||
With this selected, option "Full Symbolic/Source Debugging support" and
|
||||
"Compile the kernel with frame pointers" are also selected.
|
||||
|
||||
3. Select option "KGDB: connect over (Ethernet)". Add "kgdboe=@target-IP/,@host-IP/" to
|
||||
the option "Compiled-in Kernel Boot Parameter" under "Kernel hacking".
|
||||
|
||||
4. Connect minicom to the serial port and boot the kernel image.
|
||||
|
||||
5. Configure the IP "/> ifconfig eth0 target-IP"
|
||||
|
||||
6. Start GDB client "bfin-elf-gdb vmlinux".
|
||||
|
||||
7. Connect to the target "(gdb) target remote udp:target-IP:6443".
|
||||
|
||||
8. Set software breakpoint "(gdb) break sys_open".
|
||||
|
||||
9. Continue "(gdb) c".
|
||||
|
||||
10. Run ls in the target console "/> ls".
|
||||
|
||||
11. Breakpoint hits. "Breakpoint 1: sys_open(..."
|
||||
|
||||
12. Display local variables and function paramters.
|
||||
(*) This operation gives wrong results, see known issue 1.
|
||||
|
||||
13. Single stepping "(gdb) si".
|
||||
|
||||
14. Remove breakpoint 1. "(gdb) del 1"
|
||||
|
||||
15. Set hardware breakpoint "(gdb) hbreak sys_open".
|
||||
|
||||
16. Continue "(gdb) c".
|
||||
|
||||
17. Run ls in the target console "/> ls".
|
||||
|
||||
18. Hardware breakpoint hits. "Breakpoint 1: sys_open(...".
|
||||
(*) This hardware breakpoint will not be hit, see known issue 2.
|
||||
|
||||
19. Continue "(gdb) c".
|
||||
|
||||
20. Interrupt the target in GDB "Ctrl+C".
|
||||
|
||||
21. Detach from the target "(gdb) detach".
|
||||
|
||||
22. Exit GDB "(gdb) quit".
|
||||
|
||||
|
||||
Debug over the UART:
|
||||
|
||||
1. Compile and install the cross platform version of gdb for blackfin, which
|
||||
can be found at $(BINROOT)/bfin-elf-gdb.
|
||||
|
||||
2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under
|
||||
"Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
|
||||
With this selected, option "Full Symbolic/Source Debugging support" and
|
||||
"Compile the kernel with frame pointers" are also selected.
|
||||
|
||||
3. Select option "KGDB: connect over (UART)". Set "KGDB: UART port number" to be
|
||||
a different one from the console. Don't forget to change the mode of
|
||||
blackfin serial driver to PIO. Otherwise kgdb works incorrectly on UART.
|
||||
|
||||
4. If you want connect to kgdb when the kernel boots, enable
|
||||
"KGDB: Wait for gdb connection early"
|
||||
|
||||
5. Compile kernel.
|
||||
|
||||
6. Connect minicom to the serial port of the console and boot the kernel image.
|
||||
|
||||
7. Start GDB client "bfin-elf-gdb vmlinux".
|
||||
|
||||
8. Set the baud rate in GDB "(gdb) set remotebaud 57600".
|
||||
|
||||
9. Connect to the target on the second serial port "(gdb) target remote /dev/ttyS1".
|
||||
|
||||
10. Set software breakpoint "(gdb) break sys_open".
|
||||
|
||||
11. Continue "(gdb) c".
|
||||
|
||||
12. Run ls in the target console "/> ls".
|
||||
|
||||
13. A breakpoint is hit. "Breakpoint 1: sys_open(..."
|
||||
|
||||
14. All other operations are the same as that in KGDB over Ethernet.
|
||||
|
||||
|
||||
Debug over the same UART as console:
|
||||
|
||||
1. Compile and install the cross platform version of gdb for blackfin, which
|
||||
can be found at $(BINROOT)/bfin-elf-gdb.
|
||||
|
||||
2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under
|
||||
"Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
|
||||
With this selected, option "Full Symbolic/Source Debugging support" and
|
||||
"Compile the kernel with frame pointers" are also selected.
|
||||
|
||||
3. Select option "KGDB: connect over UART". Set "KGDB: UART port number" to console.
|
||||
Don't forget to change the mode of blackfin serial driver to PIO.
|
||||
Otherwise kgdb works incorrectly on UART.
|
||||
|
||||
4. If you want connect to kgdb when the kernel boots, enable
|
||||
"KGDB: Wait for gdb connection early"
|
||||
|
||||
5. Connect minicom to the serial port and boot the kernel image.
|
||||
|
||||
6. (Optional) Ask target to wait for gdb connection by entering Ctrl+A. In minicom, you should enter Ctrl+A+A.
|
||||
|
||||
7. Start GDB client "bfin-elf-gdb vmlinux".
|
||||
|
||||
8. Set the baud rate in GDB "(gdb) set remotebaud 57600".
|
||||
|
||||
9. Connect to the target "(gdb) target remote /dev/ttyS0".
|
||||
|
||||
10. Set software breakpoint "(gdb) break sys_open".
|
||||
|
||||
11. Continue "(gdb) c". Then enter Ctrl+C twice to stop GDB connection.
|
||||
|
||||
12. Run ls in the target console "/> ls". Dummy string can be seen on the console.
|
||||
|
||||
13. Then connect the gdb to target again. "(gdb) target remote /dev/ttyS0".
|
||||
Now you will find a breakpoint is hit. "Breakpoint 1: sys_open(..."
|
||||
|
||||
14. All other operations are the same as that in KGDB over Ethernet. The only
|
||||
difference is that after continue command in GDB, please stop GDB
|
||||
connection by 2 "Ctrl+C"s and connect again after breakpoints are hit or
|
||||
Ctrl+A is entered.
|
@ -14,3 +14,4 @@ obj-$(CONFIG_BF561) += bfin_gpio.o
|
||||
obj-$(CONFIG_MODULES) += module.o
|
||||
obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o
|
||||
obj-$(CONFIG_DUAL_CORE_TEST_MODULE) += dualcore_test.o
|
||||
obj-$(CONFIG_KGDB) += kgdb.o
|
||||
|
421
arch/blackfin/kernel/kgdb.c
Normal file
421
arch/blackfin/kernel/kgdb.c
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* File: arch/blackfin/kernel/kgdb.c
|
||||
* Based on:
|
||||
* Author: Sonic Zhang
|
||||
*
|
||||
* Created:
|
||||
* Description:
|
||||
*
|
||||
* Rev: $Id: kgdb_bfin_linux-2.6.x.patch 4934 2007-02-13 09:32:11Z sonicz $
|
||||
*
|
||||
* Modified:
|
||||
* Copyright 2005-2006 Analog Devices Inc.
|
||||
*
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ptrace.h> /* for linux pt_regs struct */
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/debugger.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/blackfin.h>
|
||||
|
||||
/* Put the error code here just in case the user cares. */
|
||||
int gdb_bf533errcode;
|
||||
/* Likewise, the vector number here (since GDB only gets the signal
|
||||
number through the usual means, and that's not very specific). */
|
||||
int gdb_bf533vector = -1;
|
||||
|
||||
#if KGDB_MAX_NO_CPUS != 8
|
||||
#error change the definition of slavecpulocks
|
||||
#endif
|
||||
|
||||
void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||
{
|
||||
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] = regs->reserved;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts ebp, esp and eip values understandable by gdb from the values
|
||||
* saved by switch_to.
|
||||
* thread.esp points to ebp. flags and ebp are pushed in switch_to hence esp
|
||||
* prior to entering switch_to is 8 greater then the value that is saved.
|
||||
* If switch_to changes, change following code appropriately.
|
||||
*/
|
||||
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
|
||||
{
|
||||
gdb_regs[BFIN_SP] = p->thread.ksp;
|
||||
gdb_regs[BFIN_PC] = p->thread.pc;
|
||||
gdb_regs[BFIN_SEQSTAT] = p->thread.seqstat;
|
||||
}
|
||||
|
||||
void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||
{
|
||||
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->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
|
||||
}
|
||||
|
||||
struct hw_breakpoint {
|
||||
unsigned int occupied:1;
|
||||
unsigned int skip:1;
|
||||
unsigned int enabled:1;
|
||||
unsigned int type:1;
|
||||
unsigned int dataacc:2;
|
||||
unsigned short count;
|
||||
unsigned int addr;
|
||||
} breakinfo[HW_BREAKPOINT_NUM];
|
||||
|
||||
int kgdb_arch_init(void)
|
||||
{
|
||||
kgdb_remove_all_hw_break();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kgdb_set_hw_break(unsigned long addr)
|
||||
{
|
||||
int breakno;
|
||||
for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++)
|
||||
if (!breakinfo[breakno].occupied) {
|
||||
breakinfo[breakno].occupied = 1;
|
||||
breakinfo[breakno].enabled = 1;
|
||||
breakinfo[breakno].type = 1;
|
||||
breakinfo[breakno].addr = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
int kgdb_remove_hw_break(unsigned long addr)
|
||||
{
|
||||
int breakno;
|
||||
for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++)
|
||||
if (breakinfo[breakno].addr == addr)
|
||||
memset(&(breakinfo[breakno]), 0, sizeof(struct hw_breakpoint));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kgdb_remove_all_hw_break(void)
|
||||
{
|
||||
memset(breakinfo, 0, sizeof(struct hw_breakpoint)*8);
|
||||
}
|
||||
|
||||
/*
|
||||
void kgdb_show_info(void)
|
||||
{
|
||||
printk(KERN_DEBUG "hwd: wpia0=0x%x, wpiacnt0=%d, wpiactl=0x%x, wpstat=0x%x\n",
|
||||
bfin_read_WPIA0(), bfin_read_WPIACNT0(),
|
||||
bfin_read_WPIACTL(), bfin_read_WPSTAT());
|
||||
}
|
||||
*/
|
||||
|
||||
void kgdb_correct_hw_break(void)
|
||||
{
|
||||
int breakno;
|
||||
int correctit;
|
||||
uint32_t wpdactl = bfin_read_WPDACTL();
|
||||
|
||||
correctit = 0;
|
||||
for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++) {
|
||||
if (breakinfo[breakno].type == 1) {
|
||||
switch (breakno) {
|
||||
case 0:
|
||||
if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN0)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~(WPIREN01|EMUSW0);
|
||||
wpdactl |= WPIAEN0|WPICNTEN0;
|
||||
bfin_write_WPIA0(breakinfo[breakno].addr);
|
||||
bfin_write_WPIACNT0(breakinfo[breakno].skip);
|
||||
} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN0)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~WPIAEN0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN1)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~(WPIREN01|EMUSW1);
|
||||
wpdactl |= WPIAEN1|WPICNTEN1;
|
||||
bfin_write_WPIA1(breakinfo[breakno].addr);
|
||||
bfin_write_WPIACNT1(breakinfo[breakno].skip);
|
||||
} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN1)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~WPIAEN1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN2)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~(WPIREN23|EMUSW2);
|
||||
wpdactl |= WPIAEN2|WPICNTEN2;
|
||||
bfin_write_WPIA2(breakinfo[breakno].addr);
|
||||
bfin_write_WPIACNT2(breakinfo[breakno].skip);
|
||||
} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN2)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~WPIAEN2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN3)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~(WPIREN23|EMUSW3);
|
||||
wpdactl |= WPIAEN3|WPICNTEN3;
|
||||
bfin_write_WPIA3(breakinfo[breakno].addr);
|
||||
bfin_write_WPIACNT3(breakinfo[breakno].skip);
|
||||
} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN3)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~WPIAEN3;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN4)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~(WPIREN45|EMUSW4);
|
||||
wpdactl |= WPIAEN4|WPICNTEN4;
|
||||
bfin_write_WPIA4(breakinfo[breakno].addr);
|
||||
bfin_write_WPIACNT4(breakinfo[breakno].skip);
|
||||
} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN4)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~WPIAEN4;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN5)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~(WPIREN45|EMUSW5);
|
||||
wpdactl |= WPIAEN5|WPICNTEN5;
|
||||
bfin_write_WPIA5(breakinfo[breakno].addr);
|
||||
bfin_write_WPIACNT5(breakinfo[breakno].skip);
|
||||
} else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN5)) {
|
||||
correctit = 1;
|
||||
wpdactl &= ~WPIAEN5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (correctit) {
|
||||
wpdactl &= ~WPAND;
|
||||
wpdactl |= WPPWR;
|
||||
/*printk("correct_hw_break: wpdactl=0x%x\n", wpdactl);*/
|
||||
bfin_write_WPDACTL(wpdactl);
|
||||
CSYNC();
|
||||
/*kgdb_show_info();*/
|
||||
}
|
||||
}
|
||||
|
||||
void kgdb_disable_hw_debug(struct pt_regs *regs)
|
||||
{
|
||||
/* Disable hardware debugging while we are in kgdb */
|
||||
bfin_write_WPIACTL(bfin_read_WPIACTL() & ~0x1);
|
||||
CSYNC();
|
||||
}
|
||||
|
||||
void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
|
||||
{
|
||||
/* Master processor is completely in the debugger */
|
||||
gdb_bf533vector = eVector;
|
||||
gdb_bf533errcode = err_code;
|
||||
}
|
||||
|
||||
int kgdb_arch_handle_exception(int exceptionVector, int signo,
|
||||
int err_code, char *remcom_in_buffer,
|
||||
char *remcom_out_buffer,
|
||||
struct pt_regs *linux_regs)
|
||||
{
|
||||
long addr;
|
||||
long breakno;
|
||||
char *ptr;
|
||||
int newPC;
|
||||
int wp_status;
|
||||
|
||||
switch (remcom_in_buffer[0]) {
|
||||
case 'c':
|
||||
case 's':
|
||||
if (kgdb_contthread && kgdb_contthread != current) {
|
||||
strcpy(remcom_out_buffer, "E00");
|
||||
break;
|
||||
}
|
||||
|
||||
kgdb_contthread = NULL;
|
||||
|
||||
/* try to read optional parameter, pc unchanged if no parm */
|
||||
ptr = &remcom_in_buffer[1];
|
||||
if (kgdb_hex2long(&ptr, &addr)) {
|
||||
linux_regs->retx = addr;
|
||||
}
|
||||
newPC = linux_regs->retx;
|
||||
|
||||
/* clear the trace bit */
|
||||
linux_regs->syscfg &= 0xfffffffe;
|
||||
|
||||
/* set the trace bit if we're stepping */
|
||||
if (remcom_in_buffer[0] == 's') {
|
||||
linux_regs->syscfg |= 0x1;
|
||||
debugger_step = 1;
|
||||
}
|
||||
|
||||
wp_status = bfin_read_WPSTAT();
|
||||
CSYNC();
|
||||
|
||||
if (exceptionVector == VEC_WATCH) {
|
||||
for (breakno = 0; breakno < 6; ++breakno) {
|
||||
if (wp_status & (1 << breakno)) {
|
||||
breakinfo->skip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
kgdb_correct_hw_break();
|
||||
|
||||
bfin_write_WPSTAT(0);
|
||||
|
||||
return 0;
|
||||
} /* switch */
|
||||
return -1; /* this means that we do not want to exit from the handler */
|
||||
}
|
||||
|
||||
struct kgdb_arch arch_kgdb_ops = {
|
||||
.gdb_bpt_instr = {0xa1},
|
||||
.flags = KGDB_HW_BREAKPOINT,
|
||||
};
|
@ -556,7 +556,7 @@ choice
|
||||
|
||||
config SERIAL_BFIN_DMA
|
||||
bool "DMA mode"
|
||||
depends on DMA_UNCACHED_1M
|
||||
depends on DMA_UNCACHED_1M && !KGDB_UART
|
||||
help
|
||||
This driver works under DMA mode. If this option is selected, the
|
||||
blackfin simple dma driver is also enabled.
|
||||
|
@ -41,6 +41,11 @@
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/serial_core.h>
|
||||
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
#include <linux/kgdb.h>
|
||||
#include <asm/irq_regs.h>
|
||||
#endif
|
||||
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/mach/bfin_serial_5xx.h>
|
||||
|
||||
@ -119,6 +124,9 @@ static void bfin_serial_stop_rx(struct uart_port *port)
|
||||
unsigned short ier;
|
||||
|
||||
ier = UART_GET_IER(uart);
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
if (uart->port.line != CONFIG_KGDB_UART_PORT)
|
||||
#endif
|
||||
ier &= ~ERBFI;
|
||||
UART_PUT_IER(uart, ier);
|
||||
}
|
||||
@ -130,6 +138,49 @@ static void bfin_serial_enable_ms(struct uart_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
static int kgdb_entry_state;
|
||||
|
||||
void kgdb_put_debug_char(int chr)
|
||||
{
|
||||
struct bfin_serial_port *uart;
|
||||
|
||||
if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
|
||||
uart = &bfin_serial_ports[0];
|
||||
else
|
||||
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
|
||||
|
||||
while (!(UART_GET_LSR(uart) & THRE)) {
|
||||
__builtin_bfin_ssync();
|
||||
}
|
||||
UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
|
||||
__builtin_bfin_ssync();
|
||||
UART_PUT_CHAR(uart, (unsigned char)chr);
|
||||
__builtin_bfin_ssync();
|
||||
}
|
||||
|
||||
int kgdb_get_debug_char(void)
|
||||
{
|
||||
struct bfin_serial_port *uart;
|
||||
unsigned char chr;
|
||||
|
||||
if (CONFIG_KGDB_UART_PORT<0 || CONFIG_KGDB_UART_PORT>=NR_PORTS)
|
||||
uart = &bfin_serial_ports[0];
|
||||
else
|
||||
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
|
||||
|
||||
while(!(UART_GET_LSR(uart) & DR)) {
|
||||
__builtin_bfin_ssync();
|
||||
}
|
||||
UART_PUT_LCR(uart, UART_GET_LCR(uart)&(~DLAB));
|
||||
__builtin_bfin_ssync();
|
||||
chr = UART_GET_CHAR(uart);
|
||||
__builtin_bfin_ssync();
|
||||
|
||||
return chr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SERIAL_BFIN_PIO
|
||||
static void local_put_char(struct bfin_serial_port *uart, char ch)
|
||||
{
|
||||
@ -152,6 +203,9 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
|
||||
{
|
||||
struct tty_struct *tty = uart->port.info->tty;
|
||||
unsigned int status, ch, flg;
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
struct pt_regs *regs = get_irq_regs();
|
||||
#endif
|
||||
#ifdef BF533_FAMILY
|
||||
static int in_break = 0;
|
||||
#endif
|
||||
@ -160,6 +214,27 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
|
||||
ch = UART_GET_CHAR(uart);
|
||||
uart->port.icount.rx++;
|
||||
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
if (uart->port.line == CONFIG_KGDB_UART_PORT) {
|
||||
if (uart->port.cons->index == CONFIG_KGDB_UART_PORT && ch == 0x1) { /* Ctrl + A */
|
||||
kgdb_breakkey_pressed(regs);
|
||||
return;
|
||||
} else if (kgdb_entry_state == 0 && ch == '$') {/* connection from KGDB */
|
||||
kgdb_entry_state = 1;
|
||||
} else if (kgdb_entry_state == 1 && ch == 'q') {
|
||||
kgdb_entry_state = 0;
|
||||
kgdb_breakkey_pressed(regs);
|
||||
return;
|
||||
} else if (ch == 0x3) {/* Ctrl + C */
|
||||
kgdb_entry_state = 0;
|
||||
kgdb_breakkey_pressed(regs);
|
||||
return;
|
||||
} else {
|
||||
kgdb_entry_state = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BF533_FAMILY
|
||||
/* The BF533 family of processors have a nice misbehavior where
|
||||
* they continuously generate characters for a "single" break.
|
||||
@ -571,7 +646,11 @@ static int bfin_serial_startup(struct uart_port *port)
|
||||
uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
|
||||
add_timer(&(uart->rx_dma_timer));
|
||||
#else
|
||||
# ifdef CONFIG_KGDB_UART
|
||||
if (uart->port.line != CONFIG_KGDB_UART_PORT && request_irq
|
||||
# else
|
||||
if (request_irq
|
||||
# endif
|
||||
(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED,
|
||||
"BFIN_UART_RX", uart)) {
|
||||
printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
|
||||
@ -601,6 +680,9 @@ static void bfin_serial_shutdown(struct uart_port *port)
|
||||
free_dma(uart->rx_dma_channel);
|
||||
del_timer(&(uart->rx_dma_timer));
|
||||
#else
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
if (uart->port.line != CONFIG_KGDB_UART_PORT)
|
||||
#endif
|
||||
free_irq(uart->port.irq, uart);
|
||||
free_irq(uart->port.irq+1, uart);
|
||||
#endif
|
||||
@ -931,6 +1013,10 @@ static int __init bfin_serial_rs_console_init(void)
|
||||
{
|
||||
bfin_serial_init_ports();
|
||||
register_console(&bfin_serial_console);
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
kgdb_entry_state = 0;
|
||||
init_kgdb_uart();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
console_initcall(bfin_serial_rs_console_init);
|
||||
@ -1023,6 +1109,10 @@ static struct platform_driver bfin_serial_driver = {
|
||||
static int __init bfin_serial_init(void)
|
||||
{
|
||||
int ret;
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
struct bfin_serial_port *uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
|
||||
struct termios t;
|
||||
#endif
|
||||
|
||||
pr_info("Serial: Blackfin serial driver\n");
|
||||
|
||||
@ -1036,6 +1126,21 @@ static int __init bfin_serial_init(void)
|
||||
uart_unregister_driver(&bfin_serial_reg);
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_KGDB_UART
|
||||
if (uart->port.cons->index != CONFIG_KGDB_UART_PORT) {
|
||||
request_irq(uart->port.irq, bfin_serial_int,
|
||||
IRQF_DISABLED, "BFIN_UART_RX", uart);
|
||||
pr_info("Request irq for kgdb uart port\n");
|
||||
UART_PUT_IER(uart, UART_GET_IER(uart) | ERBFI);
|
||||
__builtin_bfin_ssync();
|
||||
t.c_cflag = CS8|B57600;
|
||||
t.c_iflag = 0;
|
||||
t.c_oflag = 0;
|
||||
t.c_lflag = ICANON;
|
||||
t.c_line = CONFIG_KGDB_UART_PORT;
|
||||
bfin_serial_set_termios(&uart->port, &t, &t);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
183
include/asm-blackfin/kgdb.h
Normal file
183
include/asm-blackfin/kgdb.h
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* File: include/asm-blackfin/kgdb.h
|
||||
* Based on:
|
||||
* Author: Sonic Zhang
|
||||
*
|
||||
* Created:
|
||||
* Description:
|
||||
*
|
||||
* Rev: $Id: kgdb_bfin_linux-2.6.x.patch 4934 2007-02-13 09:32:11Z sonicz $
|
||||
*
|
||||
* Modified:
|
||||
* Copyright 2005-2006 Analog Devices Inc.
|
||||
*
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __ASM_BLACKFIN_KGDB_H__
|
||||
#define __ASM_BLACKFIN_KGDB_H__
|
||||
|
||||
#include <linux/ptrace.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
|
||||
|
||||
/*
|
||||
* Note that this register image is different from
|
||||
* the register image that Linux produces at interrupt time.
|
||||
*
|
||||
* Linux's register image is defined by struct pt_regs in ptrace.h.
|
||||
*/
|
||||
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
|
||||
|
||||
#define BREAKPOINT() asm(" EXCPT 2;");
|
||||
#define BREAK_INSTR_SIZE 2
|
||||
#define HW_BREAKPOINT_NUM 6
|
||||
|
||||
/* 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 0x300
|
||||
#define WPDSRC1 0xc00
|
||||
#define WPDACC1 0x3000
|
||||
|
||||
/* 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
|
||||
|
||||
extern void kgdb_print(const char *fmt, ...);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user