U-BOOT分析(三)之uboot启动流程
目前ARM一般的启动过程
- 一般来说开发板/(芯片)的启动通常分为这么个阶段:
Internal Rom-> SPL(BL1)->uboot(BL2)->Linux kernel -> file system -> start application-
Internal Rom 里边是芯片厂商内置固化的一小段代码(RomBoot)主要作用有一下几个:
- 初始化部分硬件。
- 启动模式boot mode选择。
- 读取外部固件中的SPL程序到片内RAM。
- 跳转到RAM去执行这段SPL程序,把控制权交给用户程序。
- 另外可能还有厂商的测试程序;以及为了安全性考虑,检查和加密解密外部导入的固件
-
SPL(BL1)(全称Secondary programloader):由于RomBoot会根据pin启动模式选择读取对应介质一小段数据到内存,但是读取多少合适呢,每个用户的需求不一样,芯片厂商一般固定就只读4K/8K/16K等很小一段数据,这段数据可以用来芯片初始化,读取介质数据到内存,主要负责初始化芯片,搬移uboot第二阶段的代码到系统内存(System Ram,也叫片外内存)中运行,是uboot第一阶段执行的代码。所以SPL不能太大,RomBoot会读取不完整导致加载失败。
uboot-spl.bin由uboot编译生成,对应于BL1阶段,也就是BL1的镜像,uboot-spl.bin。其代码运行于IRAM中:- 初始化部分时钟(和SDRAM相关)
- 初始化DDR(外部SDRAM)
- 从存储介质上(比如SDeMMC and flash)将BL2镜像加载到SDRAM上
- 验证BL2镜像的合法性
- 跳转到BL2镜像所在的地址上
-
uboot(BL2):主要负责初始化外部设备,引导Kernel启动,C代码实现,uboot.bin由uboot编译生成,其基本功能:
- 初始化部分硬件,包括时钟、内存等等
- 加载内核到内存上
- 加载文件系统、atags或者dtb到内存上
- 根据操作系统启动要求正确配置好一些硬件
- 启动操作系统
-
uboot启动流程:
uboot启动流程,就和我们学习写第一个代码“hellword”一样,先找到一个入口,main函数就是这个入口,而uboot里边,程序是由链接脚本决定的,链接脚本规定了哪个是程序的入口,下载官方uboot 解压后会在路径arch/arm/cpu/有u-boot.lds这个基础链接文件,但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds
下图是编译前的u-boot.lds文件部分内容,路径arch/arm/cpu/u-boot.lds链接脚本:
关注 43 44 45 行就可以了:
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start)
*(.vectors)
CPUDIR/start.o (.text*)
}
ENTRY(_start)是代码当前入口点:而_start 在文件 arch/arm/lib/vectors.S 中有定义,之后就是向量表,说明一下这个_start 必须是 global 声明的。
上图框选的代码.section “.vectors”, "ax”表示将后面的代码放到.vectors段中。
看到这句话之后Vectors have their own section so linker script can map them easily,所以我们找一个之前编译过的u-boot.map(地址映射文件)来看下
可以看出.__image_copy_start) 在映射文件中可以看到地址为 0X5fb00000,而.text 的起始地址也是0X5fb00000,中断向量表.vectors第一个也就是 _reset向量地址也是0X5fb00000,由vectors 段保存中断向量表,说明整个 uboot 的起始地址就是 0X5fb00000。紧接其后是arch/arm/cpu/arm1176/start.S编译之后的代码.
之后的主要执行流程是
reset -> cpu_init_crit -> lowlevel_init -> _main
接着bl _main跳转到arch\arm\lib\crt0.S中从入口_main开始执行
然后里边 bl board_init_f
跳转执行 board_init_f ->relocate_code -> board_init_r
这里的board_init_f在 common\board_f.c定义,board_init_f通过initcall_run_list(init_sequence_f)函数执行一系列初始化函数以实现前半部分板级初始化
全局结构体gd在arch\arm\include\asm\global_data.h中声明:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r9”)
init_sequence_f的定义在同文件的 board_init_f函数定义上边
框出的是一系列板级初始化函数列表包括PLL,Serial etc.
\arch\arm\lib\relocate.S:relocate_code实现uboot代码的重定位
board_init_r指令进入common\board_r.c:board_init_r函数,进而调用initcall_run_list(init_sequence_r)函数执行一系列初始化函数以实现后半部分板级初始化,并在initcall_run_list函数里进入run_main_loop不再返回,在run_main_loop 里会进入common\main.c:main_loop函数,里边获取启动参数一系列操作用来引导OS 。