3. OP-TEE 环境搭建:从源码到调试的全流程
说实话,搭建 OP-TEE 开发环境这件事,我前前后后折腾过不下十次。每次换新机器、换新版本,总会冒出些新问题。这一章我就把踩过的坑、总结的经验,一次性讲清楚。
咱们的目标很明确:让 OP-TEE 在 QEMU 模拟器上跑起来,并且能调试。这样你才能在上面跑国密算法,验证 TrustZone 下的可信执行逻辑。
3.1 环境概览:你需要什么?
先看一张整体架构图,心里有个谱。
整个环境分三层:主机(你面前的电脑)、QEMU 模拟层(虚拟的 ARM 开发板)、目标系统(OP-TEE 跑在 TrustZone 上)。
我个人习惯用 Ubuntu 22.04 LTS,64 位版本。其他 Linux 发行版也行,但包管理器的命令你得自己调整。
3.2 下载 OP-TEE 源码
OP-TEE 项目用 repo 工具管理多个 Git 仓库。我第一次用的时候也觉得麻烦,但用顺手了就知道——这玩意儿确实适合管理这种多仓库的嵌入式项目。
3.2.1 安装 repo 工具
# 创建 bin 目录并加入 PATH
mkdir -p ~/bin
export PATH=~/bin:$PATH
# 下载 repo 工具
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
https://mirrors.tuna.tsinghua.edu.cn/git/git-repo,速度快不少。
3.2.2 初始化并同步代码
# 创建工作目录
mkdir op-tee
cd op-tee
# 初始化 repo(以 4.0.0 版本为例)
repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml -b 4.0.0
# 同步所有仓库(这一步时间较长,建议挂代理)
repo sync -j4
-j4 表示用 4 个线程并行下载。你的 CPU 核心多可以调大,比如 -j8。我曾经在 4 核机器上开 -j16,结果网络连接数太多被服务器限流了……嗯,适可而止就好。
同步完成后,你会看到这些核心组件:
| 目录/仓库 | 作用 |
|---|---|
optee_os/ |
OP-TEE 内核(安全世界 OS) |
optee_client/ |
REE 端客户端 API 库 |
optee_examples/ |
示例 TA 和 CA 代码 |
build/ |
构建脚本和 Makefile |
qemu/ |
QEMU 模拟器源码 |
linux/ |
Linux 内核源码(REE 端) |
toolchains/ |
交叉编译工具链(自动下载) |
3.3 配置交叉编译工具链
说白了,交叉编译就是在 x86 的电脑上编译出 ARM 能跑的程序。OP-TEE 的 build/ 目录里已经集成了工具链的下载脚本,但有时候网络不好会失败。
3.3.1 自动下载(推荐)
cd build
make toolchains
这个命令会下载两个工具链:
- aarch64-linux-gnu:编译 64 位 ARM 代码(Linux 内核、OP-TEE OS)
- arm-linux-gnueabihf:编译 32 位 ARM 代码(某些 TA 和裸机程序)
3.3.2 手动配置(备选方案)
如果自动下载失败,别慌。我曾经在断网环境下搞过离线部署,手动指定工具链路径就行:
# 设置环境变量
export CROSS_COMPILE=aarch64-linux-gnu-
export CROSS_COMPILE_TA=aarch64-linux-gnu-
# 或者直接修改 build/Makefile 中的变量
# CROSS_COMPILE ?= /opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
undefined reference to __aeabi_* 这类符号找不到的问题。
3.4 编译并运行 OP-TEE
这一步最让人期待——看到终端里刷出 OP-TEE 的启动日志,那种成就感,我到现在还记得。
3.4.1 一键编译
cd build
make -j$(nproc) all
$(nproc) 会自动获取你的 CPU 核心数。第一次编译大概需要 10-20 分钟,取决于机器性能。
编译成功后,你会看到:
out/bin/目录下生成了 QEMU 启动脚本out/bios-qemu/包含引导固件out/rootfs/包含根文件系统(内置 OP-TEE 客户端和测试程序)
3.4.2 启动 QEMU 模拟器
# 启动 QEMU(两个终端模式)
make run-only
# 或者使用脚本直接启动
./out/bin/run-qemu.sh
你会看到两个终端窗口:
- 终端 1(UART0):Linux 内核启动日志,最终进入 shell 提示符
# - 终端 2(UART1):OP-TEE 安全世界日志,显示
I/TC:开头的调试信息
看到类似下面的输出,就说明 OP-TEE 跑起来了:
I/TC: OP-TEE version: 4.0.0 (gcc version 10.3.1 20210621) #1 Tue Jan 10 10:00:00 UTC 2024 aarch64
I/TC: Primary CPU initializing...
I/TC: SMC handler ready
I/TC: Core initialized
3.5 调试环境搭建
跑起来只是第一步。真正开发 TA 的时候,调试才是重头戏。我习惯用 GDB + QEMU 的调试 stub 组合。
3.5.1 启用 QEMU 调试模式
# 在 build 目录下,使用调试模式启动
make run-only DEBUG=1
# 或者手动添加 -s -S 参数
qemu-system-aarch64 \
-machine virt,secure=on \
-cpu cortex-a53 \
-smp 4 \
-s -S \ # -s 开启 GDB stub(端口 1234),-S 启动时暂停 CPU
-bios out/bios-qemu/bios.bin \
...
3.5.2 连接 GDB 调试器
另开一个终端:
# 启动 GDB,连接 QEMU
aarch64-linux-gnu-gdb \
-ex "target remote :1234" \
-ex "symbol-file out/optee_os/core/tee.elf" \
-ex "break tee_entry_std" \
-ex "continue"
这里解释一下:
target remote :1234:连接到 QEMU 的 GDB stubsymbol-file:加载 OP-TEE 内核的符号表,这样才能看到函数名和源码行号break tee_entry_std:在标准调用入口处下断点
tee_entry_std 和 tee_entry_fast 两个入口下断点。前者对应 CA 调用 TA 的普通请求,后者对应安全监控调用(比如切换世界)。这样能清晰看到 REE 和 TEE 之间的切换过程。
3.5.3 调试 TA 的常用命令
进入 GDB 后,这些命令我几乎每次都用:
| 命令 | 作用 |
|---|---|
info registers |
查看所有寄存器状态(特别是 scr_el3 和 spsr_el3) |
bt |
查看调用栈回溯 |
list |
显示当前断点附近的源码 |
p/x *(struct tee_ta_session *)0x... |
打印 TA 会话结构体内容 |
monitor system_reset |
从 GDB 重启 QEMU(不用退出再启动) |
CFG_TEE_CORE_DEBUG=y。这个配置会保留调试符号和优化级别(-O0),否则编译器优化后,源码行号和实际指令对不上。
3.6 验证环境是否正常
环境搭好了,跑个测试验证一下:
# 在 QEMU 的 Linux shell 中执行
# 列出已加载的 TA
tee-supplicant -d
# 运行 OP-TEE 自带的测试套件
xtest
# 运行示例 TA(比如 hello world)
optee_example_hello_world
如果 xtest 全部通过(或者大部分通过),恭喜你——环境搭建成功了!
3.7 常见问题与解决方案
我把这些年遇到的高频问题整理了一下:
| 问题 | 原因 | 解决方法 |
|---|---|---|
repo sync 卡住或报 443 |
网络问题,GitHub 被墙 | 使用代理或国内镜像源 |
编译报错 arm-linux-gnueabihf-gcc: not found |
工具链未下载或路径不对 | 重新执行 make toolchains |
| QEMU 启动后黑屏 | 缺少 secure=on 参数 |
检查 QEMU 命令行是否包含 -machine virt,secure=on |
| GDB 连接不上 QEMU | 端口被占用或 QEMU 未加 -s 参数 |
检查端口 1234 是否被占用,或改用 -gdb tcp::1234 |
xtest 报 TEEC_ERROR_NOT_SUPPORTED |
某些 TA 特性在 QEMU 上不支持 | 忽略该测试,或检查 CFG_* 配置是否开启 |