3. TEE开发环境搭建:QEMU模拟器、OP-TEE源码编译、TA/CA开发工具链配置
说实话,搭建TEE开发环境这件事,我见过太多人卡在第一周了。不是编译报错,就是QEMU起不来,最后连个Hello World都没跑通。我自己刚接触OP-TEE那会儿,也折腾了整整两天才把环境跑顺。今天我把这些坑都给你指出来,争取让你一次搞定。
3.1 环境概览:我们要搭什么?
先看一张整体架构图,心里有个谱。
说白了,我们要在宿主机上交叉编译OP-TEE,然后在QEMU里跑起来。QEMU模拟一个ARMv8-A开发板,上面跑两个世界:绿色的是普通世界(Linux),紫色的是安全世界(OP-TEE OS)。中间靠SMC指令来回切换。
3.2 宿主机准备:别在依赖上翻车
我建议你用Ubuntu 20.04 LTS或22.04 LTS。其他发行版不是不行,但官方repo测试最充分的就是这两个版本。我曾经在Arch Linux上折腾了一下午,最后发现是工具链版本不匹配——嗯,从那以后我就老实了。
3.2.1 安装基础依赖
sudo apt-get update
sudo apt-get install -y \
git wget curl \
build-essential cmake \
python3 python3-pip \
uuid-dev libssl-dev \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
device-tree-compiler \
xterm expect \
android-tools-adb \
libgmp-dev libmpc-dev \
netcat-openbsd \
cpio unzip rsync \
bc bison flex \
libncurses5-dev \
libglib2.0-dev libpixman-1-dev
aarch64-linux-gnu-gcc --version 确认交叉编译器能用。很多人漏了这一步,编译到一半才发现工具链没装对。
3.2.2 Python环境
OP-TEE的构建脚本依赖Python 3。我个人习惯用虚拟环境隔离,避免污染系统Python。
sudo apt-get install -y python3-venv python3-pyelftools
python3 -m venv ~/optee-venv
source ~/optee-venv/bin/activate
pip install pycryptodomex
3.3 获取OP-TEE源码
OP-TEE官方维护了一个repo清单,用repo工具一把拉下来。你想想看,如果手动一个个git clone,光依赖链就能让你崩溃。
3.3.1 安装repo工具
mkdir -p ~/.local/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.local/bin/repo
chmod a+x ~/.local/bin/repo
export PATH=~/.local/bin:$PATH
3.3.2 同步源码
mkdir ~/optee
cd ~/optee
repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml
repo sync -j$(nproc)
这一步会下载大概2-3GB的代码,包括OP-TEE OS、Linux内核、QEMU、ATF、U-Boot、根文件系统等。网速慢的话,我建议你找个代理或者晚上挂机下载。
repo sync 即可,它会断点续传。千万别手贱删了重来。
3.4 编译OP-TEE完整镜像
源码同步完后,编译其实就一行命令。但这一行命令背后做了很多事情,我拆开给你看。
3.4.1 一键编译
cd ~/optee/build
make -j$(nproc) toolchains
make -j$(nproc) all
make toolchains会下载ARM交叉编译器,大概1GB左右。如果你之前已经装过aarch64-linux-gnu-gcc,它还是会下载自己的版本——这是OP-TEE官方验证过的版本,我建议你用它的。
3.4.2 编译产物说明
编译完成后,关键产物在~/optee/build/out/bin/目录下:
| 文件 | 说明 | 大小(约) |
|---|---|---|
Image |
Linux内核镜像 | 20MB |
tee-pager_v2.bin |
OP-TEE OS分页版本 | 256KB |
tee-pageable_v2.bin |
OP-TEE OS可分页部分 | 1MB |
bl31.bin |
ARM Trusted Firmware | 64KB |
rootfs.cpio.gz |
根文件系统 | 8MB |
flash.bin |
完整QEMU启动镜像 | 32MB |
flash.bin 是QEMU的启动镜像,包含了ATF、OP-TEE、U-Boot和Linux内核。你只需要这一个文件就能启动整个系统。
3.5 启动QEMU模拟器
编译完成后,启动QEMU同样是一行命令:
cd ~/optee/build
make run-only
这会弹出两个终端窗口:一个显示Linux控制台,一个显示OP-TEE的Secure Monitor输出。登录Linux终端,用户名root,密码为空。
3.5.1 验证OP-TEE是否正常运行
登录Linux后,执行:
# 检查OP-TEE设备节点
ls /dev/tee*
# 应该看到 /dev/tee0 和 /dev/teepriv0
# 检查OP-TEE驱动是否加载
dmesg | grep tee
# 应该看到 "optee: OP-TEE: revision 3.x" 之类的信息
# 运行官方测试用例
xtest
# 这会跑几百个测试用例,全部通过说明环境正常
我记得第一次跑xtest时,看到满屏的PASS,心里那块石头才落地。如果遇到FAIL,别慌,多半是编译时漏了某个依赖。
sudo apt-get install libsdl2-dev 重新编译QEMU即可。
3.6 TA/CA开发工具链配置
环境跑通后,我们得配置开发TA和CA的工具链。说白了,就是告诉编译器:头文件在哪、库文件在哪、链接脚本用哪个。
3.6.1 导出环境变量
我习惯写一个环境设置脚本,每次开发前source一下:
#!/bin/bash
# ~/optee/optee_env.sh
export TA_DEV_KIT_DIR=~/optee/build/out/export-ta_arm64
export OPTEE_CLIENT_EXPORT=~/optee/build/out/export-client
export HOST_CROSS_COMPILE=aarch64-linux-gnu-
export TA_CROSS_COMPILE=aarch64-linux-gnu-
export TEEC_EXPORT=$OPTEE_CLIENT_EXPORT
export PATH=$PATH:~/optee/build/out/bin
echo "OP-TEE开发环境已加载"
echo "TA开发目录: $TA_DEV_KIT_DIR"
echo "CA开发目录: $OPTEE_CLIENT_EXPORT"
3.6.2 工具链关键目录结构
我们来看看TA开发工具包里有什么:
~/optee/build/out/export-ta_arm64/
├── include/
│ ├── tee_internal_api.h # TEE内部API头文件
│ ├── tee_api.h # TEE核心API
│ ├── tee_api_defines.h # 宏定义和常量
│ ├── user_ta_header.h # TA头部定义
│ └── ...
├── lib/
│ ├── libutee.a # TEE用户空间库
│ ├── libutils.a # 工具函数库
│ └── libmpa.a # 多精度算术库
├── mk/
│ ├── ta_dev_kit.mk # TA构建规则
│ └── ...
└── scripts/
└── sign_ta.py # TA签名脚本
3.6.3 编写第一个TA
光说不练假把式。我们来写一个最简单的TA——什么都不做,只返回成功。
ta_entry.c:
#include <tee_internal_api.h>
#include <tee_internal_api_extensions.h>
#define TA_UUID { 0x12345678, 0x9abc, 0xdef0, \
{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 } }
TEE_Result TA_CreateEntryPoint(void)
{
DMSG("TA创建成功");
return TEE_SUCCESS;
}
void TA_DestroyEntryPoint(void)
{
DMSG("TA销毁");
}
TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types,
TEE_Param params[4], void **session_ctx)
{
DMSG("会话打开");
return TEE_SUCCESS;
}
void TA_CloseSessionEntryPoint(void *session_ctx)
{
DMSG("会话关闭");
}
TEE_Result TA_InvokeCommandEntryPoint(void *session_ctx,
uint32_t cmd_id, uint32_t param_types, TEE_Param params[4])
{
DMSG("命令调用: %u", cmd_id);
return TEE_SUCCESS;
}
Makefile:
# 引入TA构建规则
include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk
# TA UUID,必须与代码中的一致
BINARY = 12345678-9abc-def0-1234-56789abcdef0
# 源文件
SRCS = ta_entry.c
# 链接库
LIBS = -lutee -lutils
# 编译
all: $(BINARY).ta
clean:
rm -f *.o *.ta *.elf *.map
编译:
source ~/optee/optee_env.sh
make
编译成功后,会生成12345678-9abc-def0-1234-56789abcdef0.ta文件。这就是你的第一个TA。
3.7 部署TA到QEMU
TA编译出来后,得放到QEMU的根文件系统里才能跑。有两种方式:
3.7.1 方式一:重新打包根文件系统
# 解压根文件系统
cd ~/optee/build
mkdir rootfs_extract
cd rootfs_extract
zcat ../out/bin/rootfs.cpio.gz | cpio -idmv
# 复制TA到指定目录
cp /path/to/your.ta ./lib/optee_armtz/
# 重新打包
find . | cpio -o -H newc | gzip > ../out/bin/rootfs.cpio.gz
# 重新编译flash.bin
make all
3.7.2 方式二:通过共享目录(我推荐这个)
开发阶段频繁改代码,每次都重新打包太慢了。我一般用QEMU的9p共享目录:
# 在宿主机创建共享目录
mkdir ~/optee/shared
# 修改QEMU启动脚本,添加 -virtfs 参数
# 编辑 ~/optee/build/qemu_v8.mk,找到 QEMU_VIRTFS 相关配置
# 在QEMU Linux中挂载共享目录
mkdir /mnt/shared
mount -t 9p -o trans=virtio,version=9p2000.L hostshare /mnt/shared
# 从共享目录复制TA到TEE目录
cp /mnt/shared/your.ta /lib/optee_armtz/
3.8 常见问题与排错
我把这些年遇到的高频问题整理了一下:
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
| QEMU启动后黑屏 | 缺少SDL或GTK库 | sudo apt-get install libsdl2-dev libgtk-3-dev |
| 编译报错"undefined reference to ..." | 链接库顺序不对 | 调整Makefile中LIBS顺序,-lutee放最后 |
| TA加载失败"TEEC_ERROR_ITEM_NOT_FOUND" | TA签名问题或路径不对 | 检查TA是否在/lib/optee_armtz/下,重新签名 |
| xtest卡住不动 | QEMU内存不足 | 增加QEMU内存:make run-only QEMU_MEM=2048 |
| repo sync失败 | 网络问题或Git版本过低 | 升级Git到2.20+,或使用代理 |
3.9 开发效率小技巧
最后分享几个我自己的习惯:
- 别名加速:在
~/.bashrc里加一行alias optee='source ~/optee/optee_env.sh',省得每次敲一长串。 - 增量编译:只修改TA代码时,
make就够了,别跑make all。后者会重新编译整个OP-TEE。 - 日志调试:TA里多用
DMSG()打印日志,QEMU的Secure Monitor窗口会显示这些输出。比gdb轻量多了。 - 版本锁定:把
~/optee目录用git管理起来,每次修改前commit一下。出了问题可以快速回退。