3、TEE接口分类:TEE内部API、TEE客户端API、TEE驱动接口、TEE日志接口
做TEE适配,说白了就是跟各种接口打交道。我刚开始接触TEE时,最头疼的就是分不清这些接口到底归谁管、在哪儿用。你想想看,一个安全应用从启动到销毁,中间要经过多少层调用?每一层都有自己专属的接口协议。
这一章,咱们就把TEE的四大接口家族捋清楚。搞懂了它们的分工,你写适配代码时就不会找错门了。
3.1 四类接口全景图
先看一张整体结构图,把四个接口的位置和关系印在脑子里。
这张图我每次做接口适配培训都会拿出来讲。你看,REE侧和TEE侧通过驱动接口和共享内存通信,而日志接口则像侦察兵一样,两边都能用。
3.2 TEE内部API——TA的生存工具
TEE内部API,也叫Trusted Core Framework API,是TA在安全世界里调用的基础函数库。说白了,TA想干任何事——分配内存、加解密数据、读写安全存储——都得通过这组API。
内部API按功能可以分成几个子集:
| 功能域 | 典型API | 用途 |
|---|---|---|
| 内存管理 | TEE_Malloc / TEE_Free / TEE_Realloc | TA内部堆内存分配与释放 |
| 密码学 | TEE_CipherInit / TEE_AEInit / TEE_DigestUpdate | 对称/非对称加密、哈希、MAC |
| 安全存储 | TEE_OpenPersistentObject / TEE_ReadObjectData | 持久化密钥和数据的安全存储 |
| 时间与随机数 | TEE_GetSystemTime / TEE_GenerateRandom | 获取安全时间、生成真随机数 |
| 属性与参数 | TEE_GetPropertyAsString / TEE_ParamInit | 获取TA配置属性、初始化调用参数 |
3.3 TEE客户端API——CA的通信桥梁
客户端API是REE侧CA与TEE侧TA通信的唯一官方通道。CA不能直接调用TA的函数,必须通过客户端API建立会话、传递命令、交换数据。
客户端API的核心流程就四个步骤:
- 初始化上下文:TEEC_InitializeContext — 建立与TEE驱动的连接
- 打开会话:TEEC_OpenSession — 加载并连接指定TA
- 调用命令:TEEC_InvokeCommand — 向TA发送具体操作指令
- 关闭会话:TEEC_CloseSession — 断开与TA的连接
这里有个容易忽略的细节——共享内存。CA和TA交换数据时,不能直接传指针,因为两边是隔离的地址空间。必须通过TEEC_RegisterSharedMemory或TEEC_AllocateSharedMemory申请一块双方都能访问的内存。
// 客户端API典型调用示例
TEEC_Context ctx;
TEEC_Session session;
TEEC_Operation op;
TEEC_Result res;
// 1. 初始化上下文
res = TEEC_InitializeContext(NULL, &ctx);
if (res != TEEC_SUCCESS) {
// 我习惯在这里打印错误码,方便定位驱动层问题
printf("TEEC_InitializeContext failed: 0x%x\n", res);
return -1;
}
// 2. 打开会话
res = TEEC_OpenSession(&ctx, &session, &ta_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
if (res != TEEC_SUCCESS) {
TEEC_FinalizeContext(&ctx);
return -1;
}
// 3. 准备参数并调用命令
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_VALUE_OUTPUT,
TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = input_data;
op.params[0].tmpref.size = input_len;
res = TEEC_InvokeCommand(&session, CMD_ENCRYPT, &op, NULL);
if (res != TEEC_SUCCESS) {
// 这里可以检查op.params[1].value.a获取TA返回的错误码
printf("InvokeCommand failed: 0x%x, TA error: 0x%x\n",
res, op.params[1].value.a);
}
// 4. 清理
TEEC_CloseSession(&session);
TEEC_FinalizeContext(&ctx);
3.4 TEE驱动接口——内核态的中转站
驱动接口是REE侧内核空间与TEE固件之间的通信层。它负责处理SMC(安全监控调用)的发起、共享内存的管理、中断的路由等底层操作。
不同的TEE实现,驱动接口的形态也不一样:
| TEE方案 | 驱动接口形式 | 特点 |
|---|---|---|
| OP-TEE | Linux TEE子系统驱动(drivers/tee/) | 标准化,支持多平台 |
| QSEE(高通) | qseecom驱动 | 高通私有接口,功能丰富 |
| Kinibi(Trustonic) | McDriver / McUser | 用户态+内核态双层驱动 |
| CA(华为) | hisee驱动 | 华为自研TEE驱动 |
驱动接口的适配工作,说白了就是让Linux内核能正确识别TEE硬件、建立共享内存、处理SMC异常。我做过一次从OP-TEE 3.8迁移到3.12的驱动适配,最麻烦的不是API变化,而是设备树中内存预留区域的调整——改错一个地址,整个TEE就起不来。
3.5 TEE日志接口——调试的第三只眼
日志接口是TEE调试中最容易被忽视、但关键时刻能救命的东西。TA运行在安全世界,printf不能用,gdb连不上,唯一的观察窗口就是日志。
不同TEE方案的日志接口差异很大:
| TEE方案 | 日志宏/函数 | 输出目标 |
|---|---|---|
| OP-TEE | EMSG / DMSG / IMSG / FMSG | UART串口 / 共享内存缓冲区 |
| QSEE | LOG_INFO / LOG_ERROR / LOG_DEBUG | DIAG端口 / 内存日志缓冲区 |
| Kinibi | tlApiLogPrint / tlDbgPrint | McDriver日志通道 |
OP-TEE的日志分级我特别喜欢,它用三个级别帮你快速过滤信息:
- EMSG (Error):严重错误,必须修复。比如内存越界、会话认证失败。
- IMSG (Info):关键流程信息。比如TA加载成功、会话建立。
- DMSG (Debug):调试细节。比如函数入口参数、中间计算结果。
// OP-TEE TA中的日志使用示例
#include <tee_internal_api_extensions.h>
TEE_Result TA_InvokeCommandEntryPoint(void *sessionContext,
uint32_t cmdID,
uint32_t paramTypes,
TEE_Param params[4])
{
// 入口日志,我习惯记录命令ID和参数类型
IMSG("TA_InvokeCommandEntryPoint: cmdID=%u, paramTypes=0x%x",
cmdID, paramTypes);
// 参数校验失败时打错误日志
if (paramTypes != EXPECTED_TYPES) {
EMSG("Invalid paramTypes: expected 0x%x, got 0x%x",
EXPECTED_TYPES, paramTypes);
return TEE_ERROR_BAD_PARAMETERS;
}
// 调试时打印输入数据长度
DMSG("Input buffer size: %u", params[0].memref.size);
// ... 业务逻辑 ...
IMSG("Command %u completed successfully", cmdID);
return TEE_SUCCESS;
}
3.6 四类接口的协作关系
最后,用一个表格总结四类接口的协作关系,方便你对照查阅:
| 接口类型 | 调用方 | 运行环境 | 典型场景 | 调试手段 |
|---|---|---|---|---|
| TEE内部API | TA | TEE安全世界 | 加解密、安全存储、内存分配 | DMSG/IMSG日志 |
| TEE客户端API | CA | REE普通世界 | 打开会话、调用TA命令 | 返回值检查、共享内存dump |
| TEE驱动接口 | Linux内核 | REE内核空间 | SMC调用、共享内存管理 | dmesg、ftrace、设备节点检查 |
| TEE日志接口 | TA / TEE固件 | TEE安全世界 | 调试信息输出、错误上报 | UART串口、日志缓冲区读取 |
嗯,四类接口的脉络到这里就清楚了。你在做接口适配时,遇到问题先问自己三个问题:这个接口是谁调用的?它运行在哪个世界?它的日志从哪里看?想明白这三件事,80%的适配问题都能定位到正确的方向。