如果在./arch/arm/lib/board.c文件中定义了DEBUG宏的话,在u-boot启动时就会打印u-boot映像在内存中地址以及其它一些信息。如:
-Boot 2010.09-svn40 (Mar 06 2011 - 13:15:44) Hello from Late Lee<http://latelee.org> U-Boot code: 33F80000 -> 33F9CE80 BSS: -> 33FA20DC I2C: ready RAM Configuration: Bank #0: 30000000 64 MiB Flash: 8 MiB In: serial Out: serial Err: serial Net: dm9000 Hit any key to stop autoboot: 0
可以看到u-boot在内存中的地址为33F80000。这个地址可以在相应的开发板(我的是smdk2440目录)中的config.mk文件中找到说明。在编译生成的System.map文件中第一行为:33f80000 T _start
使用md.b查看这个地址,可以看到:
LATE2440 $ md.b 33f80000 33f80000: 12 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................ 33f80010: 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................ 33f80020: 80 01 f8 33 e0 01 f8 33 40 02 f8 33 a0 02 f8 33 ...3...3@..3...3 33f80030: 00 03 f8 33 60 03 f8 33 c0 03 f8 33 ef be ad de ...3`..3...3....
如果使用UltraEdit打开编译生成的u-boot.bin文件,可以发现前面4行的内容是一样的。因为u-boot启动时将自己搬到了33f80000这个地址了。
在很久以前我写了个测试u-boot内存分布的例子,里面将gd_t和bd_t结构体的地址打印出来了。——其实,前天遇到那个未定义指令的错误中就包含有大量有用的信息。
undefined instruction pc : [<3000801c>] lr : [<33f95030>] sp : 33f4fb60 ip : 000000ff fp : 00000000 r10: 00000000 r9 : 33edc6d0 r8 : 33f4ffe0 r7 : 30008000 r6 : 33f4ffc4 r5 : fffff200 r4 : 000003f0 r3 : 00000000 r2 : 30000100 r1 : ea000012 r0 : 33f98305 Flags: nzCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
在代码中经常看到一个宏:
DECLARE_GLOBAL_DATA_PTR;
它就是将gd_t结构体地址放到寄存器r8中,这里看到r8的内容是33f4ffe0(至于其它寄存器的内容,这里不说了)。好,查看一下这个地址:
LATE2440 $ md.b 33f4ffe0 /*gd_t结构体的地址*/ 33f4ffe0: c4 ff f4 33 03 00 00 00 00 c2 01 00 01 00 00 00 ...3............ 33f4fff0: 0c 00 f5 33 01 00 00 00 00 00 00 00 50 00 f6 33 ...3........P..3 33f50000: 00 00 00 00 09 00 01 00 03 b0 81 24 62 6f 6f 74 ...........$boot 33f50010: 63 6d 64 3d 63 70 2e 6c 20 30 78 35 30 30 30 30 cmd=cp.l 0x50000
我们知道,bd_t是一个结构体(这是废话)。它的定义如下:
typedef struct global_data { bd_t *bd; unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long fb_base; /* base address of frame buffer */ void **jt; /* jump table */ } gd_t;
它的sizeof为32,这在前面的文章中证明了——4(大小)*8(成员) = 32。
现在就对照一下前面看到的地址中的内容,看看这个结构体变成了怎么样了(ARM默认为小端模式(?),而看到的地址是增长的,因此看到的数据都是反过来的)。
1、bd:指向bd_t的指针。c4 ff f4 33=>0x33f4ffc4,即bd_t结构体的地址(这里,指针占4字节)。2、flags:标志,说明见下。03 00 00 00=>3,即flags为3,gd->flags |= GD_FLG_RELOC(宏定义,值为1)(board.c),gd->flags |= GD_FLG_DEVINIT(宏定义,值为2)(console.c),它们分别表示代码已经加载(relocate)到RAM、设备已经初始化好了。3、baudrate:波特率。00 c2 01 00=> 0x1c200,即115200(dec),表示波特率为115200。4、have_console:是否有控制台(终端,或串口之类的),01 00 00 00=>1,表示调用了serial_init函数。5、env_addr:环境变量的地址,0c 00 f5 33=>0x33f5000c,环境变量结构体地址(就是看到的bootxxx那个地址)。6、env_valid:环境变量checksum有效乎?01 00 00 00=>1,环境变量checksum有效。7、fb_base:frame buffer的基地址,00 00 00 00=>0,暂时没有研究。8、jt:一个jump table,50 00 f6 33=>0x33f60050,jump table有待研究。(查看这个地址得到许多其它的“地址”,见下)
(注:关于env_addr和env_valid在代码中的用法,见./common/env_common.c中代码。)
我们再来看看bd_t结构体地址:
LATE2440 $ md.b 33f4ffc4 33f4ffc4: 00 c2 01 00 c0 a8 01 c8 00 00 00 00 f0 03 00 00 ................ 33f4ffd4: 00 01 00 30 00 00 00 30 00 00 00 04 c4 ff f4 33 ...0...0.......3 33f4ffe4: 03 00 00 00 00 c2 01 00 01 00 00 00 0c 00 f5 33 ...............3 33f4fff4: 01 00 00 00 00 00 00 00 50 00 f6 33 00 00 00 00 ........P..3....
它的定义如下:
typedef struct bd_info { int bi_baudrate; /* serial console baudrate */ unsigned long bi_ip_addr; /* IP Address */ struct environment_s *bi_env; ulong bi_arch_number; /* unique id for this board */ ulong bi_boot_params; /* where this board expects params */ struct /* RAM configuration */ { ulong start; ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS]; } bd_t;
它的sizeof为28,同样在前面的文章中证实了。
我们也分析一下:
1、bi_baudrate:波特率,00 c2 01 00=> 0x1c200,即115200(dec),表示波特率为115200。2、bi_ip_addr:c0 a8 01 c8=> 十进制为:192 168 1 200,即服务器IP地址(注意:网络为大端模式)。 gd->bd->bi_ip_addr
= getenv_IPaddr ("ipaddr");(board.c)
3、bi_env:环境变量?00 00 00 00=> 0,没研究过。4、bi_arch_number:machine type id。f0 03 00 00=>0x000003f0,开发板机器ID,即1008(MACH_TYPE_SMDK2440),gd->bd->bi_arch_number = MACH_TYPE_SMDK2440; (smdk2440.c)5、bi_boot_params:准确地说,这是启动参数的地址,00 01 00 30=>0x30000100,gd->bd->bi_boot_params =0x30000100;(smdk2440.c),与内核中需要一致。6、start:内存起始地址,00 00 00 30=>0x30000000,RAM起始地址,即0x30000000。7、size:内存大小,00 00 00 04=>0x04000000,RAM大小,64MB,来自config.mk文件的:SMDK2410(SMDK2440亦一样) has 1 bank of 64 MB DRAM
8、04 c4 ff f4 33 就是gd_t结构体的地址了,前面分析过了。
其实不用这么麻烦的,直接在u-boot下输入db就可以查看开发板的一些信息了(看代码就知道,其实两种方法没本质的区别)。
LATE2440 $ bd arch_number = 0x000003F0 env_t = 0x00000000 boot_params = 0x30000100 DRAM bank = 0x00000000 -> start = 0x30000000 -> size = 0x04000000 ethaddr = 6c:61:74:65:6c:65 ip_addr = 192.168.1.200 baudrate = 115200 bps
前面说到的jump table的地址内容是这样的:
LATE2440 $ md.b 33f60050 33f60050: 24 c8 f8 33 a4 49 f8 33 00 4a f8 33 ac 49 f8 33 $..3.I.3.J.3.I.3 33f60060: 38 4a f8 33 08 b1 f8 33 20 c8 f8 33 20 c8 f8 33 8J.3...3 ..3 ..3 33f60070: 10 b7 f8 33 d0 b4 f8 33 a4 15 f9 33 5c 4f f9 33 ...3...3...3/O.3 33f60080: d4 b0 f8 33 d4 51 f9 33 ec 99 f8 33 b4 9e f8 33 ...3.Q.3...3...3
里面就是一些33f8xxxx地址,具体的,查了也没什么发现。jump table留到以后有机会再看看。
这次的实验,加上以前画的那张图,结合起来,可以对u-boot在内存中分布有一些了解了。
补记(2011-3-20):
前面说的大端小端模式,有必要澄清一下:一个字节是最小的单位,因此它不可能存在大端小端的问题,这些模式只针对大于一个字节以上的数据,比如我们看到的地址是4个字节(32bit),因此md.b看到的数据就要反过来,但是像环境变量这些可读字符,不能用这个规则来读,即我们看到的、在内存显示的,就是它们实际的形式。
2011-3-22记:
经过初步测试,发现jump table里面的地址是一些常用函数,或者称为库函数,如33f8c824地址是get_version函数。其它的如serial_getc、serial_puts、printf、malloc、free、getenv、setenv、i2c_write、i2c_read,等等。参见exports.c、exports.h和_exports.h等等文件。
木草山人于3.07