【转】基于mips架构的uboot 启动流程 (2)连载四:

3) 青铜时代:短暂的回归 cpu/mips/start.S :

/***************************************************************************************/
重新回到汇编的天下,找到代码:
.globl relocate_code
.ent relocate_code
relocate_code:
下面的代码就是搬家了。

直到出现代码:
move a0, a1
la t9, board_init_r /* doesn't return, runs main_loop() */
j t9
程序搬家基本完成,后面的程序就可以全部在内存 DRAM 中执行了,速度会比之前在 FLASH 和 scratch memory 中运行的速度快上很多。这里跳入的代码段 board_init_r 是在 C 程序中定义的函数,仍然在刚才的那个 C 语言文件 lib_mips/board.c 中。

4) 白银时代:终于有正常的 C 环境接着进行初始化了:

/***************************************************************************************/
进入 board_init_r 函数之前,有一段让人振奋的注释:
/* This is the next part if the initialization sequence: we are now
* running from RAM and have a "normal" C environment, i. e. global
* data can be written, BSS has been cleared, the stack size in not
* that critical any more, etc.
*/
然后注意到该函数有两个传入的参数,参数是之前汇编中用 a0 寄存器传入的,在这里可以看出这两个参数的含义:
id: 之前在 U-boot 的 1M 空间中分配的 GD 结构体的地址
dest_addr: U-boot 重新定位到 DRAM 之后的代码起始地址

程序接着向下是将 id 的值用 k0 寄存器保存,将 GD 结构体中的一些字段进行设置,包括记录 U-boot 自身的代码在内存中的偏移地址等。

/***************************************************************************************/
接着是重新计算命令表 (cmd table) 的地址。什么是命令表?因为 U-boot 启动完成后可以进入命令行模式,这时候用户可以从串口输入命令来指示 U-boot 下一步做什么,每个命令对应的名称、用法、描述、执行的函数等信息,用一个命令表结构体保存,这样每一个命令在内存中有对应的一个命令表。结构体的定义在 include/Command.h 中,定义如下:
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* Implementation function */
char *usage; /* Usage message (short) */
char *help; /* Help message (long) */
} __attribute__ ((aligned (8)));
而这里给命令表重新计算地址其实只是将从 __u_boot_cmd_start 到 __u_boot_cmd_end 之间的每个命令表中的成员指针的地址加上 U-boot 在 DRAM 中的偏移地址,这样获得命令表在 DRAM 中的地址。看来转换之前的命令表中的地址应该是相对地址( ?! )。
注: 这里,注意到 __attribute((XXX)) 比较奇特的语法,其实这个是 GCC 对 C 语言的扩充, GCC 允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代码检查。要指定一个声明的属性,在声明后写
__attribute__ (( ATTRIBUTE ))
其中 ATTRIBUTE 是属性说明,多个属性以逗号分隔。 GNU C 支持十几个属性,如 noreturn, unused, aligned 等。

/***************************************************************************************/
然后是初始化 malloc() 堆空间:
mem_malloc_init();
其实是将全局变量 mem_malloc_start 和 mem_malloc_end 和 mem_malloc_brk 三个指针指向之前分配好的堆空间。

然后是重定位或者初始化环境变量的指针:
env_relocate();
将 env_ptr 指针及其指向的地址初始化,用来存放环境变量结构体,然后将 flash 中的环境变量拷贝到内存中。

然后是其余设备的初始化 devices_init() ,这是在前面的堆空间 (malloc) 、环境变量、 PCI 总线初始化后的基础之上才能进行的,这里的设备包括:
i2c_init ();
drv_lcd_init ();
drv_video_init ();
drv_keyboard_init ();
drv_logbuff_init ();
drv_system_init ();
drv_usbtty_init ();
...

然后是将标准的输入输出 std* 变量由通过串口改为通过 pci_console_active 进行输入输出。
然后是 jump table 的初始化,这里似乎是将一些函数指针记录进 GD 结构体。
然后是 console 初始化。
然后是再次打印 board/chip info ,这里打印是为了自检和板子确认。
然后是确认 loadaddr 和 bootfile 环境变量的有效性。
然后是 miscellaneous platform dependent 的初始化,函数 misc_init_r () 。
然后是网卡的初始化, eth_initialize() 。
然后是 IDE 的检测和初始化, ide_init() 。
然后是 debugger 的设置。
最后是一个空函数 late_board_init() ,用来添加比较晚的初始化代码。
下面就进入了一个死循环,循环调用 main_loop() 函数,这意味着 U-boot 基本启动完毕,进入命令行模式。

--电子创新网--
粤ICP备12070055号