[手撕ARM64启动栈(十):OP-TEE 用户空间——libteec 与 C hello_world CA/TA 交互

发布时间:2026/8/26 19:14:32
[手撕ARM64启动栈(十):OP-TEE 用户空间——libteec 与 C hello_world CA/TA 交互 系列文章目录手撕ARM64启动栈一QEMU TF-A → OP-TEE → U-Boot → Linux 全链路总览手撕ARM64启动栈二QEMU TF-A(ATF) BL1 执行上下文——GDB 实测复位到 BL2手撕ARM64启动栈三QEMU TF-A(ATF) BL1 核心职责——bl1_main()逐函数拆解与 BL2 加载手撕ARM64启动栈四QEMU TF-A(ATF) BL2 详解手撕ARM64启动栈五QEMU TF-A(ATF) BL32/OP-TEE 镜像头解析——三模式判定与入口改写手撕ARM64启动栈六QEMU TF-A(ATF) BL31 运行时服务框架与 opteed 注册手撕ARM64启动栈七QEMU TF-A(ATF) BL31 世界切换手撕ARM64启动栈八QEMU TF-A(ATF) OP-TEE 初始化详解手撕ARM64启动栈九OP-TEE Linux Driver——加载、SMC 握手与设备节点创建[手撕ARM64启动栈十OP-TEE 用户空间——libteec 与 C hello_world CA/TA 交互文章目录系列文章目录手撕ARM64启动栈十QEMU TF-A(ATF) OP-TEE 用户空间— hello_world CA/TA 交互1. 引言从一次 42 → 43 的调用说起2. hello_world运行结果3. CA 到 TA 的总体架构与 hello_world 协议3.1 目录结构3.2 UUID 与 command ID4.1 建立 context 与 session4.2 传参与 InvokeCommand4.3 关闭与释放5. 背后的机制libteec 与 GlobalPlatform API5.1 libteec 组件与 ioctl 映射5.2 核心对象与调用顺序5.3 参数类型、错误处理与共享内存6. 小结手撕ARM64启动栈十QEMU TF-A(ATF) OP-TEE 用户空间— hello_world CA/TA 交互1. 引言从一次 42 → 43 的调用说起上一篇讲到 Linux OP-TEE driver 完成 probe并注册/dev/tee0和/dev/teepriv0。本文用optee_examples/hello_world为例说明CA/TA完整交互流程到本篇截止你就可以写自己的CA/TA了并且完整了解了其工作原理本篇先给出CA/TA实际运行结果再逐步展开这次调用具体做了什么最后补充 GlobalPlatform Client API 的对象和参数细节。一次调用做了以下几件事CA 初始化 TEE contextCA 按 UUID 打开 hello_world TA sessionCA 把整数 42 作为VALUE_INOUT参数发给 TATA 加一并回写 43CA 关闭 session 和 context。2. hello_world运行结果启动 QEMU、登录 root 后运行optee_example_hello_world。Normal World 输出# optee_example_hello_world Invoking TA to increment 42 TA incremented value to 43 #Secure World 调试日志应包含D/TC:? 0 tee_ta_init_pseudo_ta_session:303 Lookup pseudo TA 8aaaf200-2450-11e4-abe2-0002a5d5c51b D/TC:? 0 ldelf_load_ldelf:110 ldelf load address 0x80008000 D/LD: ldelf:159 Loading TS 8aaaf200-2450-11e4-abe2-0002a5d5c51b D/TC:? 0 ldelf_syscall_open_bin:163 Lookup user TA ELF 8aaaf200-2450-11e4-abe2-0002a5d5c51b (early TA) D/TC:? 0 ldelf_syscall_open_bin:167 res0xffff0008 D/TC:? 0 ldelf_syscall_open_bin:163 Lookup user TA ELF 8aaaf200-2450-11e4-abe2-0002a5d5c51b (Secure Storage TA) D/TC:? 0 ldelf_syscall_open_bin:167 res0xffff0008 D/TC:? 0 ldelf_syscall_open_bin:163 Lookup user TA ELF 8aaaf200-2450-11e4-abe2-0002a5d5c51b (REE) D/TC:? 0 ldelf_syscall_open_bin:167 res0 D/TC:? 0 thread_rpc_free:2298 freeing cookie 0x100000000003 D/LD: ldelf:207 ELF (8aaaf200-2450-11e4-abe2-0002a5d5c51b) at 0x80044000 D/TA: TA_CreateEntryPoint:18 has been called D/TA: __GP11_TA_OpenSessionEntryPoint:47 has been called I/TA: Hello World! D/TC:? 0 thread_rpc_free:2298 freeing cookie 0x100000000001 D/TA: inc_value:78 has been called I/TA: Got value: 42 from NW I/TA: Increase value to: 43 D/TC:? 0 tee_ta_close_session:513 id 4 D/TC:? 0 tee_ta_close_session:527 Destroy session I/TA: Goodbye! D/TA: TA_DestroyEntryPoint:29 has been called D/TC:? 0 destroy_context:326 Destroy TA ctx (0x545f18e0)3. CA 到 TA 的总体架构与 hello_world 协议第 2 节的输出和日志走的就是这条路径CA 通过libteec打开/dev/tee0经 Linux TEE core 和 OP-TEE driver 转发给 Secure World由 hello_world TA 处理后原路返回。若 TA 尚未加载OP-TEE 会在OpenSession阶段通过 Reverse RPC 请求 Linux driver再由/dev/teepriv0交给tee-supplicant读取 REE-FS 上的.ta文件细节后续tee-supplicant篇章会展开。3.1 目录结构optee_examples/hello_world/ ├── Makefile ├── host/ │ ├── Makefile │ └── main.c # Normal World CA └── ta/ ├── Makefile ├── hello_world_ta.c # Secure World TA ├── user_ta_header_defines.h # 定义TA 属性、栈和堆大小 └── include/ └── hello_world_ta.h # 定义CA/TA 共用 UUID 和 command IDhost 与 TA 是两个独立二进制共用hello_world_ta.h里定义的 UUID 和 command ID。3.2 UUID 与 command ID#defineTA_HELLO_WORLD_UUID\{0x8aaaf200,0x2450,0x11e4,\{0xab,0xe2,0x00,0x02,0xa5,0xd5,0xc5,0x1b}}#defineTA_HELLO_WORLD_CMD_INC_VALUE0#defineTA_HELLO_WORLD_CMD_DEC_VALUE1TA 的 Makefile 用同一个 UUID 作为BINARY生成的 TA 文件名为8aaaf200-2450-11e4-abe2-0002a5d5c51b.taBINARY 8aaaf200-2450-11e4-abe2-0002a5d5c51b#4 一次open session 与InvokeCommand 逐步解析TA 侧实现五个标准入口入口调用时机hello_world 行为TA_CreateEntryPoint()TA 实例创建返回TEE_SUCCESSTA_OpenSessionEntryPoint()新 session 建立校验四个参数都是NONETA_InvokeCommandEntryPoint()CA 调用 command按 command ID 分发到inc_value()/dec_value()TA_CloseSessionEntryPoint()session 关闭无额外处理TA_DestroyEntryPoint()TA 实例销毁无额外资源清理4.1 建立 context 与 sessionCA 初始化 contextTEEC_Context ctx;TEEC_Result res;resTEEC_InitializeContext(NULL,ctx);if(res!TEEC_SUCCESS)errx(1,TEEC_InitializeContext failed with code 0x%x,res);libteec遍历/dev/tee0到/dev/teeN对候选设备执行TEE_IOC_VERSION成功后把 fd 保存到ctx。CA 按 UUID 打开 sessionTEEC_Session sess;TEEC_UUID uuidTA_HELLO_WORLD_UUID;uint32_terr_origin;resTEEC_OpenSession(ctx,sess,uuid,TEEC_LOGIN_PUBLIC,NULL,NULL,err_origin);TEEC_LOGIN_PUBLIC不附加身份数据两个NULL分别表示没有 login data、不向 TA 传 open-session 参数err_origin记录失败时的错误来源。libteec把 UUID 转成 Linux TEE UAPI 格式并发出TEE_IOC_OPEN_SESSION若 TA 尚未加载OP-TEE 会在此阶段触发 REE-FS 加载。TA 侧依次执行TA_CreateEntryPoint()和TA_OpenSessionEntryPoint()后者打印Hello World!对应第 2 节日志第一行随后 kernel 把返回的 session ID 保存到sess。4.2 传参与 InvokeCommandCA 构造参数并调用 command 0TEEC_Operation op;memset(op,0,sizeof(op));op.paramTypesTEEC_PARAM_TYPES(TEEC_VALUE_INOUT,TEEC_NONE,TEEC_NONE,TEEC_NONE);op.params[0].value.a42;printf(Invoking TA to increment %d\n,op.params[0].value.a);resTEEC_InvokeCommand(sess,TA_HELLO_WORLD_CMD_INC_VALUE,op,err_origin);if(res!TEEC_SUCCESS)errx(1,TEEC_InvokeCommand failed with code 0x%x origin 0x%x,res,err_origin);printf(TA incremented value to %d\n,op.params[0].value.a);参数 0 是双向 value。TEEC_InvokeCommand()把 command ID、session ID 和参数转换成TEE_IOC_INVOKE发给 kernel。TA 侧按 command ID 分发switch(cmd_id){caseTA_HELLO_WORLD_CMD_INC_VALUE:returninc_value(param_types,params);caseTA_HELLO_WORLD_CMD_DEC_VALUE:returndec_value(param_types,params);default:returnTEE_ERROR_BAD_PARAMETERS;}inc_value()校验参数类型后加一并回写uint32_texpectedTEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT,TEE_PARAM_TYPE_NONE,TEE_PARAM_TYPE_NONE,TEE_PARAM_TYPE_NONE);if(param_types!expected)returnTEE_ERROR_BAD_PARAMETERS;IMSG(Got value: %u from NW,params[0].value.a);params[0].value.a;IMSG(Increase value to: %u,params[0].value.a);returnTEE_SUCCESS;4.3 关闭与释放TEEC_CloseSession(sess);TEEC_FinalizeContext(ctx);TEEC_CloseSession()发送TEE_IOC_CLOSE_SESSION触发 TA 的TA_CloseSessionEntryPoint()打印Goodbye!对应第 2 节日志最后一行。TEEC_FinalizeContext()关闭/dev/tee0fd。hello_world 没有独立TEEC_SharedMemory不需要额外 release。5. 背后的机制libteec 与 GlobalPlatform API前面已经逐步看过这次调用具体做了什么这里归纳背后的通用对象模型和 ioctl 映射关系。5.1 libteec 组件与 ioctl 映射OP-TEE 的通用用户空间组件位于optee_client/optee_client/ ├── libteec/ │ ├── include/tee_client_api.h # GlobalPlatform TEE Client API │ └── src/tee_client_api.c # Linux ioctl 实现 ├── tee-supplicant/ # Reverse RPC 守护进程 └── public/libteec向上给 CA 提供 GlobalPlatform 接口向下通过 ioctl 对接 Linux TEE UAPICA → GlobalPlatform TEEC_* API → libteec → Linux TEE UAPI ioctl → /dev/tee0TEEC_InitializeContext()/OpenSession()/InvokeCommand()/CloseSession()/FinalizeContext()对应的 ioctl 已在第 4 节逐步给出另外三个 hello_world 未用到的 APIlibteec APILinux 侧动作TEEC_RequestCancellation()发出TEE_IOC_CANCELTEEC_AllocateSharedMemory()若 driver 通告 registered-memory capability先分配 page-aligned 用户 buffer再发出TEE_IOC_SHM_REGISTER否则回退到TEE_IOC_SHM_ALLOC mmap()TEEC_RegisterSharedMemory()尝试发出TEE_IOC_SHM_REGISTER必要时使用 shadow buffer5.2 核心对象与调用顺序对象用途在当前实现中的关键状态TEEC_ContextCA 与一个 TEE 实例的连接/dev/teeNfd、registered-memory 和 NULL memref 能力TEEC_UUID标识目标 TA128 位 UUIDOpenSession时转换为 ioctl 字节序TEEC_SessionCA 与一个 TA 的逻辑会话所属 context 和 kernel 返回的 session IDTEEC_Operation一次 open/invoke 的四个参数paramTypes与params[4]TEEC_SharedMemoryCA 与 TA 之间的大块数据缓冲区buffer、size、方向、SHM ID、shadow buffer 等一个 context 可以建立多个 session一个 session 可以执行多次 command。标准调用顺序TEEC_InitializeContext() → TEEC_OpenSession() → TEEC_InvokeCommand()可重复 → TEEC_CloseSession() → TEEC_FinalizeContext()资源按相反顺序释放若使用独立共享内存还要在 finalize context 前调用TEEC_ReleaseSharedMemory()。5.3 参数类型、错误处理与共享内存TEEC_Operation固定四个参数槽TEEC_PARAM_TYPES()编码每槽类型类型数据形式方向TEEC_VALUE_INPUT/OUTPUT/INOUT两个uint32_tvalue.a、value.b输入、输出、双向TEEC_MEMREF_TEMP_*单次 operation 临时 buffer输入、输出、双向TEEC_MEMREF_WHOLE/PARTIAL_*已分配或已注册的TEEC_SharedMemory由 flags/type 决定hello_world 只传一个整数用TEEC_VALUE_INOUT即可不需要额外共享内存。TEEC_OpenSession()和TEEC_InvokeCommand()失败时同时返回TEEC_Result错误码和err_origin错误来源不能只看错误码判断err_origin含义TEEC_ORIGIN_APIlibteec参数检查或 API 实现TEEC_ORIGIN_COMMSCA 与 TEE 之间的通信层TEEC_ORIGIN_TEEOP-TEE 公共核心TEEC_ORIGIN_TRUSTED_APP目标 TA6. 小结C hello_world 用最小调用序列完成一次 CA/TA 交互InitializeContext → OpenSession(UUID) → InvokeCommand(INC_VALUE, VALUE_INOUT: 42) → TA 校验参数并写回 43 → CloseSession → FinalizeContext至此我们已经完成了从boot rom到linux 的启动流程并且完成了用户空间ca与ta交互的流程整个ATF的启动流程到此告一段落。后面章节将会针对security 不同领域进行分析。

相关新闻