4. IVT与Boot Data结构

在 i.MX 系列处理器的启动链中,Boot ROM 是芯片上电后执行的第一段固件。它并不理解 ELF、FIT 或任何操作系统镜像格式,它只认一种非常朴素的“自举描述结构”——IVT(Image Vector Table,镜像向量表)。IVT 是 Boot ROM 与用户镜像之间的契约:Boot ROM 按固定规则找到 IVT,从 IVT 中读出“镜像在哪里、有多大、入口在哪里、要不要校验”,然后跳转执行。理解 IVT 与 Boot Data 的字段含义和偏移关系,是排查“镜像烧录成功但无法启动”这类问题的第一把钥匙。

一、IVT 在镜像中的位置与自举加载总览

i.MX6/6ULL/7D 等常见器件的 Boot ROM 支持多种启动设备(SD/eMMC、NAND、NOR、串口、USB)。以 SD 卡启动为例,Boot ROM 会读取 SD 卡的第一个扇区(LBA 0,512 字节)作为引导头,其中偏移 0x00 处存放的是 Boot Tag,偏移 0x04 处存放的是 IVT 的绝对地址(小端)。Boot ROM 据此跳转到该地址读取 IVT 结构。因此,镜像布局中必须保证 IVT 位于一个 Boot ROM 可寻址的位置,并且其地址被正确写入引导头。

IVT 本身是一个固定 32 字节的结构,包含 8 个 32 位字段。它并不直接描述整个镜像,而是通过指针把 Boot ROM 引向 boot_data、DCD、入口地址和自举数据。下图给出 IVT 与 Boot Data、DCD、入口地址之间的字段级关系与自举加载路径。

i.MX IVT / Boot Data 字段关系与自举加载路径 Boot ROM 读 LBA0 引导头 取 IVT 地址并跳转 引导头 (LBA0) 0x00: Boot Tag 0x04: IVT 绝对地址 IVT 结构 (32 字节, 8×32bit) 0x00 header : Tag=0xD1, Len=0x20, Ver=0x40/0x41 0x04 entry : 镜像入口地址 (绝对地址) 0x08 reserved : 保留 (通常为 0) 0x0C dcd_ptr : DCD 地址 (无则 0) 0x10 boot_data_ptr : 指向 boot_data 结构 0x14 self : IVT 自身地址 0x18 csf : CSF 地址 (HAB 签名) Boot Data 结构 (12 字节) 0x00 start : 镜像起始地址 (含 IVT) 0x04 length : 镜像总长度 (字节) 0x08 plugin : 插件标志 (通常 0) boot_data_ptr 自举加载流程 1. Boot ROM 读引导头 → 定位 IVT 2. 校验 IVT header (Tag/Len/Ver) 3. 读 boot_data → 得到 start/length 4. 按 length 将镜像搬入 RAM 5. 跳转 entry 地址执行

二、IVT 八个字段逐一解析

IVT 的 8 个字段都是 32 位小端存储,含义如下:

  • header(0x00):4 字节复合字段。低 8 位是 Tag,固定为 0xD1;次 8 位是长度,固定为 0x20(32 字节);高 16 位是版本,i.MX6 常用 0x40,i.MX7/8 常用 0x41。Boot ROM 首先校验 Tag 和长度,不匹配则直接放弃该启动设备。
  • entry(0x04):镜像入口的绝对地址。Boot ROM 完成搬运后跳转到这里。对于 U-Boot,这个地址通常是 U-Boot 的 _start 符号地址,例如 0x87800000(i.MX6ULL DDR 起始)。
  • reserved(0x08):保留字段,必须写 0。
  • dcd_ptr(0x0C):指向 DCD(Device Configuration Data)的地址。DCD 用于在跳转前配置 DDR 控制器、时钟、IOMUX 等。若不需要 DCD,该字段写 0。
  • boot_data_ptr(0x10):指向 boot_data 结构的地址。这是 IVT 与镜像长度信息之间的桥梁。
  • self(0x14):IVT 自身的绝对地址。Boot ROM 用它做一致性校验,也用于计算相对偏移。
  • csf(0x18):指向 CSF(Command Sequence File)的地址,用于 HAB 安全启动签名。未启用安全启动时写 0。
  • reserved2(0x1C):保留字段,写 0。

可以看到,IVT 本身不携带镜像长度,长度信息由 boot_data 提供。这种“指针分离”设计让 IVT 可以放在镜像头部,而 boot_data 可以紧随其后或放在其他位置,只要地址正确即可。

三、boot_data 偏移与字段含义

boot_data 是一个 12 字节结构,通常紧跟在 IVT 之后,但并非强制。它的三个字段是:

偏移字段含义典型值示例
0x00start镜像起始地址(含 IVT 的绝对地址)0x877FF400
0x04length镜像总长度(字节),含 IVT、Boot Data、DCD、U-Boot 本体0x00080000
0x08plugin插件标志,普通启动为 00x00000000

Boot ROM 读取 boot_data 后,会以 start 为源地址、length 为长度,把整段镜像搬运到 entry 所在的运行地址。注意:start 是镜像在启动介质中的加载地址,entry 是搬运完成后 CPU 跳转执行的地址,两者可以不同。例如从 SD 卡启动时,start 可能是 0x877FF400(DDR 中的加载地址),entry 是 0x87800000(U-Boot 正文入口),中间 0xC00 字节正是 IVT、Boot Data、DCD 等头部结构占用的空间。

四、entry 地址与自举加载流程

自举加载的完整路径可以概括为:Boot ROM 读引导头 → 定位 IVT → 校验 header → 读 boot_data 得到 start/length → 按 length 搬运镜像 → 跳转 entry。其中有两个关键判断点:

  1. IVT header 校验:Tag 必须为 0xD1,长度必须为 0x20。若烧录时字节序错误或地址偏移错误,Boot ROM 会认为该设备无有效镜像,转而尝试下一个启动设备。
  2. boot_data 一致性:start 必须等于 IVT 所在地址(或镜像实际起始地址),length 必须覆盖整个镜像。若 length 偏小,U-Boot 尾部被截断,跳转后可能在重定位阶段崩溃;若 start 错误,搬运源地址错位,entry 处代码完全不可执行。

实际工程中,IVT、Boot Data、DCD 通常由 mkimage 工具自动生成。以 i.MX6ULL 为例,使用 mkimage -T imximage 并配合 .imximage.cfg 配置文件,工具会根据配置中的 BOOT_FROM、LOAD_ADDR、ENTRY_ADDR 等参数,自动计算并填充 IVT 各字段。手工构造时,必须保证所有指针都是绝对地址,且与链接脚本中的地址一致。

排查启动失败时,可先用 hexdump -C image.imx | head -n 4 查看前 64 字节:偏移 0x00 应为 D1 00 20 40(或 41),偏移 0x04 应为 entry 地址的小端表示,偏移 0x10 应为 boot_data 地址。若这些字节不符合预期,问题多半出在镜像生成阶段,而非 Boot ROM 或硬件。

五、一个可对照的示例布局

假设某 i.MX6ULL 板卡从 SD 卡启动,U-Boot 链接地址为 0x87800000,镜像头部占用 0xC00 字节,则典型布局如下:

0x877FF400  IVT header      : D1 00 20 40
0x877FF404  entry           : 00 00 80 87   -> 0x87800000
0x877FF408  reserved        : 00 00 00 00
0x877FF40C  dcd_ptr         : 00 00 00 00   (无 DCD)
0x877FF410  boot_data_ptr   : 00 00 00 87   -> 0x877FF400? 需按实际填写
0x877FF414  self            : 00 00 00 87   -> 0x877FF400
0x877FF418  csf             : 00 00 00 00
0x877FF41C  reserved2       : 00 00 00 00
0x877FF420  boot_data.start : 00 F4 FF 87   -> 0x877FF400
0x877FF424  boot_data.length: 00 00 08 00   -> 0x00080000
0x877FF428  boot_data.plugin: 00 00 00 00
0x877FF42C  ...             : DCD / 填充 / U-Boot 正文
0x87800000  U-Boot _start   : 入口执行点

上表中 boot_data_ptr 和 self 的具体数值取决于 IVT 实际被放置在镜像中的哪个偏移。若 IVT 位于镜像起始处,则 self 等于镜像起始地址;若 IVT 前还有引导头,则 self 需要加上引导头长度。这一点在手工构造镜像时最容易出错,务必以链接脚本和实际烧录偏移为准。

掌握 IVT 与 Boot Data 的字段语义和偏移关系后,再回头看 U-Boot 的 u-boot.imx 文件,就能理解为什么它比 u-boot.bin 多出约 3KB 的头部,也能在启动异常时快速定位是头部生成错误、地址配置错误,还是搬运长度不足。这是 i.MX 启动链工程中最基础、也最值得反复核对的一环。