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 到底干了些什么?我把它拆成几个关键步骤:
- 复位向量跳转:CPU 复位后,从固定地址(比如 0x00000000 或 0xFFFF0000)取指。这个地址映射到 BootROM。
- 最小系统初始化:关看门狗、关中断、设置 CPU 模式、初始化栈指针(SP)。这一步通常用汇编完成。
- 时钟与 PLL 配置:芯片刚上电时通常跑在内部低速振荡器上。BootROM 要配置 PLL,把 CPU 和外设时钟提升到目标频率。
- 内存控制器初始化:如果芯片有片内 SRAM,这一步很简单。如果需要外部 DDR,那就要配置时序、电压、ODT 等参数。这一步最容易出问题。
- 启动介质检测:BootROM 会按照预设的优先级,去检测启动介质——比如先从 SPI Flash 读,不行再从 eMMC 试,再不行就回退到串口下载。
- 加载并校验第一阶段 Bootloader:从检测到的介质中读取一段代码(通常是 SPL 或 MBR),做完整性校验(CRC、签名等),然后跳转过去。
下面这张图,是我自己画的一个典型 BootROM 启动流程:
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 会做最后几件事:
- 关闭自身占用的资源(比如临时用的 SRAM 缓冲区)。
- 设置好栈指针和异常向量表。
- 清理或传递一些启动参数(比如时钟频率、内存大小)。
- 跳转到 Bootloader 的入口地址。
这一步看似简单,但很容易踩坑。我记得有一次,BootROM 在跳转前忘了关掉自己使能的中断,结果 Bootloader 刚跑起来就被一个定时器中断打断,而中断向量表还没初始化……直接跑飞。从那以后,我每次写跳转逻辑,都会加一句:disable_interrupts()。
注意:跳转前,一定要确保 CPU 处于一个“干净”的状态。关中断、清流水线、设置好栈。否则,Bootloader 的第一条指令就可能崩溃。
4.7 小结:BootROM 的“三不”原则
最后,我总结一下 BootROM 的核心设计原则,你可以把它记在脑子里:
- 不可变:物理只读,出厂固化,无法篡改。
- 不可跳过:复位后必须执行,没有别的入口。
- 不可伪造:校验逻辑必须强,公钥必须安全存储。
嗯,这一章的内容就到这里。BootROM 虽然代码量小,但它是整个系统的基石。你把它搞明白了,后面的可信启动、安全固件更新,才能站得住脚。