写在前头
*.版权声明:本篇文章为原创,可随意转载,转载请注明出处,谢谢!另我创建一个QQ群82642304,欢迎加入!
*.目的:整理一下RIotBoard开发板的启动流程,对自己的所学做一个整理总结,本系列Uboot代码基于2009.08版。
*.备注:整个系列只是对我所学进行总结,记录我认为是关键的点,另我能力有限,难免出现疏漏错误,如果读者有发现请多指正,以免我误导他人!
i.MX6 Solo从上电到Uboot
板子上电之后,先执行的是片内ROM的一段启动代码,具体可见imx6solo数据手册中< Chapter 8: System Boot >。启动代码根据寄存器配置及/或外部管脚的配置,决定进入下载模式或者从某处(Flash、emmc、SD卡等存储设备)启动Uboot。
如果是从emmc启动Uboot,根据数据手册所描述的,首先启动代码会将emmc前4K的数据拷贝到内部RAM,这些数据里面包含了Program image数据.
Program image是飞思卡尔定义的一个镜像数据结构,包括镜像向量表IVT,Boot Data,设备配置数据DCD和用户代码数据等信息,详见数据手册<8.6 Programe image>.
另:加载数据长度大小和IVT的偏移量根据启动设备类型的不同而不同,详见下表:
至于IVT为何要有一个偏移量,个人认为是空开位置用于存放MBR,目前RIotBoard是没有用到.IVT主要包含(详见< 8.6.1.1: Image Vector Table Structure >):
名称 | 含义 |
---|---|
header | IVT头部,标识IVT和IVT长度 |
entry | 第一条指令的入口地址,即Uboot的入口 |
dcd | DCD数据的地址,紧跟在Boot Data后面 |
Boot Data | Boot Data的地址,Boot Data紧跟在IVT后面 |
csf | CSF的地址 |
Boot Data包含:
名称 | 含义 |
---|---|
start | 启动数据加载到内存的地址 |
length | 启动数据加载到内存的长度 |
plugin |
整个内存结构如下图:
3. 启动代码根据Boot Data的指示,将emmc中的前Boot Data->length字节拷贝到Boot Data->start的内存位置上.拷贝之后的内存结构如下图:
左边的是片内RAM中的数据,右边的是内存中的数据.
可以看到IVT->entry指向的是内存中Application(即图中深蓝色的线)的位置,Application即是Uboot镜像,最后跳转到IVT->entry指定的地址,即进入UBoot.
代码中的启动
- 研究Uboot时,Uboot的链接脚本u-boot.lds是一个关键的文档,它指示里Uboot的入口地址以及各个段的分布:
...
ENTRY(_start) //表明Uboot的入口地址
...
.text :
{
board/freescale/mx6q_riot/flash_header.o (.text.flasheader)
cpu/arm_cortexa8/start.o
...
}
...
上述的u-boot.lds代码片段,我们获取到两个信息:
a.Uboot的入口地址是_start(它的定义是在cpu/arm_cortexa8/start.o中)
b.Uboot.bin第一个存放的是board/freescale/mx6q_riot/flash_header.o里面的代码,第二个存放的是cpu/arm_cortexa8/start.o里的代码.
根据上面的分析我们知道,在Uboot之前有一个Program image,所以我们不难猜到board/freescale/mx6q_riot/flash_header.o里定义的就是Program image.
2. 打开board/freescale/mx6q_riot/flash_header.S:
...
.section ".text.flasheader", "x"
b _start //跳转到Uboot入口地址
.org CONFIG_FLASH_HEADER_OFFSET //此处定义为IVT的偏移量0x400
ivt_header: .word 0x402000D1 /*Tag=0xD1, Len=0x0020, Ver=0x40*/
app_code_jump_v: .word _start //Uboot入口地址
reserv1: .word 0x0
dcd_ptr: .word dcd_hdr
boot_data_ptr: .word boot_data
self_ptr: .word ivt_header
app_code_csf: .word 0x0
reserv2: .word 0x0
boot_data: .word TEXT_BASE //Uboot加载到内存的地址
image_len: .word _end_of_copy - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET //Uboot加载到内存的长度
plugin: .word 0x0
...
dcd_hdr: .word 0x40E001D2 /* Tag=0xD2, Len=59*8 + 4 + 4, Ver=0x40 */
write_dcd_cmd: .word 0x04DC01CC /* Tag=0xCC, Len=80*8 + 4, Param=0x04 */
...
/* DCD */
可以看到IVT,Boot Data,DCD以及偏移量等关键信息,与上面的分析完全吻合.
我们可以通过hexdump打印一下已编译好的Uboot.bin再次核对:
$ hexdump -n 2048 u-boot.bin
0000000 0186 ea00 0000 0000 0000 0000 0000 0000
0000010 0000 0000 0000 0000 0000 0000 0000 0000
*
0000400 00d1 4020 0620 2780 0000 0000 042c 2780
0000410 0420 2780 0400 2780 0000 0000 0000 0000
0000420 0000 2780 9f98 0006 0000 0000 01d2 40e0
0000430 01cc 04dc 0e02 7407 0c00 0000 0e02 5407
0000440 0000 0000 0e02 ac04 0000 3000 0e02 b004
0000450 0000 3000 0e02 6404 0000 3000 0e02 9004
0000460 0000 3000 0e02 4c07 0000 3000 0e02 9404
0x00000000 的值是0xea000186,这是一条汇编指令,即flash_header.S中定义的b _start一致.
0x00000004~0x000003FF都是0x00000000,与flash_header.S中的定义.org CONFIG_FLASH_HEADER_OFFSET一致.
0x00000400的值是0x402000D1,与flash_header.S中的定义ivt_header: .word 0x402000D1一致.
…
所以烧写的时候,一定要把u-boot.bin烧写到emmc的最前端,这样子片内启动代码才能正常启动Uboot.
总结
i.MX6 Solo芯片引入了Program Image的数据结构,用于指示片内ROM的启动代码从何处加载Uboot并且跳转到何处进入Uboot。在Uboot源码中定义了Program Image数据结构,并且链接的时候被链接到u-boot.bin的最开始的位置。烧写Uboot的时候也应该将Uboot烧写到存储设备的最低端方可正常启动。
参考
1.i.MX6 Solo数据手册《IMX6SDLRM.pdf》