3. 启动流程第一阶段 (BL1)
各位同学,欢迎来到TEE安全启动的实战课。今天咱们聊聊整个启动链条里最底层的那个环节——BL1阶段。说白了,就是芯片上电后,CPU跑的第一段代码。
这个阶段,我个人习惯叫它“从零到一”的过程。芯片刚上电时,DDR还没初始化,代码只能放在片内SRAM里跑。BootROM就是固化在芯片内部的一段只读程序,它负责把后续的信任链建立起来。
1. BootROM到底在干什么?
BootROM的执行流程,其实没那么神秘。我拆解成几个关键步骤:
- 上电复位:CPU从复位向量地址(通常是0x00000000或0xFFFF0000)开始执行。
- 最小系统初始化:设置栈指针、关中断、配置最基本的时钟。
- 硬件自检:检查CPU核心、SRAM、关键外设是否正常。
- 启动介质检测:判断从哪个设备加载下一级镜像(比如eMMC、NAND Flash、UART)。
- 加载BL2镜像:把下一级代码从存储介质搬到SRAM里。
- 验证签名:用公钥验签,确保镜像没被篡改。
- 跳转执行:验证通过后,跳转到BL2的入口地址。
嗯,这里要注意一点:BootROM本身是不可修改的。所以它的代码必须极其精简、健壮。我在项目中遇到过,某款芯片的BootROM有个小bug,导致某些批次eMMC的初始化时序不对,最后只能通过修改PCB上的上拉电阻来规避——这种坑,能避就避。
2. 硬件初始化:时钟、DDR、MMU
BootROM里做的硬件初始化,跟操作系统启动时的初始化不太一样。它只做“够用”的配置,不会把所有外设都打开。
2.1 时钟初始化
芯片上电后,默认时钟通常来自内部低速振荡器(比如32kHz或几MHz)。BootROM需要做的是:
- 切换到外部晶振或PLL锁相环
- 配置CPU核心频率、总线频率
- 使能关键外设的时钟门控
我个人的经验是,时钟配置一定要看数据手册里的“时钟树”图。有一次我图省事,直接复制了参考代码,结果DDR初始化死活过不去——后来发现是PLL的反馈分频系数设错了,导致DDR时钟频率超了规格。
2.2 DDR初始化
DDR初始化是BootROM里最复杂的部分之一。因为DDR控制器需要精确的时序参数,而这些参数跟PCB布线、颗粒型号、温度都有关。
典型流程包括:
- 配置DDR控制器寄存器(地址映射、突发长度、CAS延迟等)
- 发送DDR初始化命令序列(NOP、预充电、刷新、模式寄存器设置)
- 进行ZQ校准(针对DDR3/DDR4)
- 执行写电平训练(Write Leveling)
- 验证DDR读写正确性(通常写一个固定模式再读回来)
这里有个避坑指南:我曾经遇到过DDR初始化偶尔失败的问题,排查了很久才发现是电源纹波太大,导致DDR的PLL锁定不稳定。后来在PCB设计时加了更干净的滤波电容,问题就解决了。所以,硬件设计阶段就要考虑DDR的电源完整性。
2.3 MMU初始化
BootROM阶段要不要开MMU?这取决于芯片架构。有些芯片在BootROM里就开启MMU,用页表把SRAM和DDR映射到虚拟地址空间,方便代码执行。但更多时候,BootROM阶段不开MMU,直接跑在物理地址上。
我个人建议:除非有特殊需求(比如需要访问非连续物理内存),否则BootROM阶段尽量不开MMU。原因很简单——少一层映射,就少一个出bug的机会。而且BootROM代码通常很小,SRAM完全够用。
3. 签名验证的硬件加速
这是TEE安全启动的核心环节。BootROM必须验证下一级镜像的签名,确保它来自可信的厂商,并且没有被篡改过。
纯软件做RSA-2048签名验证,在几十MHz的CPU上可能要跑几百毫秒甚至几秒。这对启动速度来说太慢了。所以现代SoC都集成了硬件加速器。
3.1 RSA加速器
RSA的核心运算是大数模幂(modular exponentiation)。硬件加速器通常包含:
- 大数乘法器(1024位/2048位)
- 蒙哥马利模乘单元
- 密钥存储寄存器(公钥指数和模数)
使用方式很简单:把签名和公钥写到特定寄存器,触发运算,然后读取结果。整个过程是原子性的,中间不能被中断。
关键点:公钥的存储位置必须安全。通常公钥会烧录在一次性可编程存储器(OTP)或eFuse里,BootROM直接从硬件寄存器读取,不给软件篡改的机会。
3.2 SHA加速器
签名验证前,先要对镜像做哈希计算。SHA-256是主流选择。硬件SHA加速器可以:
- 自动处理数据填充(padding)
- 支持DMA方式搬运数据,不占用CPU
- 一次计算多个块(多缓冲区)
我记得在某个项目中,BootROM需要验证一个几MB的BL2镜像。如果用软件算SHA256,光哈希就要花掉200ms。换成硬件加速器后,时间降到了5ms以内——这个差距,在量产测试时非常明显。
3.3 硬件加速的典型流程
结合RSA和SHA,完整的硬件加速签名验证流程如下:
- 从存储介质读取BL2镜像到SRAM缓冲区
- 配置SHA加速器,计算镜像的哈希值(H)
- 从OTP/eFuse读取公钥(N, e)
- 从镜像头部读取签名值(S)
- 配置RSA加速器,计算 S^e mod N,得到解密后的哈希值(H')
- 比较 H 和 H' 是否相等
- 如果相等,验证通过;否则,进入错误处理(比如死循环或重启)
小技巧:有些芯片支持“哈希+签名验证”的流水线操作。在SHA计算最后一个块的同时,RSA加速器可以开始处理之前的结果。这样能进一步缩短验证时间。
4. 一张图看懂BL1阶段
下面我用一张流程图,把整个BL1阶段串起来。你一看就明白。
重要提醒:图中第4步“DDR初始化”的位置是有讲究的。有些芯片把DDR初始化放在签名验证之后,目的是减少攻击面——万一DDR有硬件后门,恶意代码可能通过DMA篡改验证逻辑。但这样做的代价是,签名验证期间只能用片内SRAM,对大镜像不友好。具体怎么选,取决于你的安全威胁模型。
5. 实战中的几个关键点
最后,我分享几个在项目中踩过的坑,希望能帮你少走弯路。
- BootROM的代码大小限制:片内SRAM通常只有几十KB到几百KB。BootROM本身占一部分,留给BL2镜像的空间就很有限了。所以BL2必须精简,能放在SRAM里跑完初始化就行。
- 签名验证的密钥管理:公钥烧录到OTP后,要确保有“熔断”机制——烧录后就不能再改了。我见过一个方案,公钥竟然存在Flash里,结果被攻击者替换了公钥,整个安全启动形同虚设。
- 硬件加速器的错误处理:RSA加速器在计算过程中可能出错(比如奇偶校验失败)。BootROM必须检查加速器的状态寄存器,不能盲目信任硬件结果。
- 启动介质的选择顺序:BootROM通常会按照优先级尝试多个启动介质(比如先eMMC,再SD卡,最后UART)。这个顺序要小心设计,避免被攻击者通过插入恶意介质来劫持启动流程。
好了,BL1阶段的内容就讲到这里。你想想看,整个安全启动的信任链,就是从这段不可修改的BootROM代码开始的。它虽然简单,但每一行代码都关乎整个系统的安全根基。