Use BDI2000 to debug Linux kernel and modules
Hansel@163.com
2007-12-22
BDI2000 is a JTAG debugger with high cost performance. It supports multiple embedded processors such as ARM, MIPS, and XSCALE by loading different firmware. What I use is
The mips version of bdiGDB, that is, it can be simulated as a gdbserver and used with gdb for source code-level debugging. The Linux kernel version 2.6.18.8 is used.
1. BDI2000 configuration file
If the target board has a bootloader, such as redboot or u-boot, you can first initialize the target board with bootloader and then use the debugger to directly download the program to the memory.
If no bootloader exists on the target board, use the debugger configuration file to initialize the target board, including CPU and memory. The configuration file I used is as follows:
[INIT]
WM32 0xB8000000 0xefbc8cd0; 16bit ddr
WM32 0xB8000004 0x8e7156a2; 16 bitconservative twtr, twtr 19 trtw 21;
WM32 0xB8000010 0x8;
WM32 0xB8000010 0x1; write mode word
WM32 0xB800000C 0x2; enable dll (extened mode word)
WM32 0xB8000010 0x2; write extended mode word
WM32 0xB8000010 0x8; precharge enabled
WM32 0xB8000008 0x61; dll out of reset 16b
WM32 0xB8000010 0x1; write mode word
WM32 0xB8000014 0x461b;
WM32 0xB8000018 0 xFFFF; 16 B
; Pll/dividers
WM32 0xb8050000 0x800f3098; 200/200/100
WM32 0xb8050008 0x1
WM32 0xb8050004 0x50c0; gen 1 Ghz
Wm32 0xb8050018 0x1313; Ethernet 25 MHz (base)
Wm32 0xb805001c 0xEE; 33 MHz PCI
Wm32 0xb800001c 0x07
Wm32 0xb8000020 0x07
Wm32 0xb8000024 0x07
Wm32 0xb8000028 0x07
; Invalidate caches
Ivic 4 512; invalidate IC, 4 way, 256 Sets
Ivdc 4 256; invalidate DC, 4 way, 256 Sets
[Target]
Jtagclock 2;
Cputype m24k; The used target CPU type
Endian big; target is big endian
Reset hard; reset is applied via the ejtag reset pin
Startup reset; stop mode is used to let the monitor init the system
Workspace 0xa0000000; workspace in target RAM for fast download
Bdimode agent; the BDI working mode (loadonly | agent)
Breakmode soft; soft or hard, hard Uses PPC hardware breakpoints
Stepmode swbp; JTAG, hwbp or swbp
Vector catch; catch unhandled exceptions
SiO2 8023 9600; TCP port for UART connection port 8023
[Host]
IP 192.168.1.168
FILE u-boot.bin
Format bin 0x80060000
Load manual; load code MANUAL or AUTO after reset
Prompt mips>; used prompt
VPC: DEBUGPORT 2001
DUMP dump. bin
[FLASH]
; WORKSPACE 0xa0000080
; CHIPTYPE AT49X16
; CHIPSIZE 0x200400
; BUSWIDTH 32
; FILE init. s19
; FORMAT SREC
[REGS]
FILE BDI2000/reg24kf. def
2. debug the Linux Kernel
1) Compile the kernel and add debugging information
# Make menuconfig
In Kernel Hacking-> select Compile the kernel with debug info, or set CONFIG_DEBUG_INFO = y in the. config file to Compile
Option CFLAGS will include the-g parameter and re-compile the kernel. The generated vmlinux has more than 20 mb. the kernel is placed on a PC to provide debugging information. It is downloaded
The kernel of the target board must use the strip command to remove debugging information.
Compile the vmlinux. bin file in the arch/$ (ARCH)/boot directory.
# Make vmlinux. bin
. Learn about the kernel Mount address and entry address:
Use readelf:
# Mips-linux-uclibc-readelf-e vmlinux
............
Entry point address: 0x802bd000
...........
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[0] NULL 00000000 000000 000000 00 0 0 0
[1]. text PROGBITS 80060000 000800 1eb704 00 AX 0 0 2048
...........
The Mount address of the. text Segment is 0x80060000, and the kernel entry address is 0x802bd000.
You can also obtain this information by using objdump. The-d parameter Disassembly tool in vmlinux can get the Mount address, and the-f parameter can get the entry address.
Or view the System. map file:
# Cat System. map | grep _ text
# Cat System. map | grep kernel_entry
. View the kernel start_kernel address
To use the BDI breakpoint, you need to know the kernel start_kernel address.
# Cat System. map | grep start_kernel
802bd620 T start_kernel
2) download the kernel to the target board
Put the vmlinux. bin after strip to the directory where the tftp server is located.
If bootloader exists on the target board, the target board is reset and then bootloader is run first. After the bootloader initializes the target board, use the BDI debugger halt target board, and then
When downloading the kernel, be sure to download it to the kernel Mount address. After downloading the kernel, run the breakpoint at start_kernel and then run the kernel from the kernel entry address.
The breakpoint is triggered at start_kernel, and the target board enters the debugging status again, waiting for gdb to connect to the target board.
# Telnet bdi
MIPS> halt
MIPS> load 0x80060000 vmlinux. bin
MIPS> bi 0x802bd620
MIPS> go 0x802bd000
-TARGET: target has entered debug mode
Note: Set the IP address of the bdi host name in/etc/hosts.
3) Use gdb to debug the kernel
For convenience, you can create the. gdbinit file under the kernel source code tree directory. The content is:
Target remote bdi: 2001
B panic
B sys_sync
Note: The debugging program can use target extended-remote bdi: 2001 to replace the first line of. gdbinit and use the enhanced connection mode. At this time, gdbserver automatically creates the process of the program after the program exits.
Connection target board
# Mips-linux-gdbtui vmlinux
GNU gdb 6.7.1
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3 +: gnu gpl version 3 or later This is free software: you are free to change and redistribute it.
There is no warranty, to the extent permitted by law. Type "show copying"
And "show warranty" for details.
This GDB was configured as "-- host = i686-pc-linux-gnu -- target = mipseb-linux-uclibc "...
Start_kernel () at init/main. c: 457
Breakpoint 1 at 0x80085b68: file kernel/panic. c, line 78.
Breakpoint 2 at 0x800cb5e0: file fs/buffer. c, line 283.
(Gdb) c
Gdbtui is the gdb of the text user interface. It can display the source code, assembly code, and register value windows out of the command line. For more information, see the appendix.
<Debugging with gdb>.
The purpose of breakpoint at sys_sync is to use the sync command in Linux to return to gdb control.
In this way, you can use gdb to debug the Linux kernel at the source code level. Note: After the continue command, the kernel will be stuck for a period of time before it can respond to the command line normally. This is normal,
Do not consider the kernel down.
3. debug the Linux kernel module
Step: Start the kernel properly (JTAG download or Flash Boot)-> halt command under BDI2000-> RUN gdb on PC, connect to gdbserver simulated by BDI2000, and
Breakpoint-> gdb continue command-> get the kernel module loading address-> install the kernel module debugging information in gdb-> debug the kernel module at the breakpoint
Generally, BDI2000 does not support the halt command. When gdb is attached to the target board, the CPU execution is automatically stopped. Generally, it is in the Linux Kernel r4k_wait.
1) Compile the kernel module
Add the-g parameter to CFLAGS to generate debugging information.
Obtain the module entry address:
Linux 2.4 kernel:
Target Board
# Insmod-m hello. o> map
# Cat map
. This 00000060 c88d8000 2 ** 2
. Text 00000035 c88d8060 2 ** 2
. Rodata 00000069 c88d80a0 2 *** 5
......
. Data 00000000 c88d833c 2 *** 2
. BSS 00000000 c88d833c 2 *** 2
......
# Sync
Debugging Machine
(GDB) Add-symbol-file hello. O 0xc88d8060-S. Data 0xc88d833c-S. rodata 0xc88d80a0-S. BSS 0xc88d833c
(GDB) c
This method also has some shortcomings. It cannot debug the code initialized by the module, because the initialization code of the module has been executed. If the module is not loaded
The module insertion address cannot be obtained, and the breakpoint cannot be set before the module initialization. The following alternative methods can be used for such debugging requirements.
On the target board, use the above method to obtain the address information loaded by the module, and then use rmmod to uninstall the module. Import the module address information to the gdb environment on the debugging machine,
Set a breakpoint before calling the initialization code of the kernel code. In this way, when the module is inserted again on the target board, the code will stop before the module initialization.
You can use the gdb command to debug the code initialization module.
Another method for initializing the function of the debugging module is: when the kernel module is inserted, the kernel module mechanism calls the function sys_init_module (kernel/modle. c) to execute
Kernel module initialization. This function calls the initialization function of the inserted module. The program code snippet is as follows:
...... ......
If (mod-> Init! = NULL)
Ret = mod-> Init ();
...... ......
When a breakpoint is set on the statement, it can also be stopped before the execution module initialization.
Linux 2.6 kernel:
The module Mount address needs to be printed in the module initialization code .. The rodata segment address can be obtained by executing the readelf-e hello. Ko command in the file
And add the align value of the segment.
For convenience, put the code for printing the loaded address in a separate library. Example code:
[Root @ Root ~] # Cat module_lib.h
# Ifndef _ module_lib_h __
# DEFINE _ module_lib_h __
Static int lib_bss_indicator;
Typedef int (* module_init_func) (void );
Void print_module_addr (char * module_name, module_init_func init_func );
# Endif/* _ module_lib_h __*/
[Root @ Root ~] # Cat module_lib.c
# Include <Linux/module. h>
# Include "module_lib.h"
Void print_module_addr (char * module_name, module_init_func init_func)
{
Static int data_indicator = 0;
Printk (KERN_ALERT "------------ module information ---------------/n ");
Printk (KERN_ALERT "% s:. text = 0x % p/n", module_name, init_func );
Printk (KERN_ALERT "% s:. data = 0x % p/n", module_name, & data_indicator );
Printk (KERN_ALERT "% s:. bss = 0x % p/n", module_name, & LIB_bss_indicator );
Printk (KERN_ALERT "-----------------------------------------------/n ");
}
Call example:
[Root @ root ~] # Cat main. c
# Include <linux/init. h>
# Include <linux/kernel. h>
# Include <linux/module. h>
# Include "module_lib.h"
Static int _ init test_init (void)
{
Printk ("init module/n ");
Print_module_addr ("test", test_init );
Return 0;
}
Static void _ exit test_exit (void)
{
Printk ("exit modules/n ");
}
Module_init (test_init );
Module_exit (test_exit );
Makefile Syntax:
[Root @ root ~] # Cat Makefile
PWD = $ (shell pwd)
KERNEL_SRC =/lib/modules/'uname-R'/build
Obj-m: = hello. o
Hello-y: = module_lib.o main. o
KMAKE: = $ (MAKE)
All:
$ (KMAKE)-C $ (KERNEL_SRC) M = $ (PWD) modules
Clean:
$ (KMAKE)-C $ (KERNEL_SRC) M = $ (PWD) clean
$ (RM) Module. symvers
Note: In the hello-y macro definition, module_lib.o should be placed first, so that the variables defined in module_lib will be linked to the header of each segment of the ko file.
After obtaining the segments of the kernel module, the debugging method is the same as that in the 2.4 kernel.
Another simple method: If the kernel supports sysfs, each installed module will have a corresponding directory under/sys/module/, and the section directory under this directory.
You can view the addresses of all sections. Note that the. text address is different from the address seen in the above method. This method is more accurate.
# Mount-t sysfs none/sys
# Cd/sys/module/hello/sections
# Cat. text
# Cat. bss
# Cat. data
If the address of the Linux Mount module is after MMU, you also need to set the PTBASE and MMU options in the BDI2000 configuration file so that BDI2000 can find
The address ing table in linux automatically converts virtual addresses to physical addresses. For details, see the appendix.
4. Appendix:
1) Common shortcut keys for gdbtui:
Ctrl + X, A switch gdb mode/gdbtui mode (press Ctrl + X first, then press A, the same below)
Ctrl + X, 1 change the layout mode to add another window (source code or assembly code) to the command line window. If it is gdb mode, it will automatically switch to the gdbtui mode.
Ctrl + X, 2 change the layout mode and add two other windows to the command line window (source code and assembly code)
Ctrl + X, O switch the current window
Ctrl + L refresh Screen Content
2) Common gdbtui commands:
Display window size in info win
Layout next switch to the next layout mode
Layout prev switches to the previous layout mode
Layout src only displays source code
Layout asm only displays assembly code
Layout split display source code and assembly code
Layout regs adds register content display
Focus cmd/src/asm/regs/next/prev switch current window
Refresh all windows
Tui reg next displays the next set of registers
Tui reg system display system registers
Update source code window and current execution point
Winheight name +/-line adjust the height of the name window
Tabset nchar Set tab to nchar characters
3) gdbtui source code window breakpoint ID
The first character:
B breakpoint hits at least once
B Does not hit the breakpoint
H hardware interruption, hit at least once
H no hit hardware interruption
Second character:
+ Resumable enabling
-Breakpoint disabled
4) Enable BDI2000 to support MMU
When BDI can access a virtual address, it automatically searches for the virtual address ing table and converts the virtual address to a physical address. To make BDI accessible
The starting address of the BDI virtual address ing table is required for the virtual address after MMU. The PTBASE parameter in the BDI configuration file specifies
The physical address stores the page table pointing to the Linux kernel (swapper_pg_dir) and the user-layer page table (current_pgd ).
If a match is found and the user-level page table is not 0, the user-level page table is searched.
The address defined by PTBASE should be the memory address not used by the kernel, which consists of 8 bytes. The address of the kernel page table and the user page are stored in sequence.
Table address. You can find a memory address as PTBASE before the kernel Mount address. For example, if the kernel Mount address is 0x80060000, you can
Select the PTBASE address 0x800002f0.
Modify the BDI configuration file:
[TARGET]
...
Mmu xlat; MMU support enabled
PTBASE 0x800002f0; here are the page table pointers
There are two ways to write the addresses of two page tables to the address specified by PTBASE. One way is to modify the kernel head. S file and add
The code of the PTBASE address. Another method is to manually enter two page table addresses to the PTBASE address under the GDB or BDI command line.
Method 1:
In the arch/mips/kernel/head. S file
J start_kernel
END (kernel_entry)
Previously added:
/* Setup the PTE pointers for the Abatron bdiGDB .*/
La t0, 0x800002f0/* must match the bdiGDB config file */
La t1, swapper_pg_dir
Sw t1, (t0)
Addiu t0, 4
La t1, pgd_current
Sw t1, (t0)
Method 2:
Use System. map in the kernel to find the addresses of swapper_pg_dir and pgd_current:
[Root @ root] # cat System. map | grep swapper_pg_dir
80303000 B swapper_pg_dir
[Root @ root] # cat System. map | grep pgd_current
80305018 B pgd_current
Fill in the two values in the Telnet interface of GDB or BDI to the address pointed to by PTBASE.
For example, in BDI:
BDI> mm 0x800002f0 0x80303000
BDI> mm 0x800002f4 0x80305018
View
BDI> md 0x800002f0
In GDB:
(Gdb) set * (int *) 0x800002f0 = 0x80303000
(Gdb) set x (int *) 0x800002f4 = 0x80305018
View:
(Gdb) x/10 0x800002f0
You can put the two commands of gdb into. gdbinit.
5. References
[1] BDI2000 Application Notes # 02-001a: Using the Abatron BDI2000 to Debug a Linux Kernel
[2] bdiGDB EJTAG interface for GNU Debugger MIPS32 User Manual (ManGDBR4K-2000C.pdf)
[3] Debugging with gdb
[4] Linux kernel debugging http://www.ibm.com/developerworks/cn/linux/l-kdb/