标签:uboot启动的第一阶段
1:BL0 BL1 BL2分别是什么
(1)BL0:s5pv210的iROM中固化的代码
作用:初始化系统时钟,设置看门狗,初始化栈,加载BL1
(2)BL1:从外部启动介质(nand/SD卡)中加载的uboot.bin的前16K代码
作用:初始化RAM,关闭Cache,初始化DDR,设置栈,加载BL2
(3)BL2:是指在代码完成重定位后在DDR中运行的完整的uboot代码
作用:初始化其他外设,加载OS内核
三者的关系:开机上电自动运行BL0的代码,然后加载BL1到SDRAM中,接着通过重定位将BL2加载到DDR中,最终uboot启动完成,加载内核到DDR中
2:uboot的启动过程
下图是三星公司推荐210的启动过程
三星推荐的启动过程也是开机上电自动加载BL1,然后运行BL2,引导有、内核的启动,需要注意的三星推荐的启动过程中BL1和BL2都是放在SDRAM(96K)中的,但是随着uboot的发展uboot的大小已经超过了96K,所以BL2不能再放在SDRAM中,而是通过BL1重定位到DDR中
3:uboot启动时第一阶段做了哪些事情
uboot启动的入口函数是start.S,所以根据start.S这个文件中的代码去分析uboot启动的第一阶段。
(1)通过#include加载相应的头文件
(2)通过汇编伪指令.word定义4个4字节的变量,作用是为后面的计算校验和填充占位,确保image的头部有16字节的头信息,如果没有,则uboot会打印出 SD checksun Error
(3)构建异常向量表,但是由于uboot的启动时间很短,异常发生的较少,所以并没有很细致地处理各种异常,当异常发生时重启即可
(4)通过操作CPSR程序状态寄存器设置SoC为SVC模式
(5)通过读取0xE0000004寄存器的值来判断启动介质,0xE0000004寄存器的值会根据OM0-OM5这6 个引脚的电平高低自动设置,通过读取这个寄存器的值就可以得到uboot从哪里启动
(6)设置栈,此时运行的代码是BL1,是在SDRAM中运行的,还有一点需要注意的是ARM都是满减栈,也就是入栈时是向下延伸的
(7)调用lowlevel_init函数,这个函数里面主要是 检查复位状态(热启动 冷启动 睡眠唤醒),复位后IO口的保存和恢复,关看门狗,开发板供电锁存,初始化时钟,初始化DDR,并在返回start.S之前打印字符‘OK‘,需要注意的是,lowlevel_init函数中有一段代码用来判断当前运行位置是在DDR还是SDRAM,其原理是通过比较运行地址和链接地址是否相同,而且它判断的是运行地址和链接地址的第12位开始的12位是否相同
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1
beq 1f /* r0 == r1 then skip sdram init */
bic r1, pc, r0 的意思就是把pc中和r0对应的1改为0,0改为1,并存入寄存器r1中,也就是将r0取反在和pc相与 r1 = pc & (~(r0)) 如果相等则说明当前运行在DDR中,那么就不需要进行时钟和DDR的初始化,如果不相等则说明当前运行在SDRAM中,就需要进行时钟和DDR的初始化。
打印出来的字符‘OK‘,是一种调试信息,如果启动ubooot看到Ok 则说明是调用lowlevel_init后面的错误,如果没有看到OK则说明是在调用这个函数之前。在进行移植的时候也可以使用类似的方法在函数调用之间通过打印相应的字符来判断错误发的位置。
(8)再次设置开发板供电锁存,没实际意义
(9)在DDR中设置栈,原因是在lowlevel_init函数中已经对DDR进行了初始化,这时候的DDR是可用的
(10)再次判断当前运行位置是在SDRAM还是DDR,其方法还是一样,其目的是为了判断是否需要进行重定位
(11)虚拟地址映射
(12)第三次设置栈,这次设置栈的为了充分合理地使用内存(安全,紧凑不浪费内存)
(13)清bss段
(14)uboot第一阶段的最后一句: ldr pc, _start_armboot指针指向_start_armboot这个函数,也就是uboot启动的第二阶段的入口函数。
标签:uboot启动的第一阶段
原文地址:http://11674570.blog.51cto.com/11664570/1834299