3. BootROM 与一级引导:SoC 内部 BootROM 执行流程、启动设备选择、安全启动基础
ARM SoC 上电或复位后,CPU 首先执行的并非用户编写的 U-Boot 或 Linux 内核,而是固化在芯片内部只读存储器(ROM)中的一段出厂代码——BootROM。BootROM 是芯片启动的第一阶段,负责完成最底层的硬件初始化、启动设备枚举与选择,并加载一级引导程序(SPL 或类似映像)到内部 SRAM 中执行。本章深入分析 BootROM 的执行流程、启动设备选择机制以及安全启动的硬件基础。
3.1 BootROM 执行流程
BootROM 的代码由芯片厂商(如 TI、NXP、Rockchip、Allwinner 等)在流片前编写并掩膜到 ROM 中,用户无法修改。其典型执行流程如下:
- 上电复位向量:CPU 从复位向量地址(通常为 0x00000000 或 0xFFFF0000)取指,进入 BootROM 入口。
- 基础硬件初始化:设置栈指针、关闭看门狗、初始化 PLL 与系统时钟、配置内存控制器(仅限内部 SRAM 可用)。
- 启动设备检测:根据硬件引脚电平(Boot Mode Pins)或 eFuse 配置,确定本次启动的设备类型(如 SD 卡、eMMC、NAND Flash、SPI NOR、USB 下载等)。
- 设备初始化与读取:初始化对应外设控制器(如 MMC、SPI、NAND 控制器),从启动设备读取固定偏移位置的一级引导映像到内部 SRAM。
- 映像校验(可选):若使能安全启动,BootROM 会使用公钥哈希或证书链验证映像签名;校验失败则停止启动或进入恢复模式。
- 跳转执行:将 CPU 控制权交给加载到 SRAM 中的一级引导程序(SPL 或 x-loader)。
关键约束:BootROM 通常只能使用芯片内部 SRAM(大小从 32KB 到 512KB 不等),因此一级引导映像必须足够小,且必须存放在启动设备的固定扇区/偏移位置(如 SD 卡的第 1 个扇区、eMMC 的 boot partition 等)。
3.2 启动设备选择机制
BootROM 通过读取芯片特定 GPIO 引脚在复位时的电平状态(Boot Mode Pins)或一次性可编程存储器(eFuse)中的配置,来决定从哪个外设加载一级引导程序。常见的启动设备类型及特点如下表所示:
| 启动设备 | 接口类型 | 典型偏移/扇区 | 读取速度 | 常见应用 |
|---|---|---|---|---|
| SD 卡 | SD/MMC | LBA 0(第 1 扇区) | 中等 | 开发板、原型验证 |
| eMMC | MMC | Boot Partition 或用户分区 LBA 0 | 高 | 量产嵌入式设备 |
| SPI NOR Flash | SPI | 0x00000000 | 低 | 小容量固件存储 |
| NAND Flash | NAND 控制器 | 块 0,页 0 | 中等 | 大容量数据存储 |
| USB 下载 | USB OTG | 无(由主机通过 USB 协议传输) | 取决于 USB 速率 | 工厂烧录、恢复模式 |
| UART 下载 | UART | 无(串行协议传输) | 极低 | 调试、紧急恢复 |
调试技巧:在开发板上,通常通过拨码开关或跳线帽设置 Boot Mode Pins。例如,TI AM335x 系列使用 SYSBOOT[4:0] 共 5 个引脚选择启动设备;Rockchip RK3399 使用 eFuse 或 PMU 寄存器中的 boot_sel 位域。调试时,务必先确认当前启动模式与预期一致,否则 BootROM 可能从错误的设备读取数据,导致启动失败。
3.3 一级引导程序(SPL)的加载与约束
一级引导程序通常称为 SPL(Secondary Program Loader) 或 x-loader。BootROM 将其从启动设备加载到内部 SRAM 后,CPU 跳转到 SPL 入口执行。SPL 的主要职责包括:
- DRAM 初始化:配置 DDR 控制器、时序参数,使外部 DRAM 可用。
- 加载二级引导程序:从启动设备读取 U-Boot 完整映像(或 ATF + U-Boot)到 DRAM 中。
- 提供串口调试输出:初始化 UART,输出启动日志。
- 支持安全启动链:验证下一级映像的签名。
SPL 的大小受限于内部 SRAM 容量。常见 SoC 的 SRAM 大小与 SPL 大小限制如下:
| SoC 系列 | 内部 SRAM 大小 | SPL 最大尺寸 | 典型 SPL 实际大小 |
|---|---|---|---|
| TI AM335x | 64KB | 约 60KB | 40~55KB |
| NXP i.MX6 | 256KB | 约 248KB | 80~150KB |
| Rockchip RK3399 | 192KB | 约 180KB | 100~160KB |
| Allwinner H3 | 32KB | 约 28KB | 20~27KB |
常见陷阱:SPL 编译时若启用了过多驱动或功能(如文件系统支持、网络协议栈),很容易超出 SRAM 容量限制,导致 BootROM 加载后无法正常执行或发生内存越界。调试时若发现 SPL 无任何串口输出,首先检查 SPL 映像大小是否超过芯片 SRAM 上限。
3.4 安全启动基础
安全启动(Secure Boot)的目的是确保只有经过签名的、未被篡改的固件才能在目标设备上执行。BootROM 是安全信任链的根(Root of Trust)。其基本流程如下:
- 公钥注入:在芯片生产或首次部署时,将厂商公钥的哈希值(或公钥本身)烧录到 eFuse 或 OTP 存储器中。BootROM 在启动时读取该公钥。
- 映像签名:一级引导映像(SPL)在构建时使用厂商私钥进行签名,签名数据附加在映像尾部或存储在固定偏移位置。
- BootROM 校验:BootROM 加载 SPL 后,使用 eFuse 中的公钥验证映像签名。若签名无效,则停止启动或进入恢复模式。
- 链式信任:SPL 在加载 U-Boot 或 Linux 内核时,同样使用公钥或证书链验证下一级映像,形成完整的信任链。
调试安全启动:若安全启动失败,BootROM 通常会在特定寄存器中记录错误状态(如 TI AM335x 的 ROM_BOOT_ERR 寄存器),或通过 GPIO 输出错误码。在开发阶段,可以暂时禁用安全启动(如不烧录 eFuse 或使用非安全版本 BootROM),待功能调试完成后再启用签名校验。
3.5 常见 BootROM 调试方法
当 BootROM 阶段出现问题时,芯片通常没有任何串口输出(因为 UART 尚未初始化),调试难度较大。以下是一些实用方法:
- 检查 Boot Mode 引脚电平:使用万用表或示波器测量 Boot Mode 引脚在复位时的电平,确认与预期启动设备一致。
- 观察启动设备时钟/数据信号:使用示波器或逻辑分析仪捕获启动设备接口(如 SD 卡 CLK/CMD/DATA 线)上的信号,判断 BootROM 是否尝试读取数据。
- 读取 BootROM 错误寄存器:部分 SoC 提供调试寄存器(如 ROM 版本号、错误码),可通过 JTAG 或内存映射地址读取。
- 使用 JTAG 单步调试:连接 JTAG 调试器,在 BootROM 入口处设置断点,单步跟踪执行流程。
- 检查 SPL 映像放置位置:确认 SPL 映像是否烧录在启动设备正确的偏移地址(如 SD 卡 LBA 0、eMMC boot partition 的 0 偏移)。
案例:某项目使用 TI AM335x 芯片,上电后无任何输出。检查发现 Boot Mode 引脚 SYSBOOT[4:0] 被误设置为 0b10101(对应 SPI0 启动),而实际固件烧录在 SD 卡中。将引脚配置改为 0b00101(MMC0 启动)后,系统正常启动。