4. OP-TEE系统架构
要真正理解 OP-TEE 的开发方式,不能只记住“安全世界”和“普通世界”这两个词,而必须把整个系统拆成四个关键角色:运行在安全世界的 OP-TEE OS、运行在普通用户态的 OP-TEE Client(libteec)、位于 Linux 内核中的 TEE 驱动,以及成对出现的 CA(Client Application) 与 TA(Trusted Application)。它们分别处于不同的特权级和地址空间,通过标准接口层层传递请求,最终完成一次可信服务调用。
一、四个核心组件的职责划分
OP-TEE 的整体架构可以按“普通世界”和“安全世界”两条主线来理解。普通世界包含用户态的 CA 与 OP-TEE Client 库,以及内核态的 TEE 驱动;安全世界则运行 OP-TEE OS 和它加载的 TA。
- OP-TEE OS:安全世界的“小操作系统”,负责安全内存管理、TA 的加载与调度、SMC 异常处理、密码学原语、会话与共享内存管理。它运行在 ARM 的 EL3/EL1 安全态(依平台而定),是整个可信执行环境的根基。
- OP-TEE Client(libteec):普通世界用户态库,向 CA 提供
TEEC_InitializeContext、TEEC_OpenSession、TEEC_InvokeCommand等标准 API,并把请求封装成 ioctl 交给内核驱动。 - Linux 内核 TEE 驱动:通常是
optee.ko,提供/dev/tee0与/dev/teepriv0字符设备,负责用户态与安全世界之间的数据搬运、共享内存注册、SMC 调用触发以及并发与权限控制。 - CA 与 TA:CA 是普通世界的调用方,TA 是安全世界中被调用的服务实体。二者通过 UUID 匹配,以“会话 + 命令”的方式通信,TA 内部逻辑与数据对普通世界不可见。
二、整体交互流程
下面这张图展示了从 CA 到 TA 的完整调用链路,注意普通世界与安全世界之间的边界由 SMC(Secure Monitor Call)跨越,而共享内存是双方交换大数据的主要通道。
流程可以细化为以下步骤:
- CA 调用
TEEC_InitializeContext打开/dev/tee0,建立与 TEE 驱动的连接。 - CA 通过
TEEC_OpenSession指定 TA 的 UUID,libteec 将其转为 ioctl 请求下发到内核驱动。 - 内核驱动通过 SMC 指令陷入安全世界,OP-TEE OS 根据 UUID 查找并加载对应 TA,建立会话上下文。
- CA 调用
TEEC_InvokeCommand发起具体命令,参数通过共享内存或寄存器传递。 - TA 执行命令逻辑,访问安全资源或密码学服务,将结果写回共享内存。
- OP-TEE OS 返回,内核驱动唤醒 CA,libteec 解析返回值,CA 获得结果。
三、关键接口与参数对照
理解各层之间的接口,有助于排障时快速定位问题出在哪一层。下表列出了主要交互点及其对应关系。
| 层次 | 典型接口 / 对象 | 作用 |
|---|---|---|
| CA 层 | TEEC_OpenSession / TEEC_InvokeCommand |
发起会话与命令调用 |
| Client 库 | libteec.so |
API 封装、ioctl 转换 |
| 内核驱动 | /dev/tee0、optee.ko |
数据搬运、SMC 触发 |
| OP-TEE OS | 会话管理、TA 加载器 | 调度 TA、管理安全内存 |
| TA 层 | TA_InvokeCommandEntryPoint |
执行具体可信逻辑 |
四、案例场景:一次安全存储写入
假设 CA 需要把一段密钥写入安全存储。CA 调用 TEEC_OpenSession 打开安全存储 TA,随后调用 TEEC_InvokeCommand 并传入命令 ID 与数据缓冲区。libteec 将缓冲区注册为共享内存,内核驱动把物理地址和命令信息通过 SMC 传给 OP-TEE OS。OP-TEE OS 调度 TA,TA 调用内部安全存储接口完成加密写入,最后把执行状态返回。整个过程普通世界只能看到“成功/失败”,无法读取密钥明文。
TEEC_Result res;
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
res = TEEC_InitializeContext(NULL, &ctx);
res = TEEC_OpenSession(&ctx, &sess, &ta_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
memset(&op, 0, sizeof(op));
op.params[0].tmpref.buffer = key_buf;
op.params[0].tmpref.size = key_len;
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_NONE, TEEC_NONE, TEEC_NONE);
res = TEEC_InvokeCommand(&sess, CMD_WRITE_KEY, &op, NULL);
五、常见误区与复盘
误区二:把 OP-TEE Client 当成安全世界组件。libteec 运行在普通世界用户态,本身不提供任何安全保证。
误区三:忽略内核驱动的存在,直接调试 TA。若
/dev/tee0 未创建或驱动未加载,CA 连会话都无法建立。
optee.ko 已加载且 /dev/tee0 存在;再用 tee-supplicant 确认普通世界辅助进程运行正常;最后检查 TA 的 UUID 与签名是否匹配。分层定位能显著缩短调试时间。
掌握这四个组件及其交互链路,是后续编写 CA/TA、配置安全存储、调试 SMC 通信的基础。只有清楚每一次调用跨越了哪些边界、经过了哪些接口,才能在出现问题时准确判断故障位于普通世界还是安全世界。