4、启动流程第一阶段(BL1):片上ROM代码、加载BL2镜像、验证BL2签名、硬件加速器调用

好,咱们正式开始聊TEE启动的第一关——BL1阶段。

很多人觉得启动流程嘛,不就是上电、跑代码、跳转?其实没那么简单。尤其是在安全启动的场景下,BL1是整个信任链的根。根如果烂了,后面全白搭。我当年第一次接触安全启动时,就踩过这个坑——以为ROM代码肯定没问题,结果发现硬件加速器的配置没对齐,签名验证直接挂掉。嗯,从那以后我再也不敢轻视BL1了。

4.1 BL1是什么?说白了就是芯片出厂时固化的第一段代码

BL1,全称Boot Loader Stage 1,它躺在芯片的片上ROM里。你买回来的芯片,只要一上电,CPU复位后第一条指令就从这里取。ROM是只读的,所以这段代码你改不了,也删不掉。这恰恰是它的安全优势——攻击者想篡改?没门。

我个人习惯把BL1比作「看门的老大爷」。它不干重活,但负责确认进门的人是不是自己人。它只做三件事:

  • 初始化最基本的硬件(时钟、SRAM、看门狗)
  • 从指定的存储介质(比如eMMC、NAND Flash、SPI NOR)加载BL2镜像
  • 验证BL2的签名,确认镜像没被篡改

你想想看,如果BL1本身能被改,那整个安全体系就崩塌了。所以芯片厂商会把BL1死死锁在ROM里,物理上只读。

4.2 加载BL2镜像:从存储介质到SRAM

BL1跑起来后,第一件事就是找BL2。BL2通常存在外部Flash里。为什么?因为ROM空间太小了,一般只有几十KB到几百KB,放不下完整的BL2。

加载过程大致是这样:

  1. BL1初始化Flash控制器,配置好时钟和时序
  2. 从固定的偏移地址(比如Flash的0x00020000)读取BL2头部
  3. 头部里记录了BL2的大小、加载地址、签名信息
  4. BL1把BL2镜像从Flash拷贝到片上SRAM

这里有个关键点:加载地址必须是SRAM。因为此时DDR还没初始化,外部内存不可用。我见过一个项目,工程师把加载地址配到了DDR空间,结果BL1一拷贝就崩溃——DDR连电都没上呢。

核心原则:BL1阶段只能使用片上SRAM,不能依赖外部内存。SRAM大小通常只有几百KB,所以BL2镜像必须严格控制体积。

4.3 验证BL2签名:信任链的第一道锁

镜像加载完了,直接跳过去执行?不行。万一镜像被人替换成恶意代码呢?所以BL1必须验签。

验签流程我简化一下:

  • BL1内部硬编码了公钥的哈希值(或者公钥本身)
  • 从BL2头部提取签名值(通常是RSA或ECDSA签名)
  • 用公钥解密签名,得到摘要A
  • 对BL2镜像内容做哈希,得到摘要B
  • 比较A和B,一致则通过,否则拒绝启动

这里有个细节:公钥本身怎么信任?答案是在芯片制造时,厂商把公钥的哈希烧进了OTP(一次性可编程)区域。BL1启动时,先从OTP读哈希,再和自己ROM里的公钥比对。如果匹配,说明公钥是原厂的。

注意:OTP区域一旦写入就不能改。如果你在开发阶段频繁更换密钥,OTP会很快耗尽。我建议开发板用可配置的efuse模拟OTP,量产前再切到真实OTP。

4.4 硬件加速器调用:别让CPU干苦力

验签涉及RSA或ECDSA运算,这些非对称算法计算量很大。如果让CPU纯软件算,一个2048位的RSA验签可能要几百毫秒甚至几秒。用户开机等这么久?不现实。

所以BL1会调用硬件加速器。常见的硬件加速器包括:

加速器类型 用途 典型延迟
RSA加速器 非对称加解密、验签 几毫秒到几十毫秒
哈希加速器(SHA256/SHA512) 计算镜像摘要 微秒级
AES加速器 对称加解密(部分场景用于解密BL2) 微秒级

调用方式通常是:BL1往加速器的寄存器里写数据,触发运算,然后轮询或等待中断。我个人习惯用轮询,因为BL1阶段中断系统还没完全初始化,轮询更可靠。

小技巧:硬件加速器通常有「密钥槽」的概念。你可以把公钥提前加载到密钥槽里,验签时直接引用槽号,避免每次传输大密钥。我在一个项目里用这个优化,把BL1启动时间从800ms降到了120ms。

4.5 整体流程的SVG示意图

下面这张图把BL1阶段的完整流程画出来了。你可以看到从ROM代码开始,到加载、验签、跳转的完整链路。

BL1阶段启动流程 ① 上电复位 → CPU从ROM取指 ② BL1初始化:时钟、SRAM、看门狗 (ROM代码,不可修改) ③ 从Flash固定偏移加载BL2镜像到SRAM (读取头部 → 获取大小和地址 → DMA拷贝) ④ 验证BL2签名 硬件加速器:SHA256摘要 + RSA2048验签 验签失败 → 死循环 ⑤ 验签通过 → 跳转到BL2入口 信任链根:OTP中的公钥哈希 注:BL1阶段全程使用片上SRAM,不依赖DDR 硬件加速器由BL1通过寄存器直接控制

4.6 避坑指南:我踩过的几个雷

做BL1开发,有几个地方特别容易出问题。我列出来,你遇到了可以少走弯路。

  • SRAM大小估算错误:BL2镜像加上签名头部,不能超过SRAM容量。我曾经算漏了签名头部的开销,结果镜像加载到一半溢出。建议在链接脚本里预留20%的余量。
  • 硬件加速器时钟没开:有些加速器默认是关时钟的,BL1必须显式使能。我遇到过加速器返回全0的情况,查了半天发现是时钟门控没开。
  • OTP写入顺序:如果OTP支持多次写入(比如efuse),一定要先写公钥哈希,再写使能位。顺序反了会导致OTP永久锁定,公钥还没写进去。
  • 验签超时处理:硬件加速器偶尔会卡住(比如电压不稳)。BL1应该加一个超时计数器,超时后复位加速器重试。我一般设5ms超时,重试3次。

总结一下:BL1是整个安全启动的基石。它虽然代码量不大,但涉及硬件初始化、存储加载、密码学验签、硬件加速器协同等多个领域。写BL1代码时,脑子里要时刻绷着一根弦——「这段代码如果被攻击者利用,后果是什么?」。保持这种警惕,你就能写出可靠的BL1。

可可精品资料库