uboot启动流程分析如下:
第一阶段:
a -- 设置cpu工作模式为SVC模式
b -- 关闭中断,mmu,cache
v -- 关看门狗
d -- 初始化内存,串口
e -- 设置栈
f -- 代码自搬移
g -- 清bss
h -- 跳c
第二阶段
a -- 初始化外设,进入超循环
b -- 超循环处理用户命令
可见, U-Boot 属于两阶段的Bootloader
第一阶段的文件:
arch/arm/cpu/armv7 /start.S 平台相关,CPU工作模式设为SVC模式,关MMU,关icahce(CPU相关)
board/samsung/fs4412/lowlevel_init.S 开发板相关:关看门狗,内存初始化,时钟初始化,串口初始化(board相关,初始化最基本设备)
第二阶段的文件:
arch/arm/lib/crt0.S _main 函数所在处,初始化SP,为C语言准备,代码重定位,清BSS,设置R0 R1 R2 R8相应寄存器
arch/arm/lib/board.c board_init_f 函数 ,填充GD结构体,初始化外设, main_loop()函数超循环
arch/arm/cpu/armv7 /start.S 代码自搬移时会用到
针对uboot2013启动流程图如下:
下面是具体分析:
一、U-Boot 第一阶段代码分析
通常我们通过连接文件知晓程序入口点,入口查看 u-boot.lds
通过链接脚本可知入口为_start,位于arch/arm/cpu/armv7/start.o。
第一阶段开始:
1、进入arch/arm/cpu/armv7/start.S
a -- 异常向量表设置
b -- 设置CPU处于SVC工作模式
d -- 协处理器 p15 的 c12 寄存器来重新定位
e、Bl cpu_init_cp15(使分支预测无效,数据)
关闭数据预取功能;
DSB:多核CPU对数据处理指令
ISB:流水线清空指令;
关闭MMU,使能I-cache
NOTE:
分支预测:在流水线里,会将后面的代码优先加载到处理器中,由于是循环,会使后面加载的代码无效,故出现了分支预测技术。(统计跳的次数来选择装载循环的代码还是下面的代码)。
f、Bl cpu_init_crit
2、跳到Low_level_init,位于board/samsung/fs4412/lowlevel_init.S
a、关闭看门狗
b、比较当前pc指针域TEXT_BASE的高8位是否一样来判断,当前代码是否在内存中
c、对系统时钟初始化
d、对内存初始化
e、对串口初始化
结束后返回 start.S
第一阶段结束,总结如下:
1 前面总结过的部分,初始化异常向量表,设置svc模式
2 配置cp15,初始化mmu cache tlb
3 板级初始化,clk,memory,uart初始化
二、第二阶段开始:
按"CTRL + ] ", 发现 _main 在两处有定义:
这里我们选择第一个Bl _main ,跳转到arch/arm/lib/crt0.S
1、初始c运行环境(看注释就知道,初始化C运行环境,并调用board_init_f 函数)
功能:
初始化sp ,为支持c语言做准备;
保存128B 放GD结构体,存放全局信息,GD的地址存放在r8中;
跳转到 board_init_f 函数,其在arch/arm/lib/board.c 处定义;
2、跳转到arch/arm/lib/board.c
功能:
对全局信息GD结构体进行填充:
291行:mon_len 通过链接脚本可以知道存放的是uboot代码大小;
294行:fdt_blob 存放设备数地址;
303行:循环执行init_fnc_t数组的函数,作硬件初始化;
a -- init_fnc_t数组的函数定义
初始化硬件
b -- Dram_init初始化成功之后,剩余代码将会对sdram空间进行规划。
可以看到addr的值由CONFIG_SYS_SDRAM_BASE加上ram_size。也就是到了可用sdram的顶端。
e--继续对gd结构体填充
如果icahe 与 dcache 是打开的,就留出 64K 的空间作为 tlb 空间,最后 addr 就是tlb 地址,4K对齐。
f --填充完成将信息拷贝到内存指定位置
2 -- 继续回到 _main
按"CTRL + O"回到跳转前的函数,即 arch/arm/lib/crt0.S
功能:
将 r8 指向新的 gd 地址;
代码重定位;
对lr 的操作为了让返回时,返回的是重定位的here处
3 -- 代码自搬移
代码自搬移,防止与内核冲突,代码位于arch/arm/cpu/armv7/start.S
循环将代码搬移到指定高地址
这里只是将链接脚本中_image_copy_end到_start中的代码,其它段还没有操作。
在这里我们有疑惑就是将代码重定位到高地址,那运行的地址不就和链接地址不一样了,那运行可能不正常?这个疑惑就是.rel.dyn帮我们解决了,主要还是编译器帮我们做的工作,在链接中有如下:【参考:http://blog.csdn.net/skyflying2012/article/details/37660265】
4 -- 重定位到高地址之后,再次回到 _main(arch/arm/lib/crt0.S)
此时回到的是刚才的重定位的 here 处
关 icache,保证数据从SDRAM中更新,更新异常向量表,因为代码被重定位了;
清BBS;
调用board_init_r主要是对外设的初始化。
R0=gd
R1=RELOCADDR
5 -- Main_loop 函数进入超循环(arch/arm/lib/board.c)
Main_loop函数主要功能是处理环境变量,解析命令
install_auto_complete(); //安装自动补全的函数,分析如下 。
getenv(bootcmd)
bootdelay(自启动)
如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。