深入浅出:图解Firefly RK3399的TPL/SPL启动流程与U-Boot FIT镜像制作
深入浅出:图解Firefly RK3399的TPL/SPL启动流程与U-Boot FIT镜像制作
RK3399作为一款高性能的Armv8-A架构处理器,其启动流程涉及多个阶段的固件协作。不同于简单的操作指南,本文将带您深入理解从BootROM到U-Boot加载的完整链条,揭示TPL、SPL、ATF和FIT镜像的内在联系与设计哲学。
1. RK3399启动架构全景解析
RK3399的启动过程是一个精心设计的接力赛,每个阶段都有明确的职责和严格的交接条件。现代SoC的启动流程早已超越了简单的"加载-执行"模式,而是演变为一个多阶段验证、逐步解封的信任链。
1.1 BootROM:信任的起点
当RK3399上电后,首先运行的是固化在芯片内部的BootROM代码。这个阶段的几个关键特性值得注意:
- 只读执行:BootROM存储在芯片掩模ROM中,无法修改,确保初始代码绝对可信
- 最小化功能:仅支持基础的外设初始化(如时钟、内存控制器)
- 安全验证:会对下一阶段代码(TPL)进行签名校验
BootROM会根据BOOT引脚的电平选择启动介质(如eMMC、SPI Flash),然后从预定义位置加载TPL。这里有个技术细节:RK3399的BootROM要求TPL必须位于存储设备的0x40扇区(即LBA 64),这个设计避免了与分区表的冲突。
1.2 TPL:内存初始化专家
Trusted Primary Loader(TPL)是RK3399启动流程中的第一个可编程阶段,它的核心任务可以用三个关键词概括:
- DDR初始化:配置内存控制器时序参数
- 最小化环境:建立基础栈和全局变量
- 安全交接:验证SPL的完整性和真实性
在U-Boot的编译输出中,TPL对应的二进制文件是tpl/u-boot-tpl.bin。通过以下命令可以查看TPL的代码分布:
aarch64-none-linux-gnu-objdump -d tpl/u-boot-tpl | less1.3 SPL:硬件管家
Secondary Program Loader(SPL)接过TPL的接力棒,承担更复杂的硬件初始化工作:
- 完整外设驱动:包括MMC、USB、GPIO等
- 环境变量系统:建立基础的配置框架
- FIT镜像加载:解析并验证u-boot.itb的结构
SPL的一个关键设计选择是大小限制。由于通常运行在SRAM中,其代码体积必须严格控制。在U-Boot配置中,以下选项影响SPL的行为:
CONFIG_SPL_FRAMEWORK=y CONFIG_SPL_LOAD_FIT=y CONFIG_SPL_MMC_SUPPORT=y2. ATF与U-Boot的协同机制
Arm Trusted Firmware(ATF)是Armv8架构下的安全基石,在RK3399启动流程中扮演着承上启下的关键角色。
2.1 bl31.elf的核心职责
ATF编译生成的bl31.elf主要实现以下功能:
- 异常级别管理:处理Secure Monitor Call(SMC)
- 电源状态协调:实现CPU suspend/resume
- 安全服务分发:如加密、密钥管理
在Rockchip的实现中,bl31还承担了特定硬件初始化的任务。官方提供的rk3399_bl31_v1.36.elf已经包含了以下关键补丁:
- DDR频率调节策略
- 大核/小核启动顺序优化
- 安全内存区域的划分
2.2 异常级别转换流程
RK3399启动过程中的异常级别转换堪称精妙:
| 阶段 | 异常级别 | 说明 |
|---|---|---|
| BootROM | EL3 | 最高特权,可配置安全状态 |
| TPL | EL1 | 普通内核模式 |
| SPL | EL2 | 虚拟化扩展模式 |
| U-Boot | EL1 | 准备移交Linux内核 |
这个转换过程由ATF的bl31协调完成,确保每个阶段只能访问其权限范围内的资源。
3. FIT镜像的工程实践
Flattened Image Tree(FIT)是U-Boot引入的现代固件打包格式,它解决了传统镜像的多个痛点:
- 多组件整合:可包含ATF、U-Boot、设备树等
- 版本控制:支持哈希校验和数字签名
- 灵活配置:不同硬件可加载不同子镜像
3.1 镜像描述文件剖析
Rockchip提供的make_fit_atf.py脚本生成的.its文件包含以下关键部分:
/dts-v1/; / { description = "FIT image for U-Boot with ATF"; #address-cells = <1>; images { uboot { description = "U-Boot"; data = /incbin/("./u-boot-nodtb.bin"); type = "standalone"; arch = "arm64"; compression = "none"; load = <0x00200000>; entry = <0x00200000>; }; atf { description = "ARM Trusted Firmware"; data = /incbin/("./bl31.elf"); type = "firmware"; arch = "arm64"; compression = "none"; load = <0x00080000>; entry = <0x00080000>; }; }; };3.2 镜像生成全流程
完整的FIT镜像生成涉及多个工具链组件的协作:
编译阶段:
make CROSS_COMPILE=aarch64-none-linux-gnu- BL31=atf-bl31 u-boot.itb内部处理流程:
make_fit_atf.py生成.its描述文件mkimage处理设备树编译dtc将描述文件转换为二进制形式
最终产物:
u-boot.itb:包含所有组件的FIT镜像idbloader.img:TPL+SPL的组合镜像
4. 调试技巧与实战经验
在实际开发中,启动流程的调试往往是最具挑战性的环节。以下是几个经过验证的有效方法:
4.1 串口调试输出配置
RK3399的调试串口默认配置为UART2,在U-Boot中需要确保以下配置正确:
#define CONFIG_DEBUG_UART_BASE 0xFF1A0000 #define CONFIG_DEBUG_UART_CLOCK 24000000 #define CONFIG_BAUDRATE 1500000高波特率(1.5Mbps)可以确保在大量调试输出时不丢失数据。
4.2 关键断点设置
使用JTAG调试时,这些地址值得关注:
- 0xFF8C0200:TPL入口点
- 0xFF8C0000:SPL加载地址
- 0x00200000:U-Boot主体加载地址
在U-Boot代码中插入asm volatile("brk #0");可以触发调试断点。
4.3 常见问题排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 卡在BootROM | TPL未烧录到正确位置 | 检查LBA 64是否包含有效数据 |
| TPL运行后无输出 | DDR参数配置错误 | 验证lpddr4_get_ddrconfig() |
| SPL无法加载u-boot.itb | FIT镜像签名验证失败 | 检查mkimage的签名参数 |
| 频繁复位 | ATF与U-Boot版本不兼容 | 使用匹配的bl31.elf版本 |
在开发过程中,我遇到过一个典型问题:当使用某些品牌的eMMC芯片时,SPL阶段会出现读取超时。最终发现是HS400模式下的时序问题,通过在配置中关闭相关选项解决:
CONFIG_MMC_HS400_SUPPORT=n CONFIG_MMC_SDHCI_SDMA=n这个案例提醒我们,硬件差异可能导致理论上正确的代码实际无法运行。
