1. BL31启动流程深度解析
在ARM Trusted Firmware(ATF)架构中,BL31作为关键的运行时固件,承担着系统启动和运行时管理的双重职责。本文将深入剖析BL31的启动流程、核心功能实现机制以及关键技术细节。
1.1 BL31的双重角色定位
BL31在ARM系统中扮演着两个关键角色:
- 启动流程执行者:负责完成硬件初始化、安全环境建立,并协调BL32(如OP-TEE)和BL33(如U-Boot)的加载执行
- 运行时服务提供者:系统启动完成后,作为EL3级别的安全监控器(Secure Monitor)处理SMC调用和EL3中断
为什么需要独立的BL31?
- 职责分离原则:BL2专注于固件加载,BL31专注运行时服务,模块化设计更安全灵活
- 安全考虑:BL2执行后应立即退出,避免成为攻击入口
- 架构要求:BL2可能运行在S-EL1,而运行时服务需要EL3特权级
1.2 冷启动与热启动路径
BL31支持两种启动模式,通过RESET_TO_BL31编译选项控制:
c复制// 冷启动路径(默认)
el3_entrypoint_common \
_init_sctlr=0 \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=1 \
_exception_vectors=runtime_exceptions \
_pie_fixup_size=BL31_LIMIT - BL31_BASE
// 热启动路径
el3_entrypoint_common \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=runtime_exceptions \
_pie_fixup_size=BL31_LIMIT - BL31_BASE
冷启动特点:
- 从BL1开始完整启动链
- BL1已完成基础硬件初始化
- 启动时间较长但安全性更高
热启动特点:
- 通过RVBAR_EL3寄存器直接跳转到BL31
- 需要自行完成全部初始化
- 启动速度快,适合性能敏感场景
1.3 关键初始化流程
BL31的初始化可分为三个阶段:
-
入口处理(bl31_entrypoint):
- 保存前级引导参数(x0-x3)
- 建立C运行时环境
- 配置异常向量表
-
平台初始化(bl31_setup):
c复制void bl31_setup(u_register_t arg0, u_register_t arg1,
u_register_t arg2, u_register_t arg3)
{
/* 早期平台相关初始化 */
bl31_early_platform_setup2(arg0, arg1, arg2, arg3);
/* 后期架构相关初始化 */
bl31_plat_arch_setup();
}
- 控制台初始化
- 解析BL2传递的镜像描述信息
- 建立内存页表并启用MMU
- 主执行流程(bl31_main):
c复制void bl31_main(void)
{
/* 基础驱动初始化 */
bl31_platform_setup();
/* 运行时服务初始化 */
runtime_svc_init();
/* BL32初始化(如OP-TEE)*/
if (bl32_init) {
(*bl32_init)();
}
/* 准备跳转到BL33 */
bl31_prepare_next_image_entry();
}
2. 运行时服务框架剖析
2.1 SMC调用处理机制
ARM架构通过SMC指令实现安全世界与非安全世界的通信,其调用规范如下:

关键参数说明:
- Bit[31]:调用类型(Fast/Standard)
- Bit[30]:调用位宽(32/64位)
- Bits[29:24]:服务所有者编号(OEN)
- Bits[23:16]:保留位
- Bits[15:0]:功能编号
2.2 服务注册与分发
运行时服务通过DECLARE_RT_SVC宏注册:
c复制#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch) \
static const rt_svc_desc_t __svc_desc_ ## _name \
__section("rt_svc_descs") __used = { \
.start_oen = _start, \
.end_oen = _end, \
.call_type = _type, \
.name = #_name, \
.init = _setup, \
.handle = _smch }
服务处理流程:
- 根据SMC功能ID计算索引值
- 从rt_svc_descs_indices数组获取服务描述符
- 调用对应的handle函数
c复制uintptr_t handle_runtime_svc(uint32_t smc_fid, void *cookie,
void *handle, unsigned int flags)
{
int idx = get_unique_oen_from_smc_fid(smc_fid);
int index = rt_svc_descs_indices[idx];
rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;
return rt_svc_descs[index].handle(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
}
2.3 典型服务实现
2.3.1 PSCI服务
电源状态协调接口(PSCI)是ARM定义的电源管理标准:
c复制DECLARE_RT_SVC(
std_svc,
OEN_STD_START,
OEN_STD_END,
SMC_TYPE_FAST,
std_svc_setup,
std_svc_smc_handler
);
uintptr_t std_svc_smc_handler(uint32_t smc_fid, ...)
{
if (is_psci_fid(smc_fid)) {
return psci_smc_handler(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
}
// 其他标准服务处理
}
PSCI主要功能接口:
c复制#define PSCI_CPU_ON_AARCH64 0xc4000003
#define PSCI_CPU_SUSPEND_AARCH64 0xc4000001
#define PSCI_SYSTEM_OFF 0x84000008
#define PSCI_SYSTEM_RESET 0x84000009
2.3.2 OP-TEE服务
可信执行环境服务通过两种类型注册:
c复制/* Fast调用类型 */
DECLARE_RT_SVC(
opteed_fast,
OEN_TOS_START,
OEN_TOS_END,
SMC_TYPE_FAST,
opteed_setup,
opteed_smc_handler
);
/* Standard调用类型 */
DECLARE_RT_SVC(
opteed_std,
OEN_TOS_START,
OEN_TOS_END,
SMC_TYPE_STD,
NULL,
opteed_smc_handler
);
3. BL31与OP-TEE的交互
3.1 OP-TEE启动流程

关键步骤:
- 获取BL32镜像入口信息
- 初始化安全CPU上下文
- 通过el3_exit跳转到OP-TEE
- OP-TEE初始化完成后通过SMC返回
3.2 上下文保存与恢复
进入OP-TEE前的上下文保存:
assembly复制func opteed_enter_sp
mov x3, sp
str x3, [x0, #0]
sub sp, sp, #OPTEED_C_RT_CTX_SIZE
stp x19, x20, [sp, #OPTEED_C_RT_CTX_X19]
// ...保存其他寄存器...
stp x29, x30, [sp, #OPTEED_C_RT_CTX_X29]
b el3_exit
endfunc opteed_enter_sp
从OP-TEE返回后的上下文恢复:
assembly复制func opteed_exit_sp
mov sp, x0
ldp x19, x20, [x0, #(OPTEED_C_RT_CTX_X19 - OPTEED_C_RT_CTX_SIZE)]
// ...恢复其他寄存器...
ldp x29, x30, [x0, #(OPTEED_C_RT_CTX_X29 - OPTEED_C_RT_CTX_SIZE)]
mov x0, x1
ret
endfunc opteed_exit_sp
3.3 SMC调用处理
OP-TEE服务的主要SMC处理分支:
c复制uint64_t opteed_smc_handler(uint32_t smc_fid, ...)
{
switch (smc_fid) {
case TEESMC_OPTEED_RETURN_ENTRY_DONE:
// OP-TEE初始化完成
opteed_synchronous_sp_exit(optee_ctx, x1);
break;
case TEESMC_OPTEED_RETURN_CALL_DONE:
// 普通世界调用完成
cm_el1_sysregs_context_restore(NON_SECURE);
cm_set_next_eret_context(NON_SECURE);
SMC_RET4(ns_cpu_context, x1, x2, x3, x4);
case TEESMC_OPTEED_RETURN_FIQ_DONE:
// FIQ处理完成
cm_el1_sysregs_context_restore(NON_SECURE);
cm_set_next_eret_context(NON_SECURE);
SMC_RET0((uint64_t) ns_cpu_context);
default:
panic();
}
}
4. 关键问题与实战经验
4.1 常见问题排查
-
SMC调用失败:
- 检查功能ID是否在服务支持的OEN范围内
- 验证调用类型(Fast/Standard)是否匹配
- 确认调用时CPU处于正确的异常等级
-
OP-TEE启动异常:
- 检查BL32镜像的入口地址和参数
- 验证上下文保存区域是否足够
- 确认安全内存映射正确配置
-
中断路由问题:
- ��认GIC分组配置正确
- 检查SCR_EL3.IRQ/FIQ位设置
- 验证中断优先级配置
4.2 性能优化建议
-
热启动优化:
- 合理配置RVBAR_EL3寄存器
- 预初始化关键硬件资源
- 优化BL31镜像加载位置
-
SMC调用优化:
- 优先使用Fast Call类型
- 减少上下文切换开销
- 合理使用缓存维护操作
-
内存管理优化:
- 精细配置MMU页表属性
- 合理使用DCache和ICache
- 优化内存访问模式
4.3 安全加固措施
-
运行时保护:
- 启用栈保护机制
- 限制非安全世界访问安全资源
- 实施严格的参数检查
-
启动完整性:
- 验证各级镜像签名
- 实施安全启动链
- 保护关键数据不被篡改
-
异常处理:
- 完善异常向量表处理
- 实施安全断言机制
- 记录关键错误日志
5. 总结与进阶方向
BL31作为ARM可信固件的核心组件,其设计体现了以下关键思想:
- 模块化设计:通过运行时服务框架实现功能解耦
- 安全隔离:严格区分不同异常等级和世界状态
- 灵活扩展:支持多种信任执行环境实现
深入理解BL31需要结合:
- ARM架构手册(特别是异常处理和电源管理部分)
- GIC中断控制器规范
- 特定TrustOS实现(如OP-TEE)的交互协议
后续可深入研究:
- 多核启动与电源管理协同
- 安全调试机制实现
- 动态测量与认证机制
- 与Hypervisor的协同设计
实际调试经验分享:
在调试BL31与OP-TEE的交互时,建议:
- 在关键跳转点添加调试输出
- 使用JTAG调试器捕获异常状态
- 逐条验证SMC调用参数
- 检查各异常等级的寄存器状态一致性
特别注意缓存一致性问题,必要时手动维护缓存。
