现在的位置: 首页 > 综合 > 正文

【嵌入式Linux学习七步曲之第四篇 Linux内核移植】GDB和BDI2000调试PPC Linux内核

2013年10月12日 ⁄ 综合 ⁄ 共 13691字 ⁄ 字号 评论关闭

GDB和BDI2000调试PPC Linux内核

 

Sailor_forever 

sailing_9806@163.com



转载请注明

http://blog.csdn.net/sailor_8318/archive/2009/11/10/4795287.aspx

 

【摘要】本文以MPC8270处理器和Linux2.6.15.5内核为例,讲述了用BDI2000和GDB联合调试Linux内核的基本方法。给出了BDI配置脚本,内核的配置选项及调试的具体步骤,为在PPC处理器上移植Linux内核奠定了基础。
【关键字】BDI2000  GDB  MPC8270  Linux MMU  PTBASE    SIGSTOP
 
目录

BDI2000配置脚本    1
内核修改及配置    3
编译选项    3
内核配置    4
内核调试    8
GDB script    8
未开启MMU    9
开启MMU    11

BDI2000配置脚本

; bdiGDB configuration file for PUC8270/80 Board and PPCBoot and MirrorBit-Flash
; Hard Reset Configuration Word = 0x0A07B000 (RSTCONF=LOW)
; ---------------------------------------------------------
;
[INIT]
; init core register
WREG    MSR             0x00000000
;      ;clear MSR

;IMMR : internal space @ 0xFFF00000 decided by ISB of Hard Reset Configuration Word
WM32    0xFFF10004    0xFFFFFFC3    ;SYPCR: disable watchdog
WM32    0xFFF10C80    0x00000001    ;SCCR : normal operation
WM32    0xFFF10024    0x00040000    ;BCR init
WM32    0xFFF10000    0x00000000    ;SIUMCR init
WM32    0xFFF10C94    0x00000000      ;RMR init

; init memory controller
; Flash S29GL128M10, 16MB, 100ns
WM32    0xFFF10104    0xFF001846    ;OR0: Data buffer dis., 6 w.s., ext.hold on read, tc=8*15ns=120ns
WM32    0xFFF10100    0x00001001    ;BR0: Flash @0x00000000, 16bit, no parity,default value for low boot memory mode instead of the eventual value

[TARGET]
CPUTYPE     8270        ;the CPU type (603EV,750,8240,8260)
JTAGCLOCK   0          ;use 16.6 MHz JTAG clock
BOOTADDR    0x00000100  ;boot address used for start-up break, decided by CIP of Hard Reset Configuration Word
BDIMODE     AGENT         ;the BDI working mode (LOADONLY | AGENT | GATEWAY)
BREAKMODE   HARD          ;SOFT or HARD, HARD uses PPC hardware breakpoints
STEPMODE    HWBP       ;TRACE or HWBP, HWPB uses a hardware breakpoint
MMU         XLAT       ;In order to support Linux kernel debugging when MMU is on, the BDI translates effective (virtual) to physical addresses.
PTBASE      0xf0       ;the physical memory address where the BDI looks for the virtual address of the array with the two page table pointers, setup in the kernel
;VECTOR      NOCATCH      ;catch unhandled exceptions
;DCACHE      NOFLUSH    ;data cache flushing (FLUSH | NOFLUSH)
POWERUP     3000        ;start delay after power-up detected in ms
REGLIST     ALL         ;select registers needed init to transfer to CPU
STARTUP         RESET  ;for programming
;STARTUP        RUN    ;for gdb debug
WAKEUP            1000
;MEMDELAY        2000

[HOST]
IP          150.236.68.171   ;tftp sever ip where to get download image
LOAD        MANUAL      ;load code MANUAL or AUTO after reset
DEBUGPORT   2001        ;network port for gdb debug
PROMPT      PUC8270>    ;new prompt for Telnet
DUMP        E:/temp/dump.bin

[FLASH]
CHIPTYPE    MIRRORX16   ;Flash type: AMD MirrorBit
CHIPSIZE    0x1000000   ;The size of one flash chip in bytes (e.g. S29GL128M10 = 0x1000000)
BUSWIDTH    16          ;The width of the flash memory bus in bits (8 | 16 | 32 | 64)
FILE        pboot.bin   ;default file to program, here is TMN PBOOT
FORMAT      BIN
;WORKSPACE   0xFFF00000    ;workspace in int. target RAM for fast download; download is stable without this, but fast with it.

;default for erase command, total size should be larger the size of download image
ERASE       0x00000000  ;erase sector (128K) of flash
ERASE       0x00020000  ;erase sector (128K) of flash
ERASE       0x00040000  ;erase sector (128K) of flash
ERASE       0x00060000  ;erase sector (128K) of flash
ERASE       0x00080000  ;erase sector (128K) of flash
ERASE       0x000a0000  ;erase sector (128K) of flash

[REGS]
DMM1        0xFFF00000  ;IMMR base
FILE        $reg8280.def
 
配置文件的规则可以参考http://blog.csdn.net/sailor_8318/archive/2009/10/30/4745793.aspx的4.9.1    BDI configuration file for debug章节
对于Linux内核打开MMU后的调试,需要在配置文件中打开相关配置选项:MMU和PTBASE
MMU    XLAT在MMU打开之后,BDI访问内存时,会根据BAT、内核和用户页表及默认的映射关系(KERNELBASE + 0x0FFFFFFF) to
0x00000000...0x0FFFFFFF将虚拟地址转换为物理地址。MMU未打开时,将不进行地址转换,此时因为符号表中的地址为虚拟地址,因此需要重定位符号表

PTBASE是存储内核页表和用户页表首地址的指针,关系如下:
PTBASE (physical address) ->
    PTE pointer pointer(virtual address) ->
           PTE kernel pointer (virtual address)
           PTE user pointer (virtual address)

PTBASE是物理地址,可以设置为MMU打开之后内核不再访问的一块内存中的任意地址,0-0x100在复位向量之下,启动之后不再使用。因此PTBASE可以设置为0-0x100之间的任意值,但是需要和 内核匹配,此值一般为0xf0。
 

内核修改及配置

编译选项

为了能够调试内核,需要生成各种调试信息,这可以通过更改内核根目录下面的Makefile实现
-g GDB调试必备的编译参数
-O 优化选项,Linux的优化级别一般为O2,会根据性能及代码大小进行相关优化,优化后的代码和C源代码会有不一致的情况,不便于单步调试
-fomit-frame-pointer 优化函数栈回溯,当有此选项时,函数调用时可能会把参数放在寄存器中,不便于查看函数调用时的参数传递关系,因此应该去掉。可以添加-fno-omit-frame-pointer选项
-gdwarf-2 可以在GDB中看出相关宏定义。正常情况下,宏定义在编译时会进行替换,添加-gdwarf-2可以生成相关宏信息。

上述编译选项可以通过CONFIG_DEBUG_INFO来统一控制,更改如下:
ifdef CONFIG_DEBUG_INFO
CFLAGS        += -O
else
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
CFLAGS        += -Os
else
CFLAGS        += -O2
endif
endif
#sailing cancel optimisation 20091103.

ifdef CONFIG_DEBUG_INFO
CFLAGS        += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
else
ifdef CONFIG_FRAME_POINTER
CFLAGS        += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
else
CFLAGS        += -fomit-frame-pointer
endif
endif
#sailing add stack trace 20091103

ifdef CONFIG_DEBUG_INFO
CFLAGS        += -g -gdwarf-2
endif
#sailing add -gdwarf-2 20091103

内核配置

CONFIG_DEBUG_INFO宏又是从哪里来的呢?它其实是从内核的配置文件.config里面来的。
make ARCH=ppc CROSS_COMPILE=ppc_82xx-  menuconfig

Kernel hacking  --->
[*] Kernel debugging
 [*] Include BDI-2000 user context switcher    

在2.6.15.5内核中,打开Kernel debugging选项后将自动打开Compile the kernel with debug info

保存后,以上选项会在根目录下的.config文件中生成如下配置选项:
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_MAGIC_SYSRQ is not set
CONFIG_LOG_BUF_SHIFT=14
CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_SCHEDSTATS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_FS is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_RCU_TORTURE_TEST is not set
CONFIG_WANT_EXTRA_DEBUG_INFORMATION=y
CONFIG_KGDB=y
CONFIG_KGDB_CONSOLE=y
CONFIG_KGDB_ONLY_MODULES=y
# CONFIG_XMON is not set
CONFIG_BDI_SWITCH=y

CONFIG_BDI_SWITCH选项是配合BDI2000的MMU和PTBASE参数的。其将自动在PTBASE所指向的物理内存处保存内核页表和用户页表的首地址。

arch/ppc/kernel/head.S
/* Load up the kernel context */
2:    bl    load_up_mmu

#ifdef CONFIG_BDI_SWITCH
    /* Add helper information for the Abatron bdiGDB debugger.
     * We do this here because we know the mmu is disabled, and
     * will be enabled for real in just a few instructions.
     */
    lis    r5, abatron_pteptrs@h
    ori    r5, r5, abatron_pteptrs@l
    stw    r5, 0xf0(r0)    /* This must match your Abatron config */
    lis    r6, swapper_pg_dir@h
    ori    r6, r6, swapper_pg_dir@l
    tophys(r5, r5)
    stw    r6, 0(r5)
#endif /* CONFIG_BDI_SWITCH */

swapper_pg_dir为内核页表首地址,r0为内核首地址的虚拟地址,r0+0xf0经过tophys后将转换为物理地址的0xf0,在此处存储swapper_pg_dir

在arch/ppc/kernel/head.S的最后通过abatron_pteptrs保留了8个字节的空间给BDI2000用于保存2个页表指针,如下:

/* Room for two PTE pointers, usually the kernel and current user pointers
 * to their respective root page table.
 */
abatron_pteptrs:
    .space    8

/*
 * Set up the segment registers for a new context.
 */
_GLOBAL(set_context)
    mulli    r3,r3,897    /* multiply context by skew factor */
    rlwinm    r3,r3,4,8,27    /* VSID = (context & 0xfffff) << 4 */
    addis    r3,r3,0x6000    /* Set Ks, Ku bits */
    li    r0,NUM_USER_SEGMENTS
    mtctr    r0

#ifdef CONFIG_BDI_SWITCH
    /* Context switch the PTE pointer for the Abatron BDI2000.
     * The PGDIR is passed as second argument.
     */
    lis    r5, KERNELBASE@h
    lwz    r5, 0xf0(r5)
    stw    r4, 0x4(r5)
#endif
进程切换时将当前进程的页表首地址存储在虚拟地址【KERNELBASE + 0xf0】指向的地址 + 4的偏移处,实际上就是abatron_pteptrs + 4对应的虚拟地址处。

内核调试

在内核开启MMU之前,采用的是物理地址,而符号表中的地址为虚拟地址,因此需要对符号表进行重定位,调试分两个阶段
相关规则可以参考

http://blog.csdn.net/sailor_8318/archive/2009/10/30/4745793.aspx

4.9.2    Debugging of U-Boot before relocation
同时可以利用脚本来加载符号,可参考

http://blog.csdn.net/sailor_8318/archive/2009/10/30/4745793.aspx

4.9.5    GDB script

GDB script

启动GDB时会自动调用内核根目录下的脚本.gdbinit,示例如下:
target remote 150.236.68.212:2001

define add-symbol_mmu_on
        d b
        symbol-file
        add-symbol-file vmlinux 0xC0000000
        b start_kernel
end

define reset
        d b
        symbol-file
        add-symbol-file vmlinux 0
        b __start
end

未开启MMU


PUC8270> 表示BDI2000的命令行窗口
cnbjc0052 sailing/linux-2.6.15.5>表示开发主机
(gdb) 表示是开发主机上的GDB的调试窗口中
=> 表示目标板的命令行

启动开发板
PUC8270>go
开发板启动后等待用户输入
=>
。。。。
Err:   serial
U-Boot relocated to 03fb7000
Net:   FCC1 ETHERNET
=>
停止板子的运行
PUC8270>halt
    Target CPU        : MPC8280/8220/5200 (Zeppo)
    Target state      : debug mode
    Debug entry cause : COP halt
    Current PC        : 0x03feb514
    Current CR        : 0x22000008
    Current MSR       : 0x0000a002
    Current LR        : 0x03fd540c
在__start处设置断点,注意需要先halt,否则断点未能真正设置生效
PUC8270>bi 0xc
Breakpoint identification is 1
重新使开发板运行
PUC8270>go
启动Linux内核
=> boot
*  kernel: cmdline image address = 0x00400000
## Booting kernel from Legacy Image at 00400000 ...
   Image Name:   Linux-2.6.19
   Created:      2009-11-03   8:09:41 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1285009 Bytes =  1.2 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   kernel data at 0x00400040, len = 0x00139b91 (1285009)
   Uncompressing Kernel Image ... OK
   kernel loaded at 0x00000000, end = 0x002cf086
## Current stack ends at 0x03f96530
## cmdline at 0x007fff00 ... 0x007fff00
## kernel board info at 0x007ffeb0
bd address  = 0x03F96AB6
。。。。。。。。
baudrate    =  19200 bps
## No init Ramdisk
   ramdisk start = 0x00000000, ramdisk end = 0x00000000
## initrd_high = 0xffffffff, copy_to_ram = 1
   ramdisk load start = 0x00000000, ramdisk load end = 0x00000000
## Transferring control to Linux (at address 00000000) ...
   Booting using board info...

开发板在0xc断点处停止
-    TARGET: stopped
清除断点,因为8270只支持一个硬件断点,否则后面无法单步调试
PUC8270>ci
连接开发板
cnbjc0052 sailing/linux-2.6.19> ppc_82xx-gdb vmlinux
GNU gdb Red Hat Linux (6.7-1rh)
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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=ppc-linux".
The target architecture is set automatically (currently powerpc:603)
..
0x00000000 in ?? ()
调用reset宏,更新符号表
(gdb) reset
add symbol table from file "vmlinux" at
        .text_addr = 0x0
Reading symbols from /home/sailing/ linux-2.6.15.5/vmlinux...done.
(gdb)

115             .globl  __start
116     __start:
117             mr      r31,r3                  /* save parameters */
118             mr      r30,r4
119             mr      r29,r5
120             mr      r28,r6

当需要调试MMU开启之后的代码,使用下述宏即可
(gdb) add-symbol_mmu_on

开启MMU

PUC8270>go
开发板启动后等待用户输入
=>
。。。。
Err:   serial
U-Boot relocated to 03fb7000
Net:   FCC1 ETHERNET
=>
停止板子的运行
PUC8270>halt
    Target CPU        : MPC8280/8220/5200 (Zeppo)
    Target state      : debug mode
    Debug entry cause : COP halt
    Current PC        : 0x03feb514
    Current CR        : 0x22000008
    Current MSR       : 0x0000a002
    Current LR        : 0x03fd540c
获得MMU开启之后start_kernel的首地址
cnbjc0052 sailing/linux-2.6.15.5> cat System.map | grep start_kernel
c02b2610 T start_kernel
在start_kernel处设置断点,注意需要先halt,否则断点未能真正设置生效
PUC8270>bi 0xc02b2610
Breakpoint identification is 1

重新使开发板运行
PUC8270>go
启动Linux内核
=> boot
*  kernel: cmdline image address = 0x00400000
## Booting kernel from Legacy Image at 00400000 ...
   Image Name:   Linux-2.6.19
   Created:      2009-11-03   8:09:41 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1285009 Bytes =  1.2 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   kernel data at 0x00400040, len = 0x00139b91 (1285009)
   Uncompressing Kernel Image ... OK
   kernel loaded at 0x00000000, end = 0x002cf086
## Current stack ends at 0x03f96530
## cmdline at 0x007fff00 ... 0x007fff00
## kernel board info at 0x007ffeb0
bd address  = 0x03F96AB6
。。。。。。。。
baudrate    =  19200 bps
## No init Ramdisk
   ramdisk start = 0x00000000, ramdisk end = 0x00000000
## initrd_high = 0xffffffff, copy_to_ram = 1
   ramdisk load start = 0x00000000, ramdisk load end = 0x00000000
## Transferring control to Linux (at address 00000000) ...
   Booting using board info...

开发板在start_kernel断点处停止
-    TARGET: stopped
清除断点,因为8270只支持一个硬件断点,否则后面无法单步调试
PUC8270>ci
连接开发板

cnbjc0052 sailing/linux-2.6.19> ppc_82xx-gdb vmlinux
GNU gdb Red Hat Linux (6.7-1rh)
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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=ppc-linux".
The target architecture is set automatically (currently powerpc:603)
..
0xc02b2610 in ?? ()
start_kernel () at init/main.c:479
479     {
(gdb) l 479
474     void __init __attribute__((weak)) smp_setup_processor_id(void)
475     {
476     }
477
478     asmlinkage void __init start_kernel(void)
479     {
480             char * command_line;
481             extern struct kernel_param __start___param[], __stop___param[];
482
483             smp_setup_processor_id();
(gdb)

便可开始内核的调试了,大部分板级的初始化代码将在这之后运行。

若BDI2000的配置脚本中没有MMU和PTBASE选项或者CONFIG_BDI_SWITCH未打开,则在MMU开启之后,使能定时器中断后,将出现异常,无法单步调试。现象如下:
491             time_init();
(gdb)

Program received signal SIGSTOP, Stopped (signal).
time_init () at arch/ppc/kernel/time.c:322
322                             sec = ppc_md.get_rtc_time();
(gdb) n
321                             elapsed += stamp - old_stamp;
(gdb)
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)
322                             sec = ppc_md.get_rtc_time();
(gdb)
321                             elapsed += stamp - old_stamp;
(gdb)
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c248 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c244 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc01bd3fc in time_init () at arch/ppc/kernel/time.c:323
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)

Program received signal SIGSTOP, Stopped (signal).
time_init () at arch/ppc/kernel/time.c:321
321                             elapsed += stamp - old_stamp;
(gdb)
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c248 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c248 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc01bd3fc in time_init () at arch/ppc/kernel/time.c:323
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);

抱歉!评论已关闭.