4. BootROM 与第一阶段启动:芯片的“第一次呼吸”

说实话,做嵌入式这么多年,我最怕听到的一句话就是:“板子焊好了,上电,没反应。” 这时候你第一个要怀疑的,就是 BootROM 有没有跑起来。它就像芯片的“第一次呼吸”,如果这口气没喘对,后面全是白搭。

今天我们就来聊聊这个最底层、最不可篡改的启动阶段。我个人习惯把 BootROM 称为“芯片的 BIOS”,但它的权限比 BIOS 还要高——因为它是一块只读的、出厂就固化在硅片上的代码。

4.1 BootROM 到底是什么?

BootROM,全称 Boot Read-Only Memory。它是一段固化在芯片内部 ROM 中的程序。芯片上电后,CPU 复位向量指向的第一条指令,就在这里面。

它的几个关键特性:

  • 不可修改:出厂时通过掩膜写入,物理上无法更改。你想改?只能换芯片。
  • 最先执行:复位后 CPU 直接跳到这里,没有任何中间环节。
  • 硬件依赖:跟芯片架构强绑定,ARM、RISC-V、Xtensa 各有各的玩法。
  • 容量极小:通常只有几 KB 到几十 KB。你想想看,这点空间要完成初始化、校验、加载,每一行代码都得精打细算。

核心认知:BootROM 是信任链的“根”。如果它被篡改,整个系统的安全就无从谈起。所以它必须是只读的、物理不可变的。

4.2 不可变代码:为什么非要“焊死”在芯片里?

你可能要问:为什么不用 Flash?Flash 也能存代码啊,还能更新,多灵活。

嗯,这里要注意。Flash 是可以被擦写的。攻击者如果能物理接触芯片,或者通过漏洞注入恶意代码,就能把 Flash 里的启动代码换掉。一旦 BootROM 级别的代码被替换,你后续所有的安全措施——签名校验、加密启动、安全存储——全都成了摆设。

我在项目中遇到过一件事:某款 IoT 芯片,BootROM 放在 Flash 里,结果被攻击者通过调试接口把启动代码 dump 出来,逆向分析后植入了后门。从那以后,我对“不可变”这三个字有了刻骨铭心的理解。

所以,BootROM 必须满足:

  • 物理只读:ROM、OTP(One-Time Programmable)或者 eFuse 阵列。
  • 制造时固化:晶圆厂在流片阶段就写好了,你拿到芯片时它就在那里。
  • 不可绕过:CPU 复位后必须执行它,没有别的入口。

避坑指南:我曾经见过一个方案,BootROM 放在 Mask ROM 里,但为了“调试方便”,留了一个硬件跳线可以跳过它。结果量产时忘了封掉这个跳线……嗯,后果你自己想。

4.3 硬件初始化流程:从复位到第一行 C 代码

芯片上电后,BootROM 到底干了些什么?我把它拆成几个关键步骤:

  1. 复位向量跳转:CPU 复位后,从固定地址(比如 0x00000000 或 0xFFFF0000)取指。这个地址映射到 BootROM。
  2. 最小系统初始化:关看门狗、关中断、设置 CPU 模式、初始化栈指针(SP)。这一步通常用汇编完成。
  3. 时钟与 PLL 配置:芯片刚上电时通常跑在内部低速振荡器上。BootROM 要配置 PLL,把 CPU 和外设时钟提升到目标频率。
  4. 内存控制器初始化:如果芯片有片内 SRAM,这一步很简单。如果需要外部 DDR,那就要配置时序、电压、ODT 等参数。这一步最容易出问题。
  5. 启动介质检测:BootROM 会按照预设的优先级,去检测启动介质——比如先从 SPI Flash 读,不行再从 eMMC 试,再不行就回退到串口下载。
  6. 加载并校验第一阶段 Bootloader:从检测到的介质中读取一段代码(通常是 SPL 或 MBR),做完整性校验(CRC、签名等),然后跳转过去。

下面这张图,是我自己画的一个典型 BootROM 启动流程:

BootROM 启动流程图 BootROM 启动流程图 芯片上电复位 CPU 从 BootROM 取指 最小系统初始化(汇编) 时钟 / PLL 配置 内存控制器初始化 检测启动介质 → 加载 Bootloader

4.4 启动介质检测:BootROM 的“选择题”

BootROM 怎么知道从哪里加载代码?它有一套固定的优先级策略。我见过的大多数芯片,优先级是这样的:

优先级 启动介质 典型场景
1(最高) SPI NOR / NAND Flash 大多数嵌入式设备
2 eMMC / SD 卡 手机、平板、树莓派
3 USB / UART 下载 开发调试、工厂烧录
4 网络启动(TFTP/PXE) 无盘工作站、网络设备
5(最低) 回退到内部 ROM 监控 救援模式

检测方式通常是:BootROM 尝试从介质读取固定偏移处的数据,检查是否有合法的头部信息(比如 Magic Number、校验和)。如果读到的东西不对,就跳到下一个介质。

经验之谈:我建议你在设计 BootROM 时,把串口下载的优先级放低一点。曾经有个产品,BootROM 默认先检测 UART,结果生产线上有台电脑的串口一直发垃圾数据,导致所有芯片都卡在串口下载模式……排查了三天。

4.5 加载与校验:信任链的第一环

BootROM 从介质中读出来的,通常是第一阶段的 Bootloader(比如 U-Boot SPL、Coreboot romstage)。但这里有个关键问题:你怎么保证读出来的代码是可信的?

如果 BootROM 不做任何校验,攻击者只要替换掉 Flash 里的 Bootloader,就能劫持整个启动流程。所以,现代芯片的 BootROM 都会做以下至少一项:

  • CRC 校验:检查数据完整性,防止传输错误或部分擦写。
  • 数字签名验证:用公钥验证 Bootloader 的签名。公钥通常固化在 BootROM 或 eFuse 中。
  • 哈希比对:将 Bootloader 的哈希值与存储在 OTP 中的参考值比对。

我参与过一个车规级芯片的项目,BootROM 里硬编码了 RSA-2048 的公钥。每次启动,它都要对 Flash 中的 Bootloader 做签名验证。如果签名不对,直接死循环——连错误提示都不给你。当时调试的时候,我们经常因为签名没更新,板子变砖,只能通过 JTAG 救回来。

关键点:BootROM 的校验逻辑,是整个可信启动的“根信任”。一旦这个校验被绕过或破解,后续所有安全措施都失去意义。所以,公钥存储、签名算法、校验流程,必须经过严格的安全审计。

4.6 跳转到 Bootloader:最后的交接

校验通过后,BootROM 会做最后几件事:

  1. 关闭自身占用的资源(比如临时用的 SRAM 缓冲区)。
  2. 设置好栈指针和异常向量表。
  3. 清理或传递一些启动参数(比如时钟频率、内存大小)。
  4. 跳转到 Bootloader 的入口地址。

这一步看似简单,但很容易踩坑。我记得有一次,BootROM 在跳转前忘了关掉自己使能的中断,结果 Bootloader 刚跑起来就被一个定时器中断打断,而中断向量表还没初始化……直接跑飞。从那以后,我每次写跳转逻辑,都会加一句:disable_interrupts()。

注意:跳转前,一定要确保 CPU 处于一个“干净”的状态。关中断、清流水线、设置好栈。否则,Bootloader 的第一条指令就可能崩溃。

4.7 小结:BootROM 的“三不”原则

最后,我总结一下 BootROM 的核心设计原则,你可以把它记在脑子里:

  • 不可变:物理只读,出厂固化,无法篡改。
  • 不可跳过:复位后必须执行,没有别的入口。
  • 不可伪造:校验逻辑必须强,公钥必须安全存储。

嗯,这一章的内容就到这里。BootROM 虽然代码量小,但它是整个系统的基石。你把它搞明白了,后面的可信启动、安全固件更新,才能站得住脚。