3. OP-TEE Client概述

在 OP-TEE 的整体软件栈中,OP-TEE Client 是普通世界(Normal World,即 Linux 用户态与内核态)与安全世界(Secure World,即 OP-TEE OS 与 TA)之间的“桥梁层”。它并不直接执行可信应用(TA)的逻辑,而是负责把普通世界应用的请求,通过标准化的接口和驱动通道,安全地传递到安全世界,并把结果带回。理解 OP-TEE Client 的位置、组成与通信模型,是后续编写 CA/TA 程序、排查通信故障的基础。

一、OP-TEE Client 在系统中的位置

从系统分层看,OP-TEE 的完整链路自上而下大致为:普通世界用户态应用(CA)→ libteec → tee-supplicant → Linux 内核 TEE 驱动 → SMC/HVC 指令 → OP-TEE OS → TA。其中,libteec 与 tee-supplicant 共同构成 OP-TEE Client 的主体,它们都运行在普通世界,但职责截然不同。

libteec 是一个用户态库,通常以 libteec.so 形式提供,被 CA 直接链接调用。它封装了打开 TEE 设备、建立会话、调用 TA 命令、共享内存注册等操作,把复杂的 ioctl 细节隐藏起来。tee-supplicant 则是一个常驻后台的守护进程,它代表安全世界去访问普通世界的文件系统、RPMB、网络等资源。当 TA 需要加载动态库、读写安全存储文件或访问 RPMB 时,OP-TEE OS 会通过 RPC 请求“回到”普通世界,由 tee-supplicant 完成实际 I/O。

关键点:libteec 面向“调用者”,负责把 CA 的请求送进安全世界;tee-supplicant 面向“被调用者”,负责替 TA 完成普通世界侧的资源访问。二者一进一出,共同支撑起完整的 TEE 客户端能力。
普通世界 Normal World(Linux) CA 客户端应用 调用 TEEC_* API libteec.so 会话/命令/共享内存 tee-supplicant 文件/RPMB/动态库 Linux 内核 TEE 驱动 tee-supplicant 通过 ioctl 与内核 TEE 驱动交互,响应来自安全世界的 RPC 请求 libteec 通过 /dev/tee0 等设备节点下发会话与命令 安全世界 Secure World(OP-TEE OS) OP-TEE OS SMC/HVC 入口 TA 可信应用 执行安全逻辑 安全存储 / RPMB 受保护资源 SMC 进入安全世界 RPC 请求回普通世界

二、libteec 与 tee-supplicant 的职责划分

libteec 的核心职责可以概括为“会话管理 + 命令下发 + 内存共享”。它提供 TEEC_InitializeContext、TEEC_OpenSession、TEEC_InvokeCommand、TEEC_CloseSession、TEEC_FinalizeContext 等 API,并负责把参数封装成 OP-TEE 驱动可识别的结构。CA 只需关注 TA 的 UUID、命令 ID 和参数,不必关心底层 ioctl 的细节。

tee-supplicant 的职责则偏向“资源代理”。它不参与 CA 的直接调用,而是被动响应安全世界的 RPC。典型场景包括:TA 通过 TEE_LoadTa 加载动态库时,tee-supplicant 从文件系统读取 .ta 文件;TA 使用安全存储时,tee-supplicant 负责在 /data/tee/ 下读写加密文件;TA 访问 RPMB 时,tee-supplicant 通过内核 RPMB 驱动完成读写。没有 tee-supplicant,很多 TA 功能会因资源不可达而失败。

对比项 libteec tee-supplicant
运行形态 用户态动态库,被 CA 链接 常驻后台守护进程
调用方向 CA 主动调用,进入安全世界 安全世界 RPC 触发,回到普通世界
核心接口 TEEC_OpenSession / TEEC_InvokeCommand 文件、RPMB、动态库加载等 RPC 服务
典型设备 /dev/tee0、/dev/teepriv0 /dev/teepriv0、/dev/mmcblk*rpmb
缺失后果 CA 无法建立会话或调用命令 TA 无法加载库、读写安全存储或 RPMB

三、客户端与 TA 的通信模型

CA 与 TA 的通信遵循“上下文—会话—命令”三级模型。首先,CA 调用 TEEC_InitializeContext 打开 TEE 设备,获得一个上下文;然后通过 TEEC_OpenSession 指定 TA 的 UUID,与目标 TA 建立会话;最后通过 TEEC_InvokeCommand 携带命令 ID 和参数调用 TA 的具体命令。TA 侧则通过 TA_InvokeCommandEntryPoint 接收命令并返回结果。

TEEC_Context ctx;
TEEC_Session sess;
TEEC_InitializeContext(NULL, &ctx);

TEEC_UUID uuid = TA_HELLO_WORLD_UUID;
TEEC_OpenSession(&ctx, &sess, &uuid,
                 TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);

uint32_t origin;
TEEC_Parameter param;
param.value.a = 0x1234;
param.value.b = 0x5678;
TEEC_InvokeCommand(&sess, TA_HELLO_WORLD_CMD_INC_VALUE,
                   &param, &origin);

TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);

参数传递有四种类型:TEEC_VALUE_INPUT、TEEC_VALUE_OUTPUT、TEEC_MEMREF_TEMP_INPUT、TEEC_MEMREF_TEMP_OUTPUT 等。小数据可直接用 value 传递,大数据则通过共享内存(memref)传递。共享内存需要注册,确保安全世界与普通世界看到的是同一块物理内存,避免拷贝带来的性能损耗与安全风险。

通信模型可以记为:Context 是“通道”,Session 是“连接”,Command 是“消息”。一个 Context 可承载多个 Session,一个 Session 可多次 InvokeCommand,但 Session 与特定 TA 绑定。

四、典型场景与常见误区

以一个“安全计数”TA 为例:CA 打开会话后,循环调用 TA_INC_VALUE 命令,TA 在安全世界内维护计数器并返回新值。若 CA 忘记调用 TEEC_CloseSession,会话资源不会立即释放,长期运行可能耗尽会话槽位。若 tee-supplicant 未启动,TA 在首次访问安全存储时会阻塞在 RPC 上,表现为 CA 调用超时或返回 TEEC_ERROR_COMMUNICATION。

常见误区有三类:其一,把 libteec 与 tee-supplicant 混为一谈,认为只要链接了 libteec 就能访问文件系统;其二,忽略共享内存的生命周期,在 TEEC_InvokeCommand 返回前就释放缓冲区;其三,误以为 CA 可以直接调用 TA 内部函数,实际上所有交互都必须经过命令 ID 和参数结构。排查时,可先确认 /dev/tee0 是否存在、tee-supplicant 是否运行,再检查 UUID 与命令 ID 是否匹配,最后用 strace 观察 ioctl 调用是否成功。

安全通信的前提是“最小暴露”:CA 只传递必要参数,TA 只返回必要结果;共享内存使用后应及时释放,避免敏感数据在普通世界长期驻留。

综上,OP-TEE Client 位于普通世界与安全世界的交界处,libteec 负责“请求下行”,tee-supplicant 负责“资源上行”,二者配合构成 CA 与 TA 之间的完整通信链路。掌握这一模型,才能在后续的 TA 开发、安全存储与 RPMB 调试中快速定位问题。