3. OP-TEE 源码结构分析:核心目录与关键文件

拿到 OP-TEE 源码后,很多人第一反应是「从哪看起?」。我当年也踩过这个坑——对着几十个文件夹发呆。其实它的结构非常清晰,说白了就四个核心目录加几个关键文件。今天咱们就把这块彻底捋清楚。

核心要点:OP-TEE 源码按功能分层,core/ 是内核态、lib/ 是库、ta/ 是可信应用、scripts/ 是工具链。理解这个分层,你就能快速定位问题。

3.1 四层架构:core/、lib/、ta/、scripts/

先看整体结构。我习惯用一张图来理解它:

OP-TEE 源码四层架构 core/ — 内核态核心 入口、系统调用、线程调度、内存管理、驱动 关键文件:entry.S, tee_svc.c, thread.c, mm/ lib/ — 用户态与内核态共享库 libutee(TA 接口)、libutils(工具函数)、libmpa(大数运算) libkernel(内核辅助)、libmbedtls(加密库) ta/ — 可信应用(Trusted Applications) 官方示例 TA:hello_world、secure_storage、rpc_test 每个 TA 包含:user_ta_header.c、Makefile、entry.c scripts/ — 构建与调试脚本 sign_encrypt.py(TA 签名)、ta_bin_to_c.py(二进制转 C 数组) checkpatch.pl(代码风格检查)、symbolize.py(崩溃栈解析)

3.2 core/ 目录深度解析

这是 OP-TEE 的心脏。所有安全世界的内核代码都在这里。我刚开始调优时,90% 的问题都出在这个目录里。

子目录/文件 作用 我踩过的坑
arch/arm/ 架构相关代码,含启动、异常向量、MMU MMU 配置错了,TA 直接 panic
kernel/ 线程管理、调度、IPC 线程栈大小设太小,跑复杂 TA 就崩
tee/ 系统调用实现、TA 管理、属性处理 tee_svc.c 里加新调用,忘了注册
mm/ 内存管理、页表、缓存操作 缓存一致性没处理好,数据全乱
drivers/ 外设驱动(UART、SPI、RNG 等) RNG 驱动不工作,TA 随机数全一样
lib/ 内核态库(libtomcrypt、libutils) 链接顺序错了,符号找不到

我的建议:第一次看 core/,先读 kernel/thread.c 和 tee/tee_svc.c。理解了线程怎么跑、系统调用怎么分发,其他模块就顺了。

3.3 三个关键文件:entry.S、tee_svc.c、thread.c

这三个文件是 OP-TEE 的「三驾马车」。我每次移植新平台,第一个改的就是它们。

3.3.1 entry.S — 世界的入口

这是安全世界的启动入口和异常向量表。CPU 一进入安全模式,第一条指令就在这里。

/* core/arch/arm/kernel/entry.S — 简化版启动流程 */
.section .text.boot
.global _start
_start:
    /* 1. 设置栈指针 */
    ldr sp, =stack_top

    /* 2. 清零 BSS 段 */
    ldr r0, =__bss_start
    ldr r1, =__bss_end
    mov r2, #0
    bl  memset

    /* 3. 初始化 MMU 和缓存 */
    bl  arm_init_mmu

    /* 4. 跳转到 C 语言入口 */
    bl  main_init

嗯,这里要注意:不同 ARM 架构(Cortex-A vs Cortex-M)的 entry.S 差异很大。我曾经在 Cortex-M 上直接用了 Cortex-A 的启动代码,结果 MMU 初始化就挂了——因为 M 系列根本没有 MMU。

3.3.2 tee_svc.c — 系统调用的调度中心

所有从普通世界(REE)发来的请求,最终都会落到 tee_svc.c 里。它负责解析命令、调用对应的处理函数。

/* core/tee/tee_svc.c — 系统调用分发核心 */
TEE_Result tee_svc_do_call(struct tee_ta_session *sess,
                           uint32_t cmd, struct tee_ta_param *param)
{
    TEE_Result res;

    /* 1. 检查会话是否有效 */
    if (!sess || !sess->ctx)
        return TEE_ERROR_BAD_PARAMETERS;

    /* 2. 调用 TA 的入口函数 */
    res = sess->ctx->ops->invoke(sess->ctx, cmd, param);

    /* 3. 处理返回值 */
    if (res != TEE_SUCCESS)
        EMSG("TA invoke failed: 0x%x", res);

    return res;
}

避坑指南:我曾经在 tee_svc.c 里加了一个新的系统调用编号,但忘了更新 tee_svc_syscall_table 数组。结果调用时直接跳到了错误函数,系统崩溃。记住:加新调用必须同步更新表。

3.3.3 thread.c — 线程的命脉

OP-TEE 使用线程池来处理并发请求。thread.c 管理线程的创建、调度、同步。

函数 作用 典型问题
thread_init() 初始化线程池,分配栈空间 栈大小不足,导致栈溢出
thread_alloc() 分配一个空闲线程 线程数不够,请求排队超时
thread_free() 释放线程回池 忘记释放,线程泄漏
thread_sched() 线程调度,切换上下文 优先级反转,低优线程饿死

我记得有一次调优,发现 TA 响应越来越慢。查了半天,原来是 thread_alloc 返回了 NULL——线程池用完了。后来把 CFG_NUM_THREADS 从 2 改成 4,问题解决。

3.4 lib/ 目录:共享库的智慧

lib/ 是 OP-TEE 的「工具箱」。它分为用户态库和内核态库,但代码放在一起管理。

  • libutee/:TA 开发的标准接口库,你写的 TA 都会链接它。包含 TEE_* 系列 API 的实现。
  • libutils/:通用工具函数,比如 malloc、memcpy、字符串操作。内核和 TA 都用。
  • libmpa/:多精度大数运算库,RSA、ECC 等算法依赖它。
  • libmbedtls/:mbed TLS 加密库的封装,提供对称加密、哈希、证书解析。
  • libkernel/:内核态辅助函数,比如缓存操作、原子操作。

实用技巧:如果你要移植新的加密算法,优先看 libmbedtls 的接口。它已经封装好了,你只需要注册算法 ID 和回调函数。我移植 SM4 时就是这么干的,两天搞定。

3.5 ta/ 目录:可信应用的样板间

ta/ 里放的是官方示例 TA。每个 TA 都是一个独立的子目录,结构非常固定:

ta/hello_world/
├── user_ta_header.c   # TA 的 UUID、堆栈大小、入口函数声明
├── entry.c            # TA 的入口函数(TA_CreateEntryPoint 等)
├── Makefile           # 编译规则
└── sub.mk             # 子模块配置

你想想看,如果你要写自己的 TA,直接复制 hello_world 改改就行。我刚开始做 TA 开发时,就是这么干的——把 UUID 一换,逻辑一改,新 TA 就跑起来了。

3.6 scripts/ 目录:开发者的瑞士军刀

这些脚本平时不起眼,但关键时刻能救命。

脚本 用途 我什么时候用它
sign_encrypt.py 对 TA 进行签名和加密 每次编译完 TA 都要跑,否则加载失败
ta_bin_to_c.py 将 TA 二进制转为 C 数组 需要把 TA 嵌入到 bootloader 时
symbolize.py 解析崩溃栈,还原函数名和行号 TA 崩溃时,用它把地址转成可读信息
checkpatch.pl 检查代码风格是否符合内核规范 提交代码前跑一遍,避免被 review 打回

重要提醒:symbolize.py 需要编译时保留符号表。我吃过亏——为了省空间把 CFG_TA_DEBUG 关了,结果 TA 崩溃后只有一堆地址,完全没法调试。调试阶段一定要开。

3.7 构建系统:Makefile 与 CMake 的博弈

OP-TEE 早期只用 Makefile,后来引入了 CMake 支持。现在两者并存,但 Makefile 仍是主流。

3.7.1 Makefile 体系

根目录的 Makefile 是总入口,它会递归调用各子目录的 sub.mk。核心变量:

# 核心构建变量
PLATFORM ?= vexpress
PLATFORM_FLAVOR ?= qemu_virt
CFG_TEE_CORE_LOG_LEVEL ?= 2
CFG_NUM_THREADS ?= 2
CFG_TA_DEBUG ?= y

# 编译命令示例
make PLATFORM=stm32mp1 PLATFORM_FLAVOR=stm32mp157c-dk2 \
     CFG_TEE_CORE_LOG_LEVEL=3 CFG_NUM_THREADS=4

3.7.2 CMake 支持

CMake 主要用于跨平台构建和 IDE 集成。我个人觉得 CMake 的依赖管理更清晰,但社区还是以 Makefile 为主。

# CMakeLists.txt 片段
cmake_minimum_required(VERSION 3.10)
project(optee_os)

option(CFG_TEE_CORE_DEBUG "Enable debug" OFF)
set(PLATFORM "vexpress" CACHE STRING "Target platform")

add_subdirectory(core)
add_subdirectory(lib)

我的习惯:开发阶段用 Makefile,因为改一行配置就能重新编译,速度快。做 CI/CD 集成时用 CMake,因为依赖管理和测试框架更完善。

3.8 本章小结

OP-TEE 的源码结构其实不复杂。记住四层架构:core/ 是内核、lib/ 是库、ta/ 是应用、scripts/ 是工具。三个关键文件 entry.S、tee_svc.c、thread.c 是入门的钥匙。构建系统 Makefile 为主、CMake 为辅。

下次你遇到问题,先问自己:这个问题出在哪一层?是内核态还是用户态?是启动阶段还是运行时?定位清楚了,改起来就快了。