4. OP-TEE启动流程
OP-TEE 的启动并不是“内核起来后再加载一个安全模块”这么简单,它是一条从芯片上电第一条指令开始、逐级校验、逐级移交控制权的安全启动链。理解这条链路,是后续定位“TEE 起不来”“TA 加载失败”“安全世界与普通世界切换异常”等问题的前提。本节按时间顺序拆解 BL1→BL2→BL31/BL32→OP-TEE OS→TA 的完整路径,并给出可观测的调试切入点。
一、核心原理:信任根与逐级度量
安全启动链的本质是“信任锚点不可变,后续每一级都由上一级验证”。芯片内部固化的 BootROM 是信任根(Root of Trust),它只信任烧录在一次性可编程区域或受保护存储中的公钥哈希。BL1 由 BootROM 校验签名后加载,BL1 再校验 BL2,BL2 校验 BL31、BL32(OP-TEE OS)以及普通世界的 BL33(U-Boot/UEFI)。每一级只对下一级负责,形成一条单向信任链。
在 ARMv8-A 的典型 ATF(ARM Trusted Firmware)架构中,各阶段的职责划分如下:
- BL1:运行于 EL3,完成最早期 CPU 初始化、异常向量设置,从 BootROM 移交后校验并加载 BL2。
- BL2:运行于 EL3/S-EL1,负责平台初始化、内存布局准备,加载并校验 BL31、BL32、BL33 镜像。
- BL31:运行于 EL3,是安全监控器(Secure Monitor),提供 PSCI、SMC 调用入口,负责安全世界与普通世界的上下文切换。
- BL32:即 OP-TEE OS,运行于 S-EL1,是安全世界的操作系统,管理 TA、安全内存、密码学服务。
- BL33:普通世界引导程序,最终引导 Linux 内核。
二、阶段流程与信号链
下图展示从复位向量到 TA 执行的完整控制流与校验关系。实线表示控制权移交,虚线表示签名校验,颜色区分不同异常等级。
三、OP-TEE OS 初始化关键动作
BL32 被 BL31 跳入后,OP-TEE OS 开始自己的初始化序列。这一阶段是排障时最常看日志的地方,典型顺序如下:
- 入口与异常向量:设置 S-EL1 的异常向量表,初始化栈指针。
- 平台早期初始化:配置 GIC、串口、定时器,建立早期调试输出。
- MMU 与内存映射:建立安全内存(Secure RAM)、共享内存(SHM)、外设映射,划分 TA 可用区域。
- 核心服务初始化:线程调度、互斥、RPC、密码学驱动、存储驱动(RPMB/eMMC)。
- 加载内置 TA:如
early_ta、系统 PTA,完成签名验证后放入 TA 池。 - 进入等待循环:通过 SMC 等待 Normal World 的请求。
启动日志中常见的可观测点:
I/TC: OP-TEE version: 3.22.0
I/TC: Primary CPU initializing
I/TC: Primary CPU switching to normal world boot
I/TC: Secondary CPU 1 initializing
I/TC: Non-secure external DT found
I/TC: OP-TEE memory: 0x... size 0x...
I/TC: Loading early TA ...
I/TC: Initialized
optee 节点与共享内存配置。四、TA 加载与执行:从 CA 到安全世界
TA 不是开机就全部加载的,而是按需加载。普通世界的 CA 通过 libteec 发起会话,流程如下:
- CA 调用
TEEC_InitializeContext打开/dev/tee0,进入 OP-TEE 驱动。 - 驱动通过 SMC 陷入 BL31,BL31 将请求转发给 BL32。
- OP-TEE OS 根据 UUID 查找 TA;若未加载,则从 REE FS 或内置镜像读取 TA 二进制。
- 验证 TA 签名与哈希,分配 S-EL0 地址空间,建立页表。
- 在 S-EL0 执行 TA 的
TA_InvokeCommandEntryPoint,通过共享内存与 CA 交换数据。
一个典型的 CA 调用片段:
TEEC_Context ctx;
TEEC_Session sess;
TEEC_InitializeContext(NULL, &ctx);
TEEC_OpenSession(&ctx, &sess, &ta_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
TEEC_Operation op = {0};
op.params[0].tmpref.buffer = buf;
op.params[0].tmpref.size = len;
TEEC_InvokeCommand(&sess, CMD_DEMO, &op, NULL);
五、阶段职责与排障对照表
| 阶段 | 异常等级 | 核心职责 | 常见故障现象 |
|---|---|---|---|
| BL1 | EL3 | 信任根校验、早期 CPU 初始化 | 上电无输出、卡在 BootROM |
| BL2 | EL3 | 加载校验 BL31/BL32/BL33 | 镜像签名失败、加载地址冲突 |
| BL31 | EL3 | SMC 分发、世界切换 | SMC 无响应、PSCI 异常 |
| BL32 | S-EL1 | OP-TEE OS 初始化、TA 管理 | 初始化卡死、共享内存映射错误 |
| TA | S-EL0 | 可信应用执行 | UUID 找不到、签名校验失败 |
六、误区与复盘
误区一:把 BL32 当成“另一个内核”独立启动。实际上 BL32 由 BL31 通过 bl32_init 入口跳入,二者通过 SMC 和共享内存协作,缺少 BL31 的 BL32 无法被 Normal World 访问。
误区二:TA 加载失败只查 TA 本身。很多情况下是 BL32 的 REE FS 路径、RPMB 密钥或共享内存配置错误,导致 TA 根本读不到。应先确认 OP-TEE OS 已完成初始化并进入等待循环。
误区三:忽略启动链的“单向性”。一旦 BL2 校验失败,后面所有阶段都不会执行,日志会突然中断。排障时应按 BL1→BL2→BL31→BL32 的顺序逐级确认,而不是直接跳到 TA。
复盘建议:在 bring-up 阶段打开 ATF 的 LOG_LEVEL=40 与 OP-TEE 的 CFG_TEE_CORE_LOG_LEVEL=3,保留完整串口日志;对每个阶段的入口地址、镜像哈希、共享内存基址做记录,形成可对比的启动基线。这样当 TA 加载异常时,可以快速判断问题出在信任链的哪一环,而不是在安全世界与普通世界之间反复猜测。