cortex-a8 uboot系列:第六章 uboot源码分析2-启动第二阶段
0赞第一阶段结束后,调用了start_armboot函数。开始第二阶段。
start_armboot函数在lib_arm/board.c文件里。
一、 uboot第二阶段
uboot第一阶段主要就是初始化了soc内部的部件(如看门狗,时钟),然后初始化DDR并且完成重定位,建立MMU映射表,启动MMU。
Uboot第二阶段就是要初始化剩下的还没有被初始化的硬件。主要是soc外部的硬件(如iNand,网卡芯片……),uboot本身的一些东西(uboot命令,环境变量……)。然后最终初始化完必要的东西后进入uboot的命令行准备接收命令。
1. uboot的完结
uboot启动后自动运行打印很多信息(这些信息就是uboot在第一和第二阶段不断进行初始化时,打印出来的信息)。然后uboot进入了倒数bootdelay秒然后执行bootcmd对应的启动命令。
如果用户没有干涉,则会执行bootcmd进入自动启动内核流程(uboot就死掉了)。此时用户可以按下空格键打断uboot的自启动进入uboot的命令行下。然后uboot就一直工作在命令行下。
Uboot的命令行就是一个死循环,循环体内不断重复:接收命令、解析命令、执行命令。这就是uboot最终的归宿。
start_armboot函数最后就是一个无限for循环,执行main_loop函数。这个函数就是读取命令,然后执行命令。
二、 start_armboot解析1
init_fnc_t定义
这是一个函数类型,
typedef int (*init_fnc_t) (void) 这个是函数指针。参数是void,函数返回值是int型。
init_fnq_ptr 是一个二重函数指针。
二重指针作用两个:一个是用来指向一重指针,另一个是指向指针数组。
因此这里的init_fnq_ptr可以用来指向函数指针数组。
该宏在include\asm-asm目录下的global_data.h文件中。
这个宏定义了一个全局变量,名字叫gd,这个全局变量是一个指针类型,占用4个字节,用volatile修饰表示可变,用register修饰表示这个变量要尽量放到寄存器中,后面的asm(“r8”),是gcc支持的一种语法,意思是要把gd放到寄存器r8中。
综合分析,DECLARE_GLOBAL_DATA_PTR就是定义了一个要放在寄存器r8中的全局变量,名字叫gd,类型是一个指向gd_t类型变量的指针。
为什么要把这个变量定义为register?因为这个全局变量gd(global data的简称)是uboot中很重要的一个全局变量(这个全局变量是一个结构体,里面有很多内容,这些内容加起来构成的结构体就是uboot中常用的所有的全局变量),这个gd在程序中经常被访问,因此放在register中提升效率。因此纯粹是运行效率方面的考虑,和功能要求无关,并不是必须。
gd_t定义在include/asm-arm的global_data.h中。该结构体将uboot会用到的全局变量封装到一起。
bd_t:存放开发板的一些信息。
flags: 存放标志位
baudrate: 控制台的波特率
have_console: bool类型,表示控制台是否使用。
reloc_off: 重定位的偏移量
env_addr: 环境变量的地址
env_valid: bool类型,表明内存中的环境变量是否有效
fb_base: fram buffer的基地址
vfd_type:
jt: 跳转表
对于bd_t结构体,保存的是板子的信息。
bi_baudrate: 开发板的波特率,和控制台的波特率一样。
bi_ip_add: 开发板的ip地址
bi_enetaddr: 开发板的物理地址
bi_env: 环境变量的指针
bi_arch_number: 板子的机器码
bi_boot_params: 启动参数(将来传给linux的参数)地址
bi_dram: 外部dram信息结构体,内部两个变量,一个起始地址,一个大小。程序通过这个参数可以知道开发板外部接的DRAM信息
bi_enet1addr: 第二个网卡的物理地址(一般都不用)
总之,gd_t中定义了很多全局变量,都是整个uboot使用的,其中有一个bd_t类型的结构体,指向一个bd_t类型的变量,这个bd是开发板的板级信息的结构体,里面有不少硬件相关的参数,如波特率、IP地址、机器码、DDR内存分布。
内存使用排布:
DECLARE_GLOBAL_DATA_PTR只是定义了一个指针,也就是说gd里的全局变量并没有分配内存,所以在使用gd之前要给他分配内存,否则gd也只是个野指针而已。
gd和bd需要内存,内存当前没有被管理(因为没有操作系统统一管理内存),大片的DDR内存可以随意使用(只要使用内存地址直接访问内存即可)。但是也不能太随意使用,要考虑uboot其他地方用到的内存。
因此在uboot中需要有一个整体规划。
内存排布:
1. uboot区
CFG_UBOOT_BASE + XXX,长度为uboot的实际长度
Uboot中定义CFG_UBOOT_BASE为0x33e0_0000。
CFG_UBOOT_SIZE 为 2M。
2. 堆区
CFG_MALLOC_LEN为堆区的长度,计算可知为912K,大致1M左右。
CFG_ENV_SIZE为环境变量的大小。为16K。
所以malloc长度为 16 + 896 = 912K。
3. 栈区
CFG_STACK_SIZE大小为512K。
4. gd
长度为sizeof(gd_t),为36个字节。
5. bd
长度为sizeof(bd_t),实际大小大占44个字节。
根据gd_base的计算:
所以gd_base = 0x33e00000 + 0x200000(2M) – 912k – 512k – 36(gd_t结构体的大小为36字节)
这样,就知道了gd指针的地址。也就是全局变量存放的首地址。
所以此时内存分布为:
将gd区域全部给初始化为0。同时给gd中的bd确定内存地址。bd的地址就是gd的地址向下偏移sizeof(bd_t)大小.
为了防止高版本GCC的优化对内存造成的错误,要加这一句代码。