3. STM32MP启动流程与安全启动
STM32MP1系列处理器的启动过程是一条严格分级的信任链。理解这条链上每一级固件的位置、职责和验证方式,是配置安全启动和排查启动故障的基础。整个启动链可以概括为:ROM Code → FSBL(TF-A BL2)→ OP-TEE(BL32)与SSBL(U-Boot,BL33)→ Linux内核。每一级在移交控制权之前,都要对下一级镜像进行完整性和真实性验证。
一、启动链的五个阶段
第一阶段:ROM Code。芯片上电或复位后,首先执行固化在芯片内部ROM中的代码。ROM Code不可修改,它负责最基础的硬件初始化(时钟、部分外设),然后根据BOOT引脚配置和OTP(One-Time Programmable)熔丝状态,从指定启动介质(eMMC、SD卡、NOR/NAND Flash、USB、UART)加载FSBL。在安全启动模式下,ROM Code会使用存储在OTP中的公钥哈希(PKH)验证FSBL头部的签名,验证通过才继续执行。
第二阶段:FSBL(First Stage Boot Loader)。在STM32MP1的典型方案中,FSBL由TF-A(Trusted Firmware-A)的BL2阶段承担。BL2运行在SYSRAM中,负责初始化DDR控制器、配置时钟树、加载后续镜像到DDR。BL2需要从启动介质中读取FIP(Firmware Image Package)容器,解析其中的BL32(OP-TEE)和BL33(U-Boot)镜像,并分别验证其签名。
第三阶段:OP-TEE(BL32)。OP-TEE是一个可信执行环境,运行在ARM Cortex-A7的Secure World中。它提供安全服务接口,如安全存储、加解密引擎调用、随机数生成等。在启动链中,BL2将OP-TEE加载到安全内存区域并验证签名后,跳转执行。OP-TEE初始化完成后,会返回BL2或直接由BL2继续加载BL33。
第四阶段:SSBL(U-Boot,BL33)。U-Boot运行在Normal World,负责加载Linux内核、设备树和根文件系统。在安全启动场景下,U-Boot自身已由BL2验证,但U-Boot在加载内核时还可以通过FIT(Flattened Image Tree)镜像格式对内核和设备树进行签名验证,形成从ROM到内核的完整信任链。
第五阶段:Linux内核。内核启动后,可以通过dm-verity等机制对根文件系统进行完整性校验,进一步延伸信任链。
二、签名验证机制
STM32MP1的安全启动依赖非对称加密体系。整个验证过程围绕密钥对展开:私钥用于签名,公钥用于验证。公钥的哈希值(PKH)被烧录到OTP中,作为信任根。
具体流程如下:
- 密钥生成与烧录:开发者生成ECDSA或RSA密钥对,将公钥哈希写入OTP。此操作不可逆,一旦烧录,后续所有FSBL镜像必须使用对应私钥签名。
- 镜像签名:使用STM32 Signing Tool对FSBL、FIP包进行签名,生成包含签名值的头部。签名覆盖镜像的哈希值,确保任何字节改动都会导致验证失败。
- 逐级验证:ROM Code验证FSBL头部签名;FSBL验证FIP中BL32和BL33的签名;U-Boot通过FIT格式验证内核和设备树。每一级验证失败都会中止启动。
注意:OTP烧录不可逆。在量产前务必在开发板上充分验证密钥和签名流程,确认无误后再进行量产烧录。建议保留一份未烧录OTP的样片用于调试。
三、启动失败恢复策略
安全启动开启后,任何一级验证失败都会导致启动中止。如果没有恢复机制,设备可能变砖。STM32MP1提供了多层次的恢复手段:
| 失败阶段 | 现象 | 恢复方式 |
|---|---|---|
| ROM Code验证FSBL失败 | 无串口输出,或输出ROM错误码 | 切换BOOT引脚到备用介质(如SD卡),或进入USB DFU模式重新烧录 |
| FSBL加载/验证BL32/BL33失败 | 串口输出TF-A错误日志后停止 | 通过UART/USB DFU重新烧录FIP包;或使用未签名镜像临时启动 |
| U-Boot验证内核失败 | U-Boot输出FIT验证错误 | 回退到备用FIT镜像;或进入U-Boot控制台手动加载恢复镜像 |
| 内核dm-verity校验失败 | 内核panic,提示hash mismatch | 挂载只读恢复分区,触发OTA回滚到上一版本 |
在实际产品设计中,建议采用A/B双分区方案:主分区启动失败时,自动切换到备份分区。同时保留一个可通过物理按键或串口触发的恢复模式,用于强制进入DFU或U-Boot控制台。这样即使签名验证失败,也能通过重新烧录恢复设备。
调试建议:在开发阶段,可以先关闭安全启动(不烧录OTP),验证启动链各阶段是否正常。确认无误后,再逐步开启签名验证。每次修改FSBL或FIP后,务必重新签名并验证,避免因签名不匹配导致启动失败。
四、关键配置与参数
在TF-A和U-Boot的配置中,与安全启动相关的关键参数包括:
# TF-A 编译配置示例
make PLAT=stm32mp1 \
ARCH=aarch32 \
ARM_ARCH_MAJOR=7 \
AARCH32_SP=optee \
DTB_FILE_NAME=stm32mp157d-dk1.dtb \
BL33_CFG=u-boot.dtb \
TRUSTED_BOARD_BOOT=1 \
GENERATE_COT=1 \
MBEDTLS_DIR=../mbedtls
# U-Boot 环境变量示例
setenv bootargs 'root=/dev/mmcblk0p5 rootwait rw'
setenv kernel_addr_r 0xC2000000
setenv fdt_addr_r 0xC4000000
setenv bootcmd 'fatload mmc 0:4 ${kernel_addr_r} fit.itb; bootm ${kernel_addr_r}'
其中TRUSTED_BOARD_BOOT=1启用可信启动,GENERATE_COT=1生成信任链证书。U-Boot的bootm命令在加载FIT镜像时会自动验证签名,验证失败则拒绝启动。
总结来说,STM32MP1的安全启动是一条从ROM Code到Linux内核的完整信任链。每一级固件在移交控制权前都要验证下一级的签名,任何环节失败都会中止启动。通过合理的恢复策略(备用介质、DFU模式、A/B分区、恢复控制台),可以在保证安全性的同时避免设备变砖。理解这条链的每个环节,是进行安全升级和量产部署的前提。