第3章:OP-TEE源码结构:源码目录分析、核心组件介绍、构建系统详解
好,咱们直接切入正题。OP-TEE的源码,说实话第一次看的时候我也懵了一阵子。目录多,文件杂,不知道从哪下手。但摸清楚规律之后,你会发现它的结构其实非常清晰——说白了,就是「安全世界」和「非安全世界」的代码分得明明白白。
3.1 源码目录分析
我先带你走一遍顶层目录。你拿到OP-TEE的源码后,ls一下,大概会看到这些核心文件夹:
op-tee/
├── core/ # TEE内核代码(运行在安全世界)
├── ta/ # 可信应用(Trusted Applications)的示例和框架
├── lib/ # 通用库,比如libutee、libutils
├── mk/ # 构建系统的Makefile片段
├── scripts/ # 辅助脚本,比如签名、打包
├── out/ # 编译输出目录(默认)
└── documentation/ # 文档,但说实话我很少看,不如直接读代码
我个人习惯,拿到一个新版本,先看core/arch/arm/下面的东西。为什么?因为这里面藏着平台相关的代码,比如你用的是哪个SoC、哪个开发板,都在这里配置。
核心目录速查表:
| 目录 | 作用 | 我踩过的坑 |
|---|---|---|
| core/ | TEE内核,包括调度、内存管理、密码学服务 | 别乱改core/kernel/下的文件,容易崩 |
| ta/ | 可信应用示例,比如hello_world、secure_storage | 开发自己的TA时,直接复制ta/下的模板最稳 |
| lib/libutee/ | 用户态TA的API库,比如TEE_InvokeTACommand | API版本要跟内核匹配,否则链接报错 |
| mk/ | 构建系统的核心规则 | 我曾经改错一个变量名,编译了半小时才发现 |
嗯,这里要注意:core/下面还有一个arch/目录,里面按CPU架构分。目前主流是arm,但RISC-V的支持也在慢慢加进来。你如果做移植,第一件事就是看core/arch/arm/plat-xxxx/,这个plat-前缀的文件夹就是你的平台。
3.2 核心组件介绍
OP-TEE的核心组件,说白了就三块:TEE内核、可信应用(TA)、以及通信机制。我一个个说。
3.2.1 TEE内核(Core)
TEE内核运行在安全世界,它负责管理安全内存、处理安全中断、提供密码学服务。你想想看,它其实就是一个微型的操作系统,只不过功能非常专一。
核心组件包括:
- 线程调度器:管理TA的执行上下文。我记得有一次调试,发现TA莫名其妙卡死,最后查出来是线程栈溢出了。
- 内存管理单元:负责安全内存和非安全内存的隔离。这里有个关键概念叫「静态内存池」,说白了就是启动时预留好的一块安全内存,TA只能在这块区域里玩。
- 密码学服务:硬件加速的加解密、签名验签。我建议你优先用硬件加速,纯软件实现慢得让人抓狂。
- IPC框架:TA和CA(Client Application)之间的通信桥梁。
我的经验:如果你要新增一个系统调用,比如自定义一个安全存储接口,那你要改core/tee/下面的文件。别怕,代码注释挺全的,但记得同步更新lib/libutee/里的用户态封装。
3.2.2 可信应用(TA)
TA就是跑在安全世界的应用程序。每个TA都是一个独立的ELF文件,被签名后加载到TEE中执行。我刚开始写TA时,总觉得它跟普通Linux程序差不多,其实区别大了去了——TA不能直接访问外设,不能开线程,内存也受限。
一个典型的TA目录结构:
ta/hello_world/
├── include/ # 头文件,定义命令ID
├── src/ # 源码,比如main.c
├── ta_entry.c # 入口函数,处理CA发来的命令
├── Makefile # TA的构建文件
└── user_ta_header.c # TA的元数据,比如UUID、堆栈大小
这里有个坑:UUID必须唯一。我曾经因为复制粘贴忘了改UUID,结果两个TA冲突,系统直接panic。嗯,血的教训。
3.2.3 通信机制
CA和TA怎么通信?通过TEE Client API。CA调用TEEC_OpenSession建立会话,然后通过TEEC_InvokeCommand发送命令。底层其实走的是SMC(Secure Monitor Call)指令,从非安全世界陷入安全世界。
我画了一张图,帮你理解整个调用流程:
你看,CA发起的请求,经过Linux内核里的OP-TEE驱动,通过SMC指令陷入安全世界,然后TEE内核把请求分发给对应的TA。TA处理完后,原路返回结果。整个过程对CA来说是透明的——它只管调用API,不用管底层怎么切来切去。
3.3 构建系统详解
OP-TEE的构建系统基于Makefile,但说实话,它比普通的Makefile复杂得多。我第一次编译的时候,光是配置环境变量就折腾了半天。
3.3.1 构建流程概览
整个构建流程大致分三步:
- 配置:指定平台、工具链、编译选项。比如
make PLATFORM=stm32mp1。 - 编译:先编译TEE内核,再编译TA。注意,TA的编译依赖TEE内核的头文件。
- 打包:生成最终的镜像文件,比如
tee-pager.bin、tee-pageable.bin。
我建议你第一次编译时,用make -j4并行编译,但别开太多线程,否则容易把机器搞卡。我当年用8核编译,结果内存爆了,直接死机。
3.3.2 关键变量和配置
构建系统里有一堆环境变量,我挑几个最常用的:
| 变量名 | 作用 | 示例值 |
|---|---|---|
| PLATFORM | 目标平台 | stm32mp1, qemu_virt |
| O | 输出目录 | ./out |
| CFG_TEE_CORE_LOG_LEVEL | 日志级别,调试时建议开高 | 3(最高) |
| CFG_TEE_TA_LOG_LEVEL | TA的日志级别 | 3 |
| DEBUG | 是否开启调试符号 | 0或1 |
警告:生产环境一定要把CFG_TEE_CORE_LOG_LEVEL设为0,否则日志输出会拖慢性能,而且可能泄露敏感信息。我曾经在客户现场忘了关,结果被安全审计怼了一顿。
3.3.3 自定义构建
如果你要添加自己的TA,需要在ta/目录下新建一个文件夹,然后写一个Makefile。我一般直接复制ta/hello_world/的模板,改一下UUID和源文件路径就行。
举个例子,假设你要加一个叫my_secure_app的TA:
# ta/my_secure_app/Makefile
BINARY = my_secure_app
TA_SRC_DIR = src/
TA_CURRENT_LIB = libutee
include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk
然后运行make,构建系统会自动编译并签名你的TA。签名用的私钥默认在keys/目录下,但生产环境一定要换成自己的密钥。
嗯,这里再提一句:构建系统里有个CFG_TEE_CORE_EMBED_INTERNAL_TESTS选项,打开后可以跑自检。我每次移植完一个新平台,都会先跑一遍自检,确保基本功能没问题。
避坑指南:我曾经在构建时遇到undefined reference to 'xxx'的错误,查了半天发现是lib/libutee和core/的版本不匹配。记住,TA的API版本必须和TEE内核一致,否则链接不过。
好了,源码结构、核心组件、构建系统,这三块是OP-TEE开发的基石。你把这些搞清楚了,后面写TA、调驱动、做移植,都会顺手很多。下一章我们聊TA的开发实战,到时候我会拿一个具体的例子,手把手带你走一遍。
可可精品资料库