当前位置: 首页 > news >正文

深入解析OP-TEE的libteec核心API实现

在 OP-TEE 体系中,libteec是运行在 REE 用户态的客户端库。它完整实现了 GlobalPlatform (GP) 标准的 TEE Client API。libteec的核心文件是src/tee_client_api.c。它的本质工作非常纯粹:将上层面向对象的 GP API 转换为对 Linux 内核 TEE 驱动(/dev/tee0)的ioctl级联调用
以下梳理其最核心的三个 API 的代码实现骨架、关键数据结构及参数序列化逻辑。

一、TEEC_InitializeContext:初始化上下文与建立通道

该函数负责打开 TEE 驱动的字符设备文件,并获取当前 TEE 的基本版本信息与软硬件能力(Capabilities)。C
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx) { char devname[PATH_MAX] = { 0 }; int fd = 0; size_t n = 0; if (!ctx) return TEEC_ERROR_BAD_PARAMETERS; for (n = 0; n < TEEC_MAX_DEV_SEQ; n++) { uint32_t gen_caps = 0; snprintf(devname, sizeof(devname), "/dev/tee%zu", n); fd = teec_open_dev(devname, name, &gen_caps); if (fd >= 0) { ctx->imp.fd = fd; ctx->imp.reg_mem = gen_caps & TEE_GEN_CAP_REG_MEM; ctx->imp.memref_null = gen_caps & TEE_GEN_CAP_MEMREF_NULL; return TEEC_SUCCESS; } } return TEEC_ERROR_ITEM_NOT_FOUND; }

二、TEEC_OpenSession:开启与指定 TA 的安全会话

在调用具体的 TA 业务前,需要通过 UUID 寻址来拉起目标 TA。此时,libteec会把 GP 的TEEC_Operation结构参数打包转换为内核识别的tee_ioctl_paramC
TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session, const TEEC_UUID *destination, uint32_t connection_method, const void *connection_data, TEEC_Operation *operation, uint32_t *ret_origin) { struct tee_ioctl_open_session_arg *arg = NULL; struct tee_ioctl_param *params = NULL; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eorig = 0; int rc = 0; const size_t arg_size = sizeof(struct tee_ioctl_open_session_arg) + TEEC_CONFIG_PAYLOAD_REF_COUNT * sizeof(struct tee_ioctl_param); union { struct tee_ioctl_open_session_arg arg; uint8_t data[arg_size]; } buf; struct tee_ioctl_buf_data buf_data; TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT]; memset(&buf, 0, sizeof(buf)); memset(&shm, 0, sizeof(shm)); memset(&buf_data, 0, sizeof(buf_data)); if (!ctx || !session) { eorig = TEEC_ORIGIN_API; res = TEEC_ERROR_BAD_PARAMETERS; goto out; } buf_data.buf_ptr = (uintptr_t)&buf; buf_data.buf_len = sizeof(buf); arg = &buf.arg; arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; params = (struct tee_ioctl_param *)(arg + 1); uuid_to_octets(arg->uuid, destination); setup_client_data(arg, connection_method, connection_data); res = teec_pre_process_operation(ctx, operation, params, shm); if (res != TEEC_SUCCESS) { eorig = TEEC_ORIGIN_API; goto out_free_temp_refs; } rc = ioctl(ctx->imp.fd, TEE_IOC_OPEN_SESSION, &buf_data); if (rc) { EMSG("TEE_IOC_OPEN_SESSION failed"); eorig = TEEC_ORIGIN_COMMS; res = ioctl_errno_to_res(errno); goto out_free_temp_refs; } res = arg->ret; eorig = arg->ret_origin; if (res == TEEC_SUCCESS) { session->imp.ctx = ctx; session->imp.session_id = arg->session; } teec_post_process_operation(operation, params, shm); out_free_temp_refs: teec_free_temp_refs(operation, shm); out: if (ret_origin) *ret_origin = eorig; return res; }

三、TEEC_InvokeCommand:核心业务指令的下发与数据交互

这是日常业务中最频繁调用的 API。由于它专注于执行指令,不需要像OpenSession那样搬运 UUID,所以它的逻辑更轻量,直奔TEE_IOC_INVOKEC
TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, uint32_t cmd_id, TEEC_Operation *operation, uint32_t *error_origin) { struct tee_ioctl_invoke_arg *arg = NULL; struct tee_ioctl_param *params = NULL; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eorig = 0; int rc = 0; /* 1. 装填 Session ID 与要执行的命令号 */ const size_t arg_size = sizeof(struct tee_ioctl_invoke_arg) + TEEC_CONFIG_PAYLOAD_REF_COUNT * sizeof(struct tee_ioctl_param); union { struct tee_ioctl_invoke_arg arg; uint8_t data[arg_size]; } buf; struct tee_ioctl_buf_data buf_data; TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT]; memset(&buf, 0, sizeof(buf)); memset(&buf_data, 0, sizeof(buf_data)); memset(&shm, 0, sizeof(shm)); if (!session) { eorig = TEEC_ORIGIN_API; res = TEEC_ERROR_BAD_PARAMETERS; goto out; } buf_data.buf_ptr = (uintptr_t)&buf; buf_data.buf_len = sizeof(buf); arg = &buf.arg; arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; params = (struct tee_ioctl_param *)(arg + 1); arg->session = session->imp.session_id; arg->func = cmd_id; if (operation) { teec_mutex_lock(&teec_mutex); operation->imp.session = session; teec_mutex_unlock(&teec_mutex); } /* 2. 序列化参数 (TEEC_Operation -> tee_ioctl_param) */ res = teec_pre_process_operation(session->imp.ctx, operation, params, shm); if (res != TEEC_SUCCESS) { eorig = TEEC_ORIGIN_API; goto out_free_temp_refs; } /* 3. 发送关键 ioctl 陷落至内核,阻塞等待 Secure 世界(TA)处理完毕返回 */ rc = ioctl(session->imp.ctx->imp.fd, TEE_IOC_INVOKE, &buf_data); if (rc) { EMSG("TEE_IOC_INVOKE failed"); eorig = TEEC_ORIGIN_COMMS; res = ioctl_errno_to_res(errno); goto out_free_temp_refs; } res = arg->ret; eorig = arg->ret_origin; teec_post_process_operation(operation, params, shm); /* 4. 反序列化与收尾:将 TA 修改后的 Value 或 Memref 刷新回用户态 CA 内存空间 */ out_free_temp_refs: teec_free_temp_refs(operation, shm); out: if (error_origin) *error_origin = eorig; return res; }

四、 核心桥梁:参数的预处理与内存共享机制

深入细读libteec,最复杂的逻辑其实隐藏在teec_pre_process_operation内部。GP 标准规定每次调用最多传递 4 个参数(TEEC_Parameter params[4]),类型可以是VALUE(纯数值)或MEMREF(内存引用)。
static TEEC_Result teec_pre_process_operation(TEEC_Context *ctx, TEEC_Operation *operation, struct tee_ioctl_param *params, TEEC_SharedMemory *shms) { TEEC_Result res = TEEC_ERROR_GENERIC; size_t n = 0; memset(shms, 0, sizeof(TEEC_SharedMemory) * TEEC_CONFIG_PAYLOAD_REF_COUNT); for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) shms[n].imp.id = -1; if (!operation) { memset(params, 0, sizeof(struct tee_ioctl_param) * TEEC_CONFIG_PAYLOAD_REF_COUNT); return TEEC_SUCCESS; } for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) { uint32_t param_type = 0; param_type = TEEC_PARAM_TYPE_GET(operation->paramTypes, n); switch (param_type) { case TEEC_NONE: params[n].attr = param_type; break; case TEEC_VALUE_INPUT: case TEEC_VALUE_OUTPUT: case TEEC_VALUE_INOUT: params[n].attr = param_type; params[n].a = operation->params[n].value.a; params[n].b = operation->params[n].value.b; break; case TEEC_MEMREF_TEMP_INPUT: case TEEC_MEMREF_TEMP_OUTPUT: case TEEC_MEMREF_TEMP_INOUT: res = teec_pre_process_tmpref(ctx, param_type, &operation->params[n].tmpref, params + n, shms + n); if (res != TEEC_SUCCESS) return res; break; case TEEC_MEMREF_WHOLE: res = teec_pre_process_whole( &operation->params[n].memref, params + n); if (res != TEEC_SUCCESS) return res; break; case TEEC_MEMREF_PARTIAL_INPUT: case TEEC_MEMREF_PARTIAL_OUTPUT: case TEEC_MEMREF_PARTIAL_INOUT: res = teec_pre_process_partial(param_type, &operation->params[n].memref, params + n); if (res != TEEC_SUCCESS) return res; break; default: return TEEC_ERROR_BAD_PARAMETERS; } } return TEEC_SUCCESS; }
libteec中,它们会映射到内核驱动的struct tee_ioctl_param,底层转换逻辑如下:

1. 值的映射 (TEEC_VALUE_INPUT / OUTPUT / INOUT)

最直观,不涉及复杂的内存映射,直接做拷贝。
  • params[n].a = operation->params[n].value.a;
  • params[n].b = operation->params[n].value.b;

2. 内存引用的映射 (TEEC_MEMREF_TEMP_*/TEEC_REGISTERED_MEM_*)

这是性能与安全的焦点。如果 CA 传入的是一块普通的 Buffer 内存(TEEC_MEMREF_TEMP_INPUT),由于非安全世界用户态的虚拟地址(VA)安全世界根本无法直接访问,libteec会执行以下高开销操作:
  1. 申请共享内存:通过ioctl(fd, TEE_IOC_SHM_ALLOC, &shm_alloc_arg)向内核 TEE 驱动申请一段专用的、连通非安全与安全世界的物理连续/非连续共享内存(Shared Memory)。
  2. 内容拷贝:如果涉及输入(Input),使用memcpy将用户态临时 Buffer 的内容拷贝到这段共享内存中。
  3. 记录标识:将分配到的shm_num(共享内存 ID)和offset填入tee_ioctl_param.ctee_ioctl_param.a中。
  4. 回写与销毁:在teec_post_process_operation中,如果是 Output,再把数据从共享内存拷回给 CA 的临时 Buffer,并调用TEE_IOC_SHM_FREE释放共享内存。
http://www.jsqmd.com/news/1021140/

相关文章:

  • 凯撒旅业如何全方位赋能凯撒易食发展 - 品牌2026
  • 软考软件设计师备考全攻略:从核心能力到实战技巧
  • 二维二分算法:从有序矩阵搜索到四叉树实战指南
  • Codex本地代码助手安装与使用全指南
  • 从QObject到QWidget:图解Qt父子关系内存管理,告别野指针和泄漏
  • 2026年中小企业如何选代理记账机构?全国14家主流服务商横向分析报告 - 优质品牌商家
  • Nexior:基于Vercel+Docker的AI平台工程化脚手架
  • 从‘通不了信’到‘秒懂原因’:图解CAN总线7种经典故障的波形与电压特征(含LIN对比)
  • claude code(十一):【企业级应用实战】案例二:会议中的高效编码
  • 基于Windows内核驱动派遣函数HOOK的硬件指纹伪装技术实现方案
  • Livox MID-360与FAST-LIO2实战:从驱动部署到参数调优的完整指南
  • Llama-2硬件选型实战指南:从7B到70B的显存、算力与系统协同真相
  • 2026年质量好的食堂厨房设备/厨房设备/东莞厨房设备公司选择指南 - 行业平台推荐
  • R语言箱线图深度解析:从统计原理到业务决策
  • 算法复杂度分析完全指南:从入门到精通时间复杂度与空间复杂度
  • 为什么有些中文国际期刊没有影响因子?
  • 别再死记硬背了!用这10个Qt面试题实战场景,帮你真正理解面试官想问什么
  • Snowflake Time Travel 原理与实战:数据回溯、恢复与克隆全指南
  • 2026年评价高的浙江重卡干燥器/干燥筒公司选择指南 - 行业平台推荐
  • Claude Code技能开发:Skills+HTTP服务架构实战指南
  • 【爬虫实战】Instagram博主图片爬取:模拟登录+滚动加载,轻松抓取高清美图
  • 睿抗机器人开发者大赛:从ROS到Jetson的完整技术栈与实战指南
  • Meshery:开源云原生管理器,助力多场景部署与性能管理!
  • LIME局部解释原理与实战:让黑盒模型决策可读可用
  • 从QObject到QWidget:一份给Qt新手的避坑指南,帮你理清那些容易混淆的核心概念
  • Klipper固件配置完全指南:3D打印性能飞跃的终极方案
  • 网盘下载太慢?试试这款免费直链解析工具,支持9大平台
  • Windows原生部署vLLM实战指南:绕过WSL2直编CUDA内核
  • 用Python玩转扑克牌:构建可迁移的概率直觉
  • 软考高项论文别再怕!手把手教你用WBS和关键路径搞定进度管理(附真实范文拆解)