3. OP-TEE架构总览
理解 OP-TEE,不能只把它当成“一段运行在安全世界里的固件”。从工程视角看,OP-TEE 是一套横跨安全世界与普通世界的完整软件栈:安全侧有 OP-TEE OS、TA 与安全服务;普通侧有 CA、libteec、tee-supplicant 以及内核 TEE 驱动。只有把这几层之间的职责边界和调用关系理清,后续做启动链分析、平台移植和问题定位时,才能知道“问题出在哪一层”。
一、OP-TEE 的整体组成
OP-TEE 的典型部署可以拆成五个关键部分:
- OP-TEE OS(安全世界内核):运行在 ARM TrustZone 的安全态(Secure EL1/S-EL1),负责调度安全世界线程、管理安全内存、加载并执行 TA、提供内部安全服务。
- TA(Trusted Application,可信应用):运行在安全世界的应用,承载具体安全业务,例如密钥管理、安全存储、加解密、身份认证等。TA 只能通过 TEE 内部接口被调用,不能直接被普通世界访问。
- CA(Client Application,客户端应用):运行在普通世界(Normal World)的用户态程序,是业务逻辑的发起方。CA 通过 libteec 提供的 API 向 TEE 发起会话、调用命令、获取结果。
- libteec(TEE Client API 库):普通世界用户态库,封装了与内核 TEE 驱动交互的 ioctl 细节,向上提供标准 TEE Client API,如
TEEC_InitializeContext、TEEC_OpenSession、TEEC_InvokeCommand。 - tee-supplicant(普通世界用户态服务):为安全世界提供“代理服务”,例如加载 TA 镜像、读写 RPMB、访问文件系统、提供时间与随机数等。它本身不是 TEE 内核的一部分,而是普通世界的一个常驻进程。
- 内核 TEE 驱动(如 optee.ko):普通世界内核态驱动,负责与安全世界建立通信通道(SMC/HVC)、管理共享内存、转发 CA 请求与 supplicant 请求。
这六者并不是简单的上下层堆叠,而是存在“双向调用”关系:CA 向下调用 TA;TA 在执行过程中又可能反向请求普通世界的 tee-supplicant 协助。理解这条双向链路,是理解 OP-TEE 架构的关键。
二、CA 与 TA 的调用关系
CA 与 TA 之间通过“会话(Session)+ 命令(Command)”模型交互。典型流程如下:
- CA 调用
TEEC_InitializeContext打开 TEE 上下文,建立与内核驱动的连接。 - CA 调用
TEEC_OpenSession,指定目标 TA 的 UUID,请求打开一个会话。 - 内核驱动将请求转发到安全世界,OP-TEE OS 根据 UUID 查找并加载对应 TA。
- CA 通过
TEEC_InvokeCommand发送命令与参数,TA 在安全世界执行并返回结果。 - 业务完成后,CA 关闭会话并释放上下文。
下面是一段典型的 CA 调用代码片段:
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
uint32_t err;
err = TEEC_InitializeContext(NULL, &ctx);
err = TEEC_OpenSession(&ctx, &sess, &ta_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(
TEEC_MEMREF_TEMP_INPUT, TEEC_NONE,
TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = input;
op.params[0].tmpref.size = input_len;
err = TEEC_InvokeCommand(&sess, CMD_PROCESS, &op, NULL);
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
可以看到,CA 并不直接接触安全世界,它只与 libteec 打交道;libteec 再通过 ioctl 与内核驱动通信。这种分层设计让普通世界应用无需了解 SMC 指令、共享内存布局等底层细节。
三、tee-supplicant 的反向服务链路
TA 运行在安全世界,但安全世界通常没有完整的文件系统、网络协议栈或 RPMB 驱动。当 TA 需要加载镜像、读写安全存储或访问 RPMB 时,它会通过 OP-TEE OS 向普通世界发起 RPC 请求。此时内核驱动会通知 tee-supplicant,由 supplicant 完成实际的文件或 RPMB 操作,再把结果回传给安全世界。
这条链路可以概括为:TA → OP-TEE OS → 内核驱动 → tee-supplicant → 普通世界资源,返回时方向相反。它说明 tee-supplicant 不是可有可无的辅助进程,而是 TA 能否正常加载和运行的必要条件。如果 supplicant 未启动,很多 TA 会在打开会话阶段直接失败。
TEEC_ERROR_ITEM_NOT_FOUND 或 TA 加载失败,除了检查 TA 镜像是否存在,还应确认 tee-supplicant 是否已运行、其日志中是否有 RPC 请求记录。
四、各组件职责与交互关系一览
| 组件 | 运行位置 | 主要职责 | 与谁交互 |
|---|---|---|---|
| CA | 普通世界用户态 | 发起 TEE 业务请求 | libteec |
| libteec | 普通世界用户态 | 封装 TEE Client API 与 ioctl | CA、内核 TEE 驱动 |
| 内核 TEE 驱动 | 普通世界内核态 | 通信、共享内存、请求转发 | libteec、OP-TEE OS、tee-supplicant |
| tee-supplicant | 普通世界用户态 | TA 加载、RPMB、文件系统代理 | 内核 TEE 驱动 |
| OP-TEE OS | 安全世界 | TA 调度、安全内存、内部服务 | 内核驱动、TA |
| TA | 安全世界 | 执行具体安全业务 | OP-TEE OS、tee-supplicant(经 RPC) |
五、常见误区与复盘
第一个常见误区是把 tee-supplicant 当成“可选组件”。实际上,只要 TA 需要从普通世界加载镜像或访问安全存储,supplicant 就必须存在。第二个误区是认为 CA 可以直接调用 TA 函数。CA 与 TA 之间只有会话和命令,没有函数级链接,所有参数都必须通过 TEE 参数结构传递。第三个误区是忽略内核驱动的中枢作用:它既是 CA 请求的下行通道,也是 supplicant 服务的上行通道,任何一侧配置错误都会导致整体不可用。
把上述关系串起来,可以用一张图概括 OP-TEE 的完整调用链:普通世界的 CA 经 libteec 进入内核驱动,再通过 SMC 进入安全世界的 OP-TEE OS 并调用 TA;TA 在需要外部资源时,又经 OP-TEE OS 和内核驱动反向请求 tee-supplicant。这条双向链路,就是 OP-TEE 架构的总览。