一,IMX6ULL映像文件
1,格式概述
对于IMX6ULL,烧写在EMMC、SD/TF卡上的程序,并不能“自己复制自己”,是“别人把它复制到内存里”。一上电首先运行的是boot ROM上的程序,它从EMMC、SD/TF卡上把程序复制进内存里。
烧写在EMMC、SD卡或是TF卡上的,除了程序本身,还有位置信息、DCD 信息,这些内容合并成一个映像文件,如下图:
这4 部分内容合并成为一个映像文件,烧写在EMMC、SD 卡或TF 卡等启动设备的某个固定地址,boot ROM 程序去这个固定地址读出映像文件。启动设备不同, 固定地址不同,如下图:
2,格式详解
下面的讲解图中,列有C 语言格式的结构体,这些结构体来源于U-boot 的tools 目录下的imximage.h。
2.1 Image Vector Table(IVT)
IVT 会被放在固定的地址,IVT 中是一系列的地址,boot ROM 程序会根据这些地址来确定映像文件中其他部分在哪里。
IVT 格式如下:
要注意的是上图中这4项:
a) header:里面有3项:tag、length、version。length表示IVT的大小,它是32字节。要注意是的,它是大字节序的。
b) entry:用户程序运行时第1条指令的地址,就是程序的链接地址、程序被复制到内存哪里。
c) dcd:映像被复制到内存后,其中的DCD数据的地址。
d) boot data:映像被复制到内存后,其中的boot data的地址。
e) self:映像被复制到内存后,IVT自己所在的地址。
2.2 Boot data
映像被复制到内存后,整个映像文件(IVT之前还有几个扇区数据,比如分区表)所在的地址。
a) start:这是映像文件在内存中的地址,以SD/TF卡为例:
映像文件=(1K数据,内含分区表等信息)+IVT+BootData+DCD+用户数据(bin文件)
注意,IVT并不在映像文件的最前面,start也不是IVT在内存中的地址,而是整个映像文件在内存中的地址:
start = IVT在内存中的地址 - IVT offset
什么意思?假设IVT被保存在启动设备TF卡1024偏移地址处,IVT被复制到内存地址0x87000000,那么start=0x87000000-1024。
所以start表示的是启动设备开头的数据,被复制到内存哪里去。
从它的含义也可以推理出:boot ROM程序会把启动设备开头的数据,复制到内存;而不仅仅是从IVT开始复制。
b) length:保存在启动设备上的整个映像文件的长度,从0地址开始(不是从IVT开始)。
c) plugin:这是一个标记位,当它为1时表示这个映像文件是“plugin”,即插件。
boot ROM程序可以支持有限的启动设备,如果你想双持更多的启动设备比如网络启动、CDROM启动,就需要提供对应的驱动。这些驱动就是“plugin”,我们的教程不涉及,该标记位为0。
Boot data就是用来表示映像文件应该被复制到哪里去,以及它的大小。boot ROM程序就是根据它来把整个映像文件复制到内存去的。
2.3 DCD
“Device Configuration Data”,设备配置数据(DCD),这些DCD将会跟bin文件一起打包烧写在启动设备上。boot ROM程序会从启动设备上读出DCD数据,根据DCD来写对应的寄存器以便初始化芯片。DCD中列出的是对某些寄存器的读写操作,我们可以在DCD中设置DDR控制器的寄存器值,可以在DCD中使用更优的参数设置必需的硬件。这样boot ROM程序就会帮我们初始化DDR和其他硬件,然后才可以把bin程序读到DDR中并运行。
实际上DCD还可以更复杂,它支持多种命令:write data、check data、nop、unlock。我们可以通过write data命令写寄存器,通过check data命令等待寄存器就绪。
2.4 User code and data
就是用户程序或数据,原原本本地添加到映像文件里就可以。
3,实例
制作过程中各项值的计算方法如下图所示:
我们不需要手工去计算,一个mkimage命令就搞定了。上图中各步骤细说如下:
1)确定入口地址entry
我们的程序运行时要放在内存中哪一个位置,这是我们决定的。它被称为入口地址、链接地址。
2)确定映像文件在内存中的地址start
boot ROM程序启动时,会把“Initial Load Region”读出来,“Initial load Region”里含有IVT、Boot data、DCD。boot ROM根据DCD初始化设备后,再把整个映像文件读到内存。
在启动设备上,“Initial Load Region”之后紧跟着我们的程序,反过来说就是我们程序的前面,放着“Initial Load Region”。假设“Initial Load Region”的大小为load_size,那么在内存中“Initial Load Region”的位置start = entry – load_size。
注意:“Initial Load Region”位于启动设备0位置,它的头部并不是IVT,而是一些无用的数据(或是分区信息)。
在IMX6ULL中有一个表格,列出了不同启动设备对应的“Initial Load Region Size”:
3)确定IVT在内存中的地址self:
我们知道IVT在启动设备上某个固定的位置,上或中的“Image Vector Table Offset”:ivt_offset。那么在内存中它的位置可以如下计算:
self = start + ivt_offset = entry – load_size + ivt_offset
4)确定Boot data在内存中的地址boot_data
IVT的大小是32字节,IVT之后就是Boot data,而IVT中的boot_data值表示Boot data在内存中的位置,计算如下:
boot_data = self + 32 = entry – load_size + ivt_offset + 32
5)确定DCD在内存中的地址dcd
Boot data的大小是12字节,Boot data之后就是DCD,而IVT中的dcd值表示DCD在内存中的位置,计算如下:
dcd = boot_data + 12 = entry – load_size + ivt_offset + 44
6)写入DCD的数据:
DCD是用初始化硬件的,特别是初始化DDR。而DDR的初始化非常的复杂、专业,我们一般是使用硬件厂家提供的代码。
我们是使用类似下面的指令来制作映象文件:
./tools/mkimage -n board/free