当前位置: 首页 > U-Boot随笔 > 正文

u-boot移植随笔(11):一些内存地址的研究(gd_t和bd_t结构体)

李迟按:学计算机的人不应该对二进制反感,因为计算机只认识0和1,但面对许多枯燥的0和1,有些人望而却步。我大约是大三、大四时候开始强迫自己认真对待二进制的,最早应该是研究SD卡时候,其时主要研究FAT表,使用WinHex工具;后来研究网络协议,主要是ENC28J60芯片,代码是国外一个组织写的,基于AVR芯片,这个实践算是理论结合实践。再后来,慢慢对底层的东西感兴趣,不断看,研究,心里老想着数据在内存中如何分布的(gdb是个强大工具)。现在,对于研究某个文件的结构,直接分析二进制数据是最好的方法。因此,建议看到此文的同志,不妨对自己狠点,过了这个坎,后面将是一马平川。正如那些说学英文很难的同志,如果肯下点苦功(辛苦的“苦”),相信勤能补拙,定会有收获。然而,作为当局者的我们,又谈何容易?
如果在./arch/arm/lib/board.c文件中定义了DEBUG宏的话,在u-boot启动时就会打印u-boot映像在内存中地址以及其它一些信息。如:

U-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内存分布的例子:u-boot移植随笔(5):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,这在前面的文章:u-boot移植随笔(5):u-boot的内存分布图中证明了——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…3O.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

本文固定链接: http://www.latelee.org/porting-uboot/u-boot-porting-gdt-bdt.html

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter