LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
12
返回列表 发新帖
楼主: 虚名界

open source game

[复制链接]
 楼主| 发表于 2008-6-24 21:55:31 | 显示全部楼层
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-6-24 21:56:12 | 显示全部楼层
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-6-29 21:42:18 | 显示全部楼层
U-Boot的移植之(四)调试篇:下载U-Boot到目标板进行调试
U-Boot Practically Porting Guide

Author: Aaron Wong aaronwong@engineer.com

U-Boot的移植之(四)调试篇:下载U-Boot到目标板进行调试

编译完成之后,得到的几个重要文件是:

(1) u-boot.bin: 116K,原始二进制文件,用于下载到启动ROM进行系统引导;

(2) u-boot: 384K,ELF格式映像文件,可加载到SDRAM或SRAM中进行调试;

(3) u-boot.srec: Motorola S-Records格式映像。

(4) System.map: U-Boot映像文件的符号表,各符号的链接地址。


最有效的调试方法是下载到目标板的启动闪存,使用硬件仿真器进行跟踪调试。使用Skyeye,Qemu等软件仿真器不能达到真实的调试效果,尤其不能真实反映第一阶段的底层初始化过程,只适合作U-Boot的学习与研究之用。有人提出在没有硬件仿真器的情况下,使用“点灯**(利用目标板的LED指示程序运行阶段)”进行跟踪调试,这实际上无异于盲人摸象,特别是在底层初始化阶段,一条指令就可能导致异常。也有人提出注释掉start.S中的lowlevel_init调用,将U-Boot映像加载到SDRAM中进行调试,这实际上只能对U-Boot进行功能调试,而无法跟踪U-Boot的底层初始化过程。当然,如果实在嫌烧写FLASH的速度较慢,又心疼其擦写寿命,也可以将U-Boot映像加载到片上SRAM中调试,因为U-Boot的开始一部分代码是位置无关的(除了后6个异常向量外,不过并不构成影响);这要求片上SRAM够大,因为U-Boot的映像大小约有300K-400K。


笔者使用Banyan-U ARM EMULATOR JTAG仿真器,结合AXD软件平台进行调试。

首先将u-boot.bin下载到FLASH地址0x0,连接好串口,启动minicom或超级终端,目标板上电后,串口控制台无任何输出。这很有可能是lowlevel_init那段代码出了问题,因为它牵涉到GPIO的配置,处理器时钟频率设置,系统总线频率与存储器的时序匹配及初始化,稍有差错就会当机。当然也有可能是串口的配置不正确,但这部分比较简单,出错的可能性比较小。

对于下载到FLASH存储器的原始二进制文件,只能进行汇编级的跟踪调试。先利用objdump工具生成U-Boot映像的反汇编代码:

    arm-iwmmxt-linux-gnueabi-objdump -S u-boot > u-boot.S

反汇编代码u-boot.S和符号表System.map将是跟踪调试过程中的得力助手。

另一个重要的调试技巧是在AXD中现场修改寄存器和存储单元的内容,这样可以帮助我们找到问题所在,而不必每次改动都重新编译u-boot,也避免了FLASH的频繁烧写。

例如,笔者在单步跟踪调试时,发现在地址0xa30804b4处,使用指令”str r1, [r0]”配置GPDR1(GPIO方向寄存器1)后,存储器0x0地址开始的大片内容全部被更改,导致异常终止。这时可以在该处设置一个断点,复位目标板全速运行到断点处,修改寄存器r1的值(GPDR1的初值),再执行该条指令。经试验发现,对于XSBASE270开发板,必须要先初始化GAFRx,再初始化GPDRx,才不致于发生上述异常。而在start.S中,是先完成GPDRx的初始化之后,再初始化GAFRx的,因此需要在源代码中将这两段代码的位置互换,重新编译后,再下载到FLASH中。

U-Boot的串口控制台输出如下:

    U-Boot 1.3.0-rc2 (Oct 16 2007 - 01:57:29)

    DRAM: 64 MB

    Flash: 32 MB

    In: serial

    Out: serial

    Err: serial

    Hit any key to stop autoboot: 0

    $


另一个问题是环境变量的设置与保存。将环境变量保存在FLASH中,使用setenv命令设置环境变量,再使用saveenv命令保存,这样在下次开机时,就会使用新的环境变量。如果使用的是U-Boot自带的CFI闪存驱动,在保存环境变量时可能会出现如下问题:

    $ setenv ipaddr 192.168.1.21

    $ saveenv

    Saving Environment to Flash...

    Un-Protected 1 sectors

    Erasing Flash...

    Flash erase error at address 40000

    Block Erase Error.

    Block locked.

    done

    Erased 1 sectors

这是因为缺省情况下U-Boot对FLASH有软件写保护,这时在U-Boot启动完毕后即使使用jflashmm工具也无法对FLASH进行烧写:

    [aaronwong@localhost Jflash-XSBase270]$ sudo ./jflashmm u-boot.bin


    JFLASH Version 5.01.007

    COPYRIGHT (C) 2000 - 2003 Intel Corporation


    PLATFORM SELECTION:

    Processor= PXA27x

    Development System= XSBase270

    Data Version= 1.00.001


    PXA27x revision ??

    Found flash type: 28F128K18


    Erasing block at address 0

    Error, Block erase timed out

解决办法可参考Uboot-Users邮件列表Erase error on dual P30(CFI) flash chips主题讨论,具体是在include/configs/xsbase270.h中定义CFG_FLASH_PROTECTION,该选项在README文件中的描述如下:

    - CFG_FLASH_PROTECTION

    If defined, hardware flash sectors protection is used

    instead of U-Boot software protection.

修改完毕重新编译U-Boot,在目标板上电后U-Boot启动完毕之前,使用jflashmm工具将新的u-boot.bin烧写到目标板启动闪存。这时可成功修改环境变量并保存到FLASH中:

    $ setenv ipaddr 192.168.1.21

    $ saveenv

    Saving Environment to Flash...

    . done

    Un-Protected 1 sectors

    Erasing Flash...

    . done

    Erased 1 sectors

    Writing to Flash... done

    . done

    Protected 1 sectors


一旦U-Boot的基本功能调试通过,能正常在目标板运行,剩余的工作就是根据实际情况调整TEXT_BASE以及内核引导参数,使用U-Boot来引导Linux内核。在下一节中,将给出U-Boot引导Linux内核的实例。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-6-29 23:42:47 | 显示全部楼层
以下内容来自笔者在中国Linux论坛Linux嵌入技术讨论区的张贴:
aaronwong: u-boot中代码的疑问(_armboot_start与_start)?
---------------------------
我使用的是u-boot-1.3.0-rc2。在cpu/pxa/start.S中,有如下的标号定义:
_TEXT_BASE:
.word TEXT_BASE /*uboot映像在SDRAM中的重定位地址,我设置为0xa170 0000 */

.globl _armboot_start
_armboot_start:
.word _start /*_start是程序入口,链接完毕它的值应该是0xa170 0000=TEXT_BASE*/
/* 这句话的意思应该是在_armboot_start标号处,保存了_start的值,也就是说,_armboot_start是存放_start的地址,该地址对应的存储单元内容是0xa170 0000*/
/*
* These are defined in the board-specific linker script. 下面的定义与上面应该是一个意思。
*/
.globl _bss_start
_bss_start:
.word __bss_start
======================
按照上面的理解,__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是__bss_start - _start;在relocate代码段中计算uboot的大小时,也体现了这一点。
实际上,_armboot_start并没有实际意义,它只是在"ldr r2, _armboot_start"中用來寻址_start的值而已,_bss_start也是一样的道理,真正有意义的应该是_start和 __bss_start本身。
但是,令我不解的是,在C入口函数start_armboot()中(对应文件为lib_arm/board.c),有如下代码:
void start_armboot (void)
{
.........
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); //第一句话
..........
monitor_flash_len = _bss_start - _armboot_start; //第二句话
...............
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //第三句话
..........
}
==============================================
按照上面的理解,_armboot_start与_bss_start都是没有实际意义的,它们只是一个地址,有实际意义的是地址中的内容_start和 __bss_start(虽然也还是地址)。象第一句话,其“意图”很明显,是把gd作为全局数据结构体的指针,并初始化为“SDRAM中的uboot起始地址(即TEXT_BASE)-CFG_MALLOC_LEN-全局数据结构体大小”。
要实现这个“意图”,应该是写成:gd = (gd_t*)(_start - CFG_MALLOC_LEN - sizeof(gd_t));或者gd = (gd_t*)(TEXT_BASE- CFG_MALLOC_LEN - sizeof(gd_t));才对阿?用_armboot_start来作运算应该是没有任何意义才对!?
第二句话也是一样的道理,它的意图是要计算u-boot映像的大小,应该写成__bss_start - _start才对阿?
我使用readelf工具查看编译所得到的uboot映像文件得到信息如下:
[aaronwong@localhost build]$ readelf -s u-boot|grep _start
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
上面我删除了与该讨论无关的包含“_start""t的标号信息。
显然,我前面的理解应该是正确的(_start=TEXT_BASE=0xa170 0000)。那么u-boot源代码中的monitor_flash_len=_bss_start - _armboot_start=0xa1700048 - 0xa1700044 = 4,有什么意义??
迷茫中,期盼大虾指点迷津,谢谢~!!!
eltshan: [Re: aaronwong]
-----------------
1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start
1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start
1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start
1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start

我想:
_start所在的地址是a1700000,
_armboot_start 所在的地址是a1700044,
那么 根据这句:
_armboot_start: .word _start
所以_armboot_start的值应该是a1700000

所以
monitor_flash_len = _bss_start - _armboot_start = a171b070 - a1700000 = 1b070
而不是你说的 = 4

以上个人意见.
aaronwong: [Re: eltshan]
-------------------
谢谢,eltshan!你的理解是正确的,不过我看了之后还是没能想得很明白,因为我在想,按你所说,那么_start的值应该是多少呢?难道是“b reset”这条指令的机器码?所以我对ELF格式的u-boot映像文件作了反汇编,分析之后终于找到了症结所在。以下是部分分析过程,首先是反汇编:
arm-iwmmxt-linux-gnueabi-objectdump -D u-boot > u-boot.s
并提取了monitor_flash_len = _bss_start - _armboot_start;这条语句相关的反汇编代码如下:
==============================
a1700044 <_armboot_start>:
a1700044: a1700000 .word 0xa1700000

a1700048 <_bss_start>:
a1700048: a171b070 .word 0xa171b070

a171b070 <monitor_flash_len>:
a171b070: 00000000 .word 0x00000000

.....
a1700f40: e59f41d0 ldr r4, [pc, #464] ; a1701118 <start_armboot+0x1dc>
//r4=[a1701118]=a1700044
.....
a1700f7c: e59f3198 ldr r3, [pc, #408] ; a170111c <start_armboot+0x1e0>
//r3=[a1700044]=a1700048
a1700f80: e5942000 ldr r2, [r4]
//r2=[a1700044]=a1700000
a1700f84: e59f4194 ldr r4, [pc, #404] ; a1701120 <start_armboot+0x1e4>
//r4=[a1701120]=a1719d24
a1700f88: e5933000 ldr r3, [r3]
//r3=[a1700048]=a171b070
a1700f8c: e0623003 rsb r3, r2, r3
//r3= r3-r2 = a171b070-a1700000 = 1b070;
a1700f90: e59f218c ldr r2, [pc, #396] ; a1701124 <start_armboot+0x1e8>
//r2=[a1701124]=a171b070
a1700f94: e5823000 str r3, [r2]
//monitor_flash_len=[r2]=r3=1b070
......

a1701118: a1700044 .word 0xa1700044
a170111c: a1700048 .word 0xa1700048
a1701120: a1719d24 .word 0xa1719d24
a1701124: a171b070 .word 0xa171b070
========================================
上面//是我自己的注释。这表明,你的理解的确是正确的。
经过这个过程之后,我终于认识到自己的误解在哪里了。原来,我是把"汇编语言中LDR伪指令对符号的引用"与"C语言中对汇编程序中符号/常量/变量的引用"搞混淆了。我想说明以下几点:

(1) readelf以及u-boot.map和System.map所给出的符号表中符号的值,实际上是表示符号所在的地址,而不是指符号本身的值。

(2) 汇编语言中没有指针的概念,因此对符号的引用是"赤裸裸"的。例如:
==========
.globl _armboot_start
_armboot_start: .word _start
ldr r2, _armboot_start
==========
实际上反汇编以后是:
============
a1700044 <_armboot_start>:
a1700044: a1700000 .word 0xa1700000
a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044 <_armboot_start>
============
也就是说,_armboot_start是一个地址0xa1700044,其中的内容是0xa1700000,上面对_armboot_start的引用是直接将其替换为其表示的地址0xa1700044,而非其中的内容0xa1700000。这就是"赤裸裸"的引用。

(3) C语言则不同,对变量/符号/常量的引用必须要通过地址来寻址,不管是全局变量还是局部变量,不同的是局部变量在生命期结束后,所占的地址空间会被释放而已。即使是函数调用时的参数传递,虽然是将实参的值"拷贝"给形参,但"拷贝"的过程也是通过实参和形参的地址来对两者进行访问的。
所以,在C语言中的 "monitor_flash_len = _bss_start - _armboot_start" 这句话中对_armboot_star的引用,实际上是把它用作了指针,把它作为访问对象的地址来使用,通过这个地址即a1700044 来访问对应存储空间所存放的内容亦即0xa1700000,_bss_start也是同样的道理。所以这句话实际上是monitor_flash_len =[a1700048]-[0xa1700044]=a171b070-a1700000 = 1b070,这样就得到了正确的结果。

现在,我们再回答最前面的问题:_start的值是什么?_start 表示地址0xa1700000 ,在汇编语言中,对_start的"绝对引用"(这里是与用相对寻址进行跳转进行区别)就是将其替换为0xa1700000,但其中存放的内容的的确确就是"b reset"这条指令的机器码,所以如果在C语言中引用_start,得到的结果反而就是这个指令的机器码了。其实这个问题很简单,只是和C语言的引用搅在一起,一些概念被偷换了而已。

以上是我的个人理解,有什么不对,还请指正。

再次感谢eltshan的提点。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表