TEE-TA学习轨迹第九篇:libteec代码详细分析

TEE-TA学习轨迹第九篇:libteec代码详细分析
libteec代码详细分析libteec.so是TEETrusted Execution Environment可信执行环境客户端API的核心实现遵循GlobalPlatformGPTEE标准主要用于Linux用户空间REE富执行环境与安全世界的TEE如OP-TEE进行通信提供会话管理、共享内存操作、命令调用等核心能力。一、代码整体背景1. 版权与许可代码由Linaro维护基于BSD许可开源适配OP-TEEOpen Portable TEE是连接普通应用与TEE安全服务的关键层。2. 核心依赖系统调用fcntl文件控制、ioctl设备控制、mmap内存映射、pthread线程安全等TEE标准头文件tee_client_api.hGP TEE客户端API定义、linux/tee.hLinux内核TEE驱动接口调试/日志teec_trace.hTEEC日志宏如EMSG。二、关键宏与常量定义宏/常量用途__aligned(x)封装编译器的内存对齐属性__attribute__((aligned(x)))MIN(x,y)取两个值的最小值TEEC_MAX_DEV_SEQ尝试打开TEE设备文件的最大序列遍历/dev/tee0到/dev/tee9MEMREF_SHM_ID/Offs/Size操作struct tee_ioctl_param中内存引用的SHM ID、偏移、大小字段SHM_FLAG_*共享内存内部标志BUFFER_ALLOCED缓冲区已分配SHADOW_BUFFER_ALLOCED影子缓冲区已分配三、静态辅助函数工具层1. 线程安全锁封装static void teec_mutex_lock(pthread_mutex_t *mu) { pthread_mutex_lock(mu); } static void teec_mutex_unlock(pthread_mutex_t *mu) { pthread_mutex_unlock(mu); }封装pthread互斥锁的加/解锁保证多线程访问TEE资源时的安全性。2. 内存分配页对齐分配static void *teec_paged_aligned_alloc(size_t sz) { void *p NULL; size_t page_sz sysconf(_SC_PAGESIZE); // 获取系统页大小 size_t aligned_sz ((sz page_sz - 1) / page_sz) * page_sz; // 向上取整到页大小 if (aligned_sz sz !posix_memalign(p, page_sz, aligned_sz)) return p; return NULL; }按系统页大小对齐分配内存TEE共享内存要求页对齐避免内存对齐导致的访问错误。3. 设备打开验证TEE兼容性static int teec_open_dev(const char *devname, const char *capabilities, uint32_t *gen_caps) { int fd 0; struct tee_ioctl_version_data vers; memset(vers, 0, sizeof(vers)); fd open(devname, O_RDWR); if (fd 0) return -1; if (ioctl(fd, TEE_IOC_VERSION, vers)) { EMSG(TEE_IOC_VERSION failed); goto err; } /* We can only handle GP TEEs */ if (!(vers.gen_caps TEE_GEN_CAP_GP)) goto err; if (capabilities) { if (strcmp(capabilities, optee-tz) 0) { if (vers.impl_id ! TEE_IMPL_ID_OPTEE) goto err; if (!(vers.impl_caps TEE_OPTEE_CAP_TZ)) goto err; } else { /* Unrecognized capability requested */ goto err; } } *gen_caps vers.gen_caps; return fd; err: close(fd); return -1; }打开TEE设备文件如/dev/tee0通过TEE_IOC_VERSION ioctl验证必须是GPGlobalPlatform兼容的TEE若指定capabilitiesoptee-tz需验证是OP-TEE实现且支持TZTrusted Zone返回设备FD和TEE通用能力gen_caps。4. 共享内存分配/注册static int teec_shm_alloc(int fd, size_t size, int *id); // 向TEE申请分配共享内存 static intstatic int teec_shm_alloc(int fd, size_t size, int *id) { int shm_fd 0; struct tee_ioctl_shm_alloc_data data; memset(data, 0, sizeof(data)); data.size size; shm_fd ioctl(fd, TEE_IOC_SHM_ALLOC, data); if (shm_fd 0) return -1; *id data.id; return shm_fd; }teec_shm_register(int fd, void *buf, size_t size, int *id); // 注册用户内存到TEEstatic int teec_shm_register(int fd, void *buf, size_t size, int *id) { int shm_fd 0; struct tee_ioctl_shm_register_data data; memset(data, 0, sizeof(data)); data.addr (uintptr_t)buf; data.length size; shm_fd ioctl(fd, TEE_IOC_SHM_REGISTER, data); if (shm_fd 0) return -1; *id data.id; return shm_fd; }通过TEE_IOC_SHM_ALLOC/TEE_IOC_SHM_REGISTER ioctl与TEE内核交互返回共享内存FD和ID。四、核心API实现GP标准接口1. 上下文管理初始化/销毁TEEC_InitializeContextTEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)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; }功能初始化TEE上下文与TEE设备建立连接逻辑遍历/dev/tee0到/dev/tee9调用teec_open_dev打开兼容的TEE设备记录设备FD、TEE能力如是否支持注册内存、空内存引用失败返回TEEC_ERROR_ITEM_NOT_FOUND无可用TEE设备。TEEC_FinalizeContextvoid TEEC_FinalizeContext(TEEC_Context *ctx)void TEEC_FinalizeContext(TEEC_Context *ctx) { if (ctx) close(ctx-imp.fd); }功能销毁TEE上下文关闭设备FD。2. 共享内存管理TEEC_AllocateSharedMemoryTEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm) { int fd 0; size_t s 0; if (!ctx || !shm) return TEEC_ERROR_BAD_PARAMETERS; if (!shm-flags || (shm-flags ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))) return TEEC_ERROR_BAD_PARAMETERS; s shm-size; if (!s) s 8; if (ctx-imp.reg_mem) { shm-buffer teec_paged_aligned_alloc(s); if (!shm-buffer) return TEEC_ERROR_OUT_OF_MEMORY; fd teec_shm_register(ctx-imp.fd, shm-buffer, s, shm-imp.id); if (fd 0) { free(shm-buffer); shm-buffer NULL; return TEEC_ERROR_OUT_OF_MEMORY; } shm-imp.registered_fd fd; } else { fd teec_shm_alloc(ctx-imp.fd, s, shm-imp.id); if (fd 0) return TEEC_ERROR_OUT_OF_MEMORY; shm-buffer mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (shm-buffer (void *)MAP_FAILED) { shm-imp.id -1; return TEEC_ERROR_OUT_OF_MEMORY; } shm-imp.registered_fd -1; } shm-imp.shadow_buffer NULL; shm-imp.alloced_size s; shm-imp.flags SHM_FLAG_BUFFER_ALLOCED; return TEEC_SUCCESS; }功能由TEE内核分配共享内存而非用户提供逻辑按页对齐分配内存注册到TEE若TEE不支持注册内存通过mmap映射TEE分配的内存到用户空间。TEEC_ReleaseSharedMemoryvoid TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shm)功能释放共享内存清理影子缓冲区/映射内存关闭FD重置元信息。TEEC_RegisterSharedMemoryTEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm) { TEEC_Result res TEEC_SUCCESS; int fd 0; size_t s 0; if (!ctx || !shm) return TEEC_ERROR_BAD_PARAMETERS; if (!shm-flags || (shm-flags ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))) return TEEC_ERROR_BAD_PARAMETERS; if (!shm-buffer) return TEEC_ERROR_BAD_PARAMETERS; s shm-size; if (!s) s 8; if (ctx-imp.reg_mem) { fd teec_shm_register(ctx-imp.fd, shm-buffer, s, shm-imp.id); if (fd 0) { shm-imp.registered_fd fd; shm-imp.shadow_buffer NULL; shm-imp.flags 0; goto out; } /* * If were here TEE_IOC_SHM_REGISTER failed, probably * because some read-only memory was supplied and the Linux * kernel doesnt like that at the moment. * * The error could also have some other origin. In any case * were not making matters worse by trying to allocate and * register a shadow buffer before giving up. */ shm-imp.shadow_buffer teec_paged_aligned_alloc(s); if (!shm-imp.shadow_buffer) return TEEC_ERROR_OUT_OF_MEMORY; fd teec_shm_register(ctx-imp.fd, shm-imp.shadow_buffer, s, shm-imp.id); if (fd 0) { shm-imp.registered_fd fd; shm-imp.flags SHM_FLAG_SHADOW_BUFFER_ALLOCED; goto out; } if (errno ENOMEM) res TEEC_ERROR_OUT_OF_MEMORY; else res TEEC_ERROR_GENERIC; free(shm-imp.shadow_buffer); shm-imp.shadow_buffer NULL; return res; } else { fd teec_shm_alloc(ctx-imp.fd, s, shm-imp.id); if (fd 0) return TEEC_ERROR_OUT_OF_MEMORY; shm-imp.shadow_buffer mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (shm-imp.shadow_buffer (void *)MAP_FAILED) { shm-imp.id -1; return TEEC_ERROR_OUT_OF_MEMORY; } shm-imp.registered_fd -1; shm-imp.flags 0; } out: shm-imp.alloced_size s; return TEEC_SUCCESS; }功能将用户空间的内存注册到TEE供安全世界访问关键逻辑校验参数上下文/共享内存非空、标志合法、缓冲区非空若TEE支持内存注册reg_mem直接注册用户内存若注册失败如用户内存只读分配影子缓冲区可写注册影子缓冲区并拷贝用户数据记录共享内存ID、FD、影子缓冲区等元信息。3. 会话管理TEEC_OpenSessionTEEC_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; }功能与TEE中的可信应用TA建立会话核心步骤构造tee_ioctl_open_session_arg参数TA的UUID、客户端登录方式、参数列表预处理操作参数转换为TEE内核可识别的格式见下文参数处理通过TEE_IOC_OPEN_SESSION ioctl发送会话打开请求后处理参数将TEE返回的数据拷贝回用户空间记录会话ID返回执行结果。TEEC_CloseSessionvoid TEEC_CloseSession(TEEC_Session *session) { struct tee_ioctl_close_session_arg arg; memset(arg, 0, sizeof(arg)); if (!session) return; arg.session session-imp.session_id; if (ioctl(session-imp.ctx-imp.fd, TEE_IOC_CLOSE_SESSION, arg)) EMSG(Failed to close session 0x%x, session-imp.session_id); }功能关闭TEE会话通过TEE_IOC_CLOSE_SESSION ioctl通知TEE内核。4. 命令调用TEEC_InvokeCommandTEEC_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; 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); } res teec_pre_process_operation(session-imp.ctx, operation, params, shm); if (res ! TEEC_SUCCESS) { eorig TEEC_ORIGIN_API; goto out_free_temp_refs; } 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); out_free_temp_refs: teec_free_temp_refs(operation, shm); out: if (error_origin) *error_origin eorig; return res; }功能向已建立的TEE会话发送命令调用TA的接口逻辑构造tee_ioctl_invoke_arg参数会话ID、命令ID、参数列表预处理操作参数保证线程安全关联operation与session通过TEE_IOC_INVOKE ioctl发送命令后处理参数返回TEE的执行结果和错误来源。5. 操作取消TEEC_RequestCancellationvoid TEEC_RequestCancellation(TEEC_Operation *operation) { TEEC_Session *session NULL; struct tee_ioctl_cancel_arg arg; memset(arg, 0, sizeof(arg)); if (!operation) return; teec_mutex_lock(teec_mutex); session operation-imp.session; teec_mutex_unlock(teec_mutex); if (!session) return; arg.session session-imp.session_id; arg.cancel_id 0; if (ioctl(session-imp.ctx-imp.fd, TEE_IOC_CANCEL, arg)) EMSG(TEE_IOC_CANCEL: %s, strerror(errno)); }功能取消正在执行的TEE操作通过TEE_IOC_CANCEL ioctl通知TEE内核。五、参数处理TEE支持多种参数类型值参数、内存引用参数代码通过pre_process/post_process系列函数完成用户空间参数 ↔ TEE内核参数的转换1. 参数类型分类参数类型用途TEEC_NONE无参数TEEC_VALUE_*整型值参数输入/输出/输入输出TEEC_MEMREF_TEMP_*临时内存引用单次命令有效无需提前注册TEEC_MEMREF_WHOLE整段注册内存引用使用已注册的共享内存全部空间TEEC_MEMREF_PARTIAL_*部分注册内存引用使用已注册共享内存的指定偏移/大小2. 预处理teec_pre_process_*转换参数类型为TEE内核定义的tee_ioctl_param格式处理内存引用分配临时共享内存、拷贝用户数据到影子缓冲区校验参数合法性如偏移大小不超过共享内存范围。3. 后处理teec_post_process_*将TEE返回的结果如输出内存数据、值参数拷贝回用户空间更新用户传入的参数结构体如内存大小、值参数的a/b字段。4. 临时内存释放teec_free_temp_refs释放临时内存引用分配的共享内存避免内存泄漏。六、错误处理与辅助函数1. ioctl_errno_to_resstatic TEEC_Result ioctl_errno_to_res(int err)将系统调用错误码如ENOMEM、EINVAL转换为GP标准的TEEC错误码如TEEC_ERROR_OUT_OF_MEMORY、TEEC_ERROR_BAD_PARAMETERS。2. uuid_to_octetsstatic void uuid_to_octets(uint8_t d[TEE_IOCTL_UUID_LEN], const TEEC_UUID *s)将TEEC_UUID结构体转换为TEE内核要求的字节数组格式。3. setup_client_datastatic void setup_client_data(struct tee_ioctl_open_session_arg *arg, uint32_t connection_method, const void *connection_data)设置客户端登录方式如公共登录、用户登录、组登录填充会话的客户端身份信息。七、核心功能总结该代码实现了GP TEE客户端API的核心能力主要解决以下需求TEE设备交互遍历并打开兼容的TEE设备验证TEE兼容性共享内存管理支持注册用户内存、分配TEE内存处理内存对齐和只读内存问题影子缓冲区会话生命周期与TA建立/关闭会话传递客户端身份信息命令调用向TA发送命令处理多类型参数的转换与数据拷贝线程安全通过互斥锁保证多线程访问TEE资源的安全性错误标准化将系统错误转换为GP标准错误码便于上层应用处理。八、典型使用流程1. TEEC_InitializeContext → 初始化TEE上下文 2. TEEC_AllocateSharedMemory/RegisterSharedMemory → 分配/注册共享内存 3. TEEC_OpenSession → 与TA建立会话 4. TEEC_InvokeCommand → 调用TA的命令传递参数、共享内存 5. TEEC_CloseSession → 关闭会话 6. TEEC_ReleaseSharedMemory → 释放共享内存 7. TEEC_FinalizeContext → 销毁上下文