第2章:OP-TEE源码结构——源码目录分析、核心组件介绍、构建系统解析、编译流程

好,咱们直接进入正题。上一章我们把OP-TEE的宏观概念聊清楚了,这一章我带你钻进它的肚子里,看看源码到底是怎么组织的。说实话,我第一次接触OP-TEE源码时,也被那一堆文件夹搞得有点懵。但摸清楚规律后,你会发现它的结构其实非常清晰。

2.1 源码目录分析

OP-TEE的源码托管在GitHub上,项目名叫optee_os。你把它clone下来后,顶层目录大概长这样:

optee_os/
├── core/              # 核心代码,TEE内核
├── lib/               # 库文件,密码学、内存管理等
├── mk/                # 构建系统的Makefile片段
├── out/               # 编译输出目录
├── scripts/           # 辅助脚本
├── ta/                # Trusted Application示例
├── documentation/     # 文档
└── keys/              # 签名密钥

嗯,这里我要重点说说core/目录。它才是OP-TEE的心脏。我当年调试一个内存隔离的bug,就是在core/arch/arm/下面翻了好几天。

core/的子目录结构:

  • arch/:架构相关代码,ARMv7、ARMv8都在这里。你想想看,不同CPU的页表、异常处理都不一样,这部分就是做硬件适配的。
  • kernel/:TEE内核的核心逻辑,包括线程管理、IPC、系统调用。说白了,这就是TEE里的“操作系统”。
  • mm/:内存管理。我遇到过最头疼的问题就是这里——页表配置错了,导致安全世界和非安全世界的内存互相泄露。
  • tee/:TEE核心服务,比如TEE_OpenSession、TEE_InvokeCommand这些API的实现。
  • drivers/:外设驱动,比如UART、SPI、RNG(随机数生成器)。

个人经验:如果你要移植OP-TEE到新平台,core/arch/arm/plat-xxxx/这个目录是你最先要看的。每个平台都有自己的板级初始化代码,我习惯先从这里入手,把串口调通,后面调试就方便多了。

2.2 核心组件介绍

OP-TEE不是一个大泥球,它由几个关键组件拼起来的。我画了一张图,帮你理清它们的关系:

OP-TEE核心组件架构图 非安全世界 (REE) 安全世界 (TEE) Client Application (CA) TEE Client API (libteec) OP-TEE Linux 驱动 Trusted Application (TA) TEE Internal API OP-TEE 内核 (core/) 安全硬件 (TZASC/TrustZone) SMC调用

这张图里,我标出了几个关键组件:

  • CA(Client Application):跑在Linux里的普通应用,比如你的手机支付App。它通过TEE Client API发起请求。
  • libteec:TEE Client API的实现库。CA调用它,它再通过Linux驱动和TEE通信。
  • OP-TEE Linux驱动:负责在REE和TEE之间传递消息。我调试时经常在这里加打印,看SMC调用是否成功。
  • TA(Trusted Application):跑在TEE里的安全应用,比如指纹比对、密钥管理。TA通过TEE Internal API使用TEE内核的服务。
  • OP-TEE内核:就是前面说的core/目录,提供内存隔离、密码学、安全存储等基础能力。

避坑指南:我曾经以为TA和CA是直接通信的,后来才发现它们之间隔了好几层。每次调用都要经过:CA → libteec → 驱动 → SMC → TEE内核 → TA。这个路径上的任何一环出问题,调用就会失败。所以调试时,建议从驱动层开始排查。

2.3 构建系统解析

OP-TEE用的是自己那套基于Makefile的构建系统。说实话,我第一次看mk/目录下的文件时,头都大了。但用久了你会发现,它其实很灵活。

构建系统的核心文件:

文件/目录 作用
Makefile 顶层Makefile,入口文件
mk/ 构建规则片段,按功能拆分
mk/config.mk 全局配置选项,比如是否开启调试
core/core.mk 核心模块的构建规则
ta/ta.mk TA的构建规则

你想想看,为什么OP-TEE不用CMake或者Kbuild?我个人觉得,Makefile的好处是依赖关系清晰,而且嵌入式环境里Makefile的兼容性最好。你总不能在裸机上跑Python吧?

构建系统的关键变量:

# 平台配置
PLATFORM ?= vexpress-qemu_v8a
PLATFORM_FLAVOR ?= qemu_virt

# 调试选项
CFG_TEE_CORE_DEBUG ?= y
CFG_TEE_TA_LOG_LEVEL ?= 3

# 内存配置
CFG_TZDRAM_START ?= 0x0e000000
CFG_TZDRAM_SIZE ?= 0x02000000

嗯,这里要注意CFG_开头的变量。它们是OP-TEE的配置开关,几乎每个功能都可以通过它来控制。我习惯在mk/config.mk里先搜一遍,看看有哪些选项可用。

2.4 编译流程

编译OP-TEE,说白了就是三步走:配置、编译、打包。但每一步里都有不少门道。

第一步:配置

运行make menuconfig或者直接设置环境变量。我个人更喜欢直接写变量,因为可以脚本化:

make PLATFORM=vexpress-qemu_v8a \
     CFG_TEE_CORE_DEBUG=y \
     CFG_TEE_TA_LOG_LEVEL=4 \
     O=out

第二步:编译

直接make就行。编译过程大致是:

  1. 先编译工具链相关的头文件检查
  2. 然后编译lib/目录下的库文件
  3. 接着编译core/目录下的内核代码
  4. 最后编译ta/目录下的示例TA
  5. 链接生成最终的tee.bin或tee.elf

注意:我第一次编译时,卡在了链接阶段。后来发现是内存布局文件core/arch/arm/plat-xxxx/platform_config.h里的地址配置错了。TEE的基地址必须和硬件设计一致,否则跑不起来。

第三步:打包

编译完成后,out/目录下会生成:

  • tee.bin:纯二进制镜像,可以直接烧录
  • tee.elf:带符号表的镜像,用于调试
  • tee.dump:反汇编文件,我经常用它来查问题
  • ta/:编译好的TA文件,后缀是.ta

如果你用的是QEMU模拟器,还需要把tee.bin和Linux内核、设备树打包到一起。这个流程在OP-TEE的build/仓库里有脚本,我建议你直接用现成的。

我的习惯:每次编译前,我都会先make clean一下,避免旧文件干扰。然后加上CFG_TEE_CORE_DEBUG=y,这样出错了能直接看到堆栈。等调试完了,再关掉调试选项,编译release版本。

好了,这一章我们把OP-TEE的源码结构、核心组件、构建系统和编译流程都过了一遍。说白了,源码结构就是告诉你“东西在哪”,核心组件告诉你“谁负责什么”,构建系统告诉你“怎么配”,编译流程告诉你“怎么跑”。把这四块搞明白,你就能在OP-TEE的代码里自由穿梭了。

可可精品资料库