3. TEE开发环境搭建:QEMU模拟器、OP-TEE源码编译、TA/CA开发工具链配置

说实话,搭建TEE开发环境这件事,我见过太多人卡在第一周了。不是编译报错,就是QEMU起不来,最后连个Hello World都没跑通。我自己刚接触OP-TEE那会儿,也折腾了整整两天才把环境跑顺。今天我把这些坑都给你指出来,争取让你一次搞定。

3.1 环境概览:我们要搭什么?

先看一张整体架构图,心里有个谱。

OP-TEE 开发环境整体架构 主机开发环境(Ubuntu 20.04/22.04) 交叉编译工具链 | OP-TEE源码 | TA/CA SDK | 构建系统 QEMU 模拟器(ARMv8-A 平台) 模拟 FVP 基础平台 | 提供 UART/中断/内存控制器 REE(非安全侧) Linux Kernel + 根文件系统 CA(Client Application) OP-TEE Client API (libteec) TEE(安全侧) OP-TEE OS (Trusted OS) TA(Trusted Application) TEE Internal API Monitor Mode(SMC 切换)

说白了,我们要在宿主机上交叉编译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 过程中如果网络中断,重新执行 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,别慌,多半是编译时漏了某个依赖。

避坑指南:QEMU窗口如果显示"Could not initialize SDL",说明缺少SDL库。执行 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/
我的经验:开发阶段用共享目录,调试通过后再重新打包根文件系统。这样每次修改TA只需要重新编译,然后cp一下就行,省了重新编译整个镜像的时间。

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+,或使用代理
重要提醒:TA的UUID必须和Makefile中的BINARY一致,否则OP-TEE会拒绝加载。我曾经因为UUID大小写问题排查了半小时——嗯,从那以后我都是直接复制粘贴。

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一下。出了问题可以快速回退。