IVT、Boot Data structures、DCD是I.MX6启动时的重要数据和配置。IVT中包含Boot Data structures、DCD地址定位的信息。
BOOT ROM 根据这些地址定位找到DCD的存储位置,使用DCD中的配置参数,来初始化DDR,配置时钟,以及其他启动时必要的硬件设置。
Boot Data structures中包含数据拷贝的目标地址和长度, BOOT ROM会在必要的检查和上述的初始化之后,将启动设备中的数据拷贝
到该目标地址中(通常是DDR),然后跳转到IVT中的entry执行程序镜像。
以下的分析中,我们忽略CSF的使用,CSF只用在High Assurance Boot中,有兴趣可参见I.MX6参考手册中的相关描述。
1. I.MX6 BOOT ROM启动流程中IVT、DCD等
相关
数据操作
当我们选择从SPI FLASH启动时,BOOT ROM会拷贝启动设备SPI FLASH中的前4K bytes数据到片内RAM(OCRAM)。
这初始的4K数据中必须包含IVT(Image vector table)、DCD(Device configuration data)和Boot Data structures,
但由于使用SPI FLASH启动时,IVT的偏移为1K,所以,可以说剩余的3K数据中必须包含IVT、DCD和Boot Data structures。
另外DCD最大长度限制为1768 bytes。
BOOT ROM拷贝SPI FLASH 4K的数据到内部RAM(OCRAM)中去,这4K的空间包含1K的空余头(通常内含分区表)和内含IVT、DCD的数据块。
接着BOOT ROM检查OCRAM 4K数据中包含的IVT头标志0xD1,然后执行DCD检查。这些检查无误通过后,从boot data structure 中解析出
下一步拷贝的目的地址和长度(记为length),然后从SPI FLASH地址0,拷贝长度length的数据到上面解析出的目的地址处。
这里有一个如何在OCRAM中定位boot data structure地址的问题。我们编译生成的u-boot.imx,它的boot data内容即为boot data structure
的地址,而该地址是一个绝对地址,实际为DDR地址,而OCRAM中此时的地址还是原始数据中的该地址。由于此时DDR还未准备好,还未执行
u-boot.imx到DDR的拷贝,BOOT ROM不能使用boot data中的绝对(DDR地址)地址来定位boot data structure。而只能使用偏移地址在
OCRAM中定位boot data structure。
注意IVT中还有一个self表项,它指向它自身,即IVT的首地址。比如hexdump出u-boot.imx的self的值为0x177ff400,boot_data的值为
0x177ff420,那么boot data structure的地址偏移为
boot data structure offset = boot_data - self(IVT首地址) = 0x20,
加载到OCRAM中后,boot data structure的有效地址为:OCRAM IVT 加载首地址 + boot data structure offset,
DCD数据的地址也是如此计算的。
上述过程用C语言伪码描述为:
#define BOOT_ROM_COPY_DATA_LEN 0x1000 /×4 K bytes */
#define IVT_OFFSET 0x400
typedefstruct{
uint8_t tag;
uint16_t length;
uint8_t version;
}ivt_header_t;
typedefstruct{
ivt_header_t header;
uint32_tentry;
uint32_treserved1;
uint32_tdcd_ptr;
uint32_tboot_data_ptr;
uint32_tself;
uint32_tcsf;
uint32_treserved2;
} ivt_t;
typedefstruct{
uint32_tstart;
uint32_tsize;
uint32_tplugin;
}boot_data_t;
uint32_t *ocram_load_ptr;
ivt_header_t*ivt_h;
ivt_t *ivt;
boot_data_t *boot_data_ptr;
ocram_load_ptr=malloc(OCRAM,BOOT_ROM_COPY_DATA_LEN);
copy_to_ocram(spi_flash_addr0,BOOT_ROM_COPY_DATA_LEN, ocram_load_ptr);
ivt_h =(ivt_header_t *)(ocram_load_ptr+ IVT_OFFSET);
ivt =(ivt_t*)(ocram_load_ptr+ IVT_OFFSET);
if(ivt_h->tag!=0xD1){
trace"invalid ivt head tag";
return-1;
}
/*DCD有效性检查*/
......
/*调用DCD中的命令,对DDR、时钟等进行配置*/
......
/*DDR已可用*/
boot_data_ptr =(boot_data_t *)(ocram_load_ptr +(ivt->dcd_ptr-ivt->self));
copy_to_ddr(boot_data_ptr->start,boot_data_ptr->size,spi_flash_addr0);
jump_to ivt->entry;
执行完boot_data_ptr中地址和长度的数据拷贝后,我们假定u-boot的加载地址为0x17800000,SPI Flash和DDR RAM的内存镜像图如下:
2. 构建系统生成包含IVT、boot Data、DCD等
头部的镜像文件
u-boot.imx
2.1 u-boot-2009.8版本
在u-boot-2009.8中,IVT、boot Data、DCD在汇编文件flash_header.S实现:
ivt_header: .word 0x402000D1 /* Tag=0xD1, Len=0x0020, Ver=0x40 */
app_code_jump_v: .word _start
reserv1: .word 0x0
dcd_ptr: .word dcd_hdr
boot_data_ptr: .word boot_data
self_ptr: .word ivt_header
#ifdef CONFIG_SECURE_BOOT
app_code_csf: .word __hab_data
#else
app_code_csf: .word 0x0
#endif
reserv2: .word 0x0
boot_data: .word TEXT_BASE
#ifdef CONFIG_SECURE_BOOT
image_len: .word __hab_data_end - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET
#else
image_len: .word _end_of_copy - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET
#endif
plugin: .word 0x0
dcd_hdr: .word 0x40E001D2 /* DDR cfg Tag=0xD2, Len=59*8 + 4 + 4, Ver=0x40 */
write_dcd_cmd: .word 0x048402CC /* Tag=0xCC, Len=80*8 + 4, Param=0x04 */
/* DCD */
/* DDR3 initialization based on the MX6Solo Auto Reference Design (ARD) */
/* DDR IO TYPE */
MXC_DCD_ITEM(1, IOMUXC_BASE_ADDR + 0x774, 0x000c0000)
MXC_DCD_ITEM(2, IOMUXC_BASE_ADDR + 0x754, 0x00000000)
/* CLOCK */
MXC_DCD_ITEM(3, IOMUXC_BASE_ADDR + 0x4ac, 0x00000030)
MXC_DCD_ITEM(4, IOMUXC_BASE_ADDR + 0x4b0, 0x00000030)
......
上述代码中,部分包含地址的变量和其目标地址的关系图如下:
u-boot构建系统会将此文件编译后,连同u-boot.bin文件链接生成最终的u-boot.imx。
另外,我们看到,上面的ivt_header和dcd_hdr,均是以0x40开头的tag,而i.mx6的用户参考手册reference manual中
该值应为0x41,对此,论坛上有如下解释:
Really i.MX6 is one of HAB 4.1, the mentions about ver. 4.0 are derivative
from i.MX53 and do not influence on HAB functionality. One of main differences
between the ver. 4.1 and ver. 4.0 is ability for the rev. 4.1 to support Encrypted
Boot by ROM.
由于u-boot-2009.8版本已为过时的版本,在此不再多做分析。
2.1 u-boot-2015.07版本
u-boot-2015.07及以后的版本中,更改了u-boot.imx的产生方式,不再使用上述的flash_header.S,
而是使用mkimage工具直接生成。此处使用的mkimage包含的目标文件类型-T参数中新增了imximage选项,
mkimage会调用格式类型imximage的解释器来生成针对i.mx(5系6系)系列芯片的 IVT、boot Data、DCD的格式头。具体的代码
实现在tools/imximage.c中。
我们以boundary的nitrogen6q开发板为例,当构建系统执行:
./tools/mkimage -n board/boundary/nitrogen6x/nitrogen6q.cfg.cfgtmp -T imximage -e 0x17800000 -d u-boot.bin u-boot.imx
其中-n指明输入文件为nitrogen6q.cfg.cfgtmp,-T指明解释器为imximage,-e为镜像程序入口点。
mkimage调用选项-T的imximage 解释器解析输入文件nitrogen6q.cfg.cfgtmp,而文件nitrogen6q.cfg.cfgtmp为nitrogen6q.cfg编译时
预处理(使用gcc -E选项)后产生的临时文件。nitrogen6q.cfg的内容如下:
/* image version */
IMAGE_VERSION 2
/*
* Boot Device : one of
* spi, sd (the board has no nand neither onenand)
*/
BOOT_FROM spi
#define __ASSEMBLY__
#include <config.h>
#include "asm/arch/mx6-ddr.h"
#include "asm/arch/iomux.h"
#include "asm/arch/crm_regs.h"
#include "ddr-setup.cfg"
#include "1066mhz_4x128mx16.cfg"
#include "clocks.cfg"
注意,其中包含的IMAGE_VERSION 2和BOOT_FROM spi,它们将在下面会被使用。
该文件预处理后会将ddr-setup.cfg、1066mhz_4x128mx16.cfg、clocks.cfg的内容复制到nitrogen6q.cfg.cfgtmp中,而
前三个文件即为DCD配置文件,如ddr-setup.cfg的内容为:
DATA 4, MX6_IOM_DRAM_SDQS0, 0x00000030
DATA 4, MX6_IOM_DRAM_SDQS1, 0x00000030
DATA 4, MX6_IOM_DRAM_SDQS2, 0x00000030
DATA 4, MX6_IOM_DRAM_SDQS3, 0x00000030
如MX6_IOM_DRAM_SDQS0 又在文件arch/arm/include/asm/arch/mx6q-ddr.h中定义:
#define MX6_IOM_DRAM_SDQS0 0x020e05a8
预处理后为(删除了空行和注解):
IMAGE_VERSION 2
BOOT_FROM spi
DATA4,0x020e05a8,0x00000030
DATA4,0x020e05b0,0x00000030
DATA4,0x020e0524,0x00000030
DATA4,0x020e051c,0x00000030
接下来我们分析imximage.c代码中相关处理:
函数的调用流程为:parse_cfg_file-->parse_cfg_fld-->parse_cfg_cmd-->设置imximage_ivt_offset。
下面我们主要分析配置解析函数parse_cfg_cmd:
statictable_entry_t imximage_boot_offset[]={
{FLASH_OFFSET_ONENAND, "onenand", "OneNAND Flash",},
{FLASH_OFFSET_NAND, "nand", "NAND Flash", },
{FLASH_OFFSET_NOR, "nor", "NOR Flash", },
{FLASH_OFFSET_SATA, "sata", "SATA Disk", },
{FLASH_OFFSET_SD, "sd", "SD Card", },
{FLASH_OFFSET_SPI, "spi", "SPI Flash", },
{FLASH_OFFSET_QSPI, "qspi", "QSPI NOR Flash",},
{-1, "", "Invalid", },
};
staticvoidparse_cfg_cmd(structimx_header*imxhdr,int32_t cmd,char*token,
char*name,intlineno,intfld,intdcd_len)
{
intvalue;
staticintcmd_ver_first=~0;
switch(cmd){
caseCMD_IMAGE_VERSION:
imximage_version=get_cfg_value(token,name,lineno);
......
set_hdr_func();
break;
caseCMD_BOOT_FROM:
imximage_ivt_offset=get_table_entry_id(imximage_boot_offset,
......
break;
caseCMD_BOOT_OFFSET:
imximage_ivt_offset=get_cfg_value(token,name,lineno);
......
break;
......
}
}
函数parse_cfg_cmd使用前导函数从输入文件nitrogen6q.cfg.cfgtmp取出的参数,当参数是CMD_IMAGE_VERSION,它对应
nitrogen6q.cfg.cfgtmp文件中的MAGE_VERSION 2,然后调用get_cfg_value得到版本号为2。imximage的头格式分版本号1和2。
同理CMD_BOOT_FROM,对应于nitrogen6q.cfg.cfgtmp文件中的BOOT_FROM spi,调用get_table_entry_id在
表imximage_boot_offset找到spi对应的偏移量FLASH_OFFSET_SPI(它在文件imximage.h中定义为0x400,这里反映了不同
的启动设备其IVT偏移量的不同)。后续的操作以此为参数,然后联合其他的解析结果,生成总的结构体imx_header_v2_t,
最后mkimage将这个结构体插入文件u-boot.bin的头部,生成u-boot.imx文件。
另外,我们可以在文件imximage.c中的函数print_hdr_v2结尾处增加如下代码,在终端中输出产生的头部信息:
printf("\n--------------------------------------\n");
printf("tag: 0x%04x\n", (uint8_t)fhdr_v2->header.tag);
printf("length: 0x%04x\n", (uint16_t)fhdr_v2->header.length);
printf("version: 0x%04x\n", (uint8_t)fhdr_v2->header.version);
printf("\n");
printf("entry: 0x%08x\n", (uint32_t)fhdr_v2->entry);
printf("reserved1: 0x%08x\n", (uint32_t)fhdr_v2->reserved1);
printf("dcd_ptr: 0x%08x\n", (uint32_t)fhdr_v2->dcd_ptr);
printf("boot_data_ptr: 0x%08x\n", (uint32_t)fhdr_v2->boot_data_ptr);
printf("self: 0x%08x\n", (uint32_t)fhdr_v2->self);
printf("csf: 0x%08x\n", (uint32_t)fhdr_v2->csf);
printf("reserved2: 0x%08x\n", (uint32_t)fhdr_v2->reserved2);
printf("\n");
printf("db start: 0x%08x\n", (uint32_t)db->start);
printf("db size: 0x%08x\n", (uint32_t)db->size);
printf("db plugin: 0x%08x\n", (uint32_t)db->plugin);
printf("--------------------------------------\n\n");
在u-boot源码中重新编译主机程序mkimage:
u-boot$make tools-only
然后单独执行mkimage命令:
u-boot$./tools/mkimage -n board/boundary/nitrogen6x/nitrogen6q.cfg.cfgtmp -T imximage -e 0x17800000 -d u-boot.bin u-boot.imx
相应的输出结果大致如下:
tag: 0x00d1
length: 0x2000 the overall length of the IVT in bytes, including the header. (the length is fixed and must have a value of 32 bytes),8K????
version: 0x0040
entry: 0x17800000
reserved1: 0x00000000
dcd_ptr: 0x177ff42c
boot_data_ptr: 0x177ff420
self: 0x177ff400
csf: 0x00000000
reserved2: 0x00000000
db start: 0x177ff000
db size: 0x00075000
db plugin: 0x00000000