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 发起 → libteec 封装 → 内核驱动转发 → SMC 陷入安全世界 → OP-TEE OS 调度 TA → TA 执行并返回 → 结果沿原路回传。

二、整体交互流程

下面这张图展示了从 CA 到 TA 的完整调用链路,注意普通世界与安全世界之间的边界由 SMC(Secure Monitor Call)跨越,而共享内存是双方交换大数据的主要通道。

普通世界(Normal World) CA 客户端应用 TEEC_* API 调用 OP-TEE Client libteec.so Linux 内核 TEE 驱动 /dev/tee0 · optee.ko 调用 ioctl 安全世界(Secure World) OP-TEE OS 调度 / 内存 / 会话管理 TA 可信应用 UUID 匹配 · 命令处理 共享内存 参数 / 大数据 调度 映射 SMC 陷入 返回结果

流程可以细化为以下步骤:

  1. CA 调用 TEEC_InitializeContext 打开 /dev/tee0,建立与 TEE 驱动的连接。
  2. CA 通过 TEEC_OpenSession 指定 TA 的 UUID,libteec 将其转为 ioctl 请求下发到内核驱动。
  3. 内核驱动通过 SMC 指令陷入安全世界,OP-TEE OS 根据 UUID 查找并加载对应 TA,建立会话上下文。
  4. CA 调用 TEEC_InvokeCommand 发起具体命令,参数通过共享内存或寄存器传递。
  5. TA 执行命令逻辑,访问安全资源或密码学服务,将结果写回共享内存。
  6. 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);

五、常见误区与复盘

误区一:认为 CA 可以直接访问 TA 的内存。实际上二者地址空间完全隔离,只能通过共享内存和参数传递数据。
误区二:把 OP-TEE Client 当成安全世界组件。libteec 运行在普通世界用户态,本身不提供任何安全保证。
误区三:忽略内核驱动的存在,直接调试 TA。若 /dev/tee0 未创建或驱动未加载,CA 连会话都无法建立。
排障建议:先确认 optee.ko 已加载且 /dev/tee0 存在;再用 tee-supplicant 确认普通世界辅助进程运行正常;最后检查 TA 的 UUID 与签名是否匹配。分层定位能显著缩短调试时间。

掌握这四个组件及其交互链路,是后续编写 CA/TA、配置安全存储、调试 SMC 通信的基础。只有清楚每一次调用跨越了哪些边界、经过了哪些接口,才能在出现问题时准确判断故障位于普通世界还是安全世界。