2、开发环境搭建:QEMU 模拟器、ARM 开发板、OP-TEE 源码编译与部署

说实话,搭建开发环境这件事,看着文档多,其实套路就那几个。我当年第一次搞 OP-TEE 时,光编译就卡了两天——后来发现是工具链版本没对齐。这一章咱们就把 QEMU 模拟器和真实开发板的环境一次讲透。

2.1 整体环境概览

先看一张图,把整个环境的关系理清楚。你想想看,OP-TEE 跑在安全世界里,普通 Linux 跑在非安全世界,中间靠一个叫「Monitor」的桥梁切换。QEMU 模拟的就是这个硬件平台。

OP-TEE 开发环境架构图 OP-TEE 开发环境架构 REE(非安全世界) Linux 内核 + 用户态 CA (Client App) OP-TEE Driver TEE Client API libteec TEE(安全世界) OP-TEE OS + Trusted Apps TA (Trusted App) OP-TEE Core TEE Internal API libutee Monitor QEMU 模拟整个硬件平台,包含 REE 和 TEE 两侧

核心要点:开发环境 = 交叉编译工具链 + OP-TEE 源码 + QEMU/开发板。三者缺一不可。

2.2 主机环境准备

我建议你用 Ubuntu 20.04 或 22.04 LTS。为什么?因为 OP-TEE 官方 CI 主要在这两个版本上跑,坑最少。我曾经试过 Arch Linux,结果依赖包版本对不上,折腾了一整天。

2.2.1 安装必要工具

# 基础编译工具
sudo apt-get update
sudo apt-get install -y \
    git wget make gcc g++ \
    python3 python3-pip \
    device-tree-compiler \
    flex bison \
    libssl-dev libncurses-dev \
    xterm

# 串口调试工具
sudo apt-get install -y picocom minicom

小技巧:装完记得检查 gcc 版本。我遇到过 gcc-9 编译 OP-TEE 3.x 报错的情况,换成 gcc-8 就好了。用 gcc --version 确认一下。

2.2.2 交叉编译工具链

ARM 开发板上的程序不能在 x86 主机上直接跑,得用交叉编译器。说白了,就是在你的电脑上编译出 ARM 能运行的二进制文件。

目标平台 工具链名称 下载方式
QEMU (arm32) arm-linux-gnueabihf- apt install gcc-arm-linux-gnueabihf
QEMU (arm64) aarch64-linux-gnu- apt install gcc-aarch64-linux-gnu
真实开发板 arm-none-eabi- ARM 官网下载或 Linaro 预编译包

我个人习惯把工具链路径加到 ~/.bashrc 里,省得每次 export:

export PATH=$PATH:/usr/bin
export CROSS_COMPILE=aarch64-linux-gnu-
export CC=${CROSS_COMPILE}gcc

2.3 获取 OP-TEE 源码

OP-TEE 项目用 repo 工具管理多个仓库。嗯,这里要注意:repo 是 Google 开发的,专门用来管理多 Git 仓库的项目。

# 安装 repo
mkdir -p ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
export PATH=~/bin:$PATH

# 同步 OP-TEE 源码(以 3.20.0 为例)
mkdir -p ~/optee
cd ~/optee
repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml -b 3.20.0
repo sync -j4

注意:repo sync 会下载大约 2-3 GB 数据,网络不好的话建议用代理或夜间下载。我当年在学校宿舍同步了一整晚才完事。

同步完成后,目录结构大概是这样的:

optee/
├── build/          # 编译脚本和 Makefile
├── optee_os/       # OP-TEE 内核源码
├── optee_client/   # 客户端库 (libteec)
├── optee_examples/ # 示例 TA/CA
├── linux/          # Linux 内核
├── trusted-firmware-a/  # ARM Trusted Firmware
└── qemu/           # QEMU 源码

2.4 QEMU 模拟器编译与运行

QEMU 模拟器是最方便的调试环境。你不需要买开发板,一台电脑就能跑。我刚开始学 OP-TEE 时,90% 的调试都是在 QEMU 上完成的。

2.4.1 一键编译

OP-TEE 的 build 目录提供了 Makefile,一条命令就能编译整个系统:

cd ~/optee/build
make -j$(nproc) all

编译完成后,生成的文件在 out/bin/ 目录下:

文件 说明
flash.bin 包含 BL1/BL2/BL31/BL32 的完整固件镜像
Image Linux 内核镜像
rootfs.cpio.gz 根文件系统(包含 CA 和 TA)
tee-pager_v2.bin OP-TEE OS 镜像

2.4.2 启动 QEMU

make run-only

这条命令会启动两个终端窗口:一个跑 Linux 终端(非安全世界),一个跑 OP-TEE 终端(安全世界)。你可以在 Linux 终端里运行 CA 程序,调用 TA 服务。

调试技巧:如果想看 OP-TEE 的启动日志,在启动 QEMU 时加上 CONSOLE=1:make run-only CONSOLE=1。这样所有日志会打印在当前终端,方便抓取。

2.5 ARM 开发板环境搭建

QEMU 虽好,但真实硬件上的行为有时不一样。我记得有一次在 QEMU 上跑得好好的 TA,放到 Hikey 960 上就崩溃了——原因是内存布局差异。所以,最终还是要上真板子。

2.5.1 支持的开发板

OP-TEE 官方支持以下开发板:

  • Hikey 960 / Hikey 620:华为麒麟芯片,最常用的 OP-TEE 开发板
  • Raspberry Pi 3/4:便宜,但性能一般
  • STM32MP1:工业级,适合产品原型
  • Xilinx Zynq UltraScale+:FPGA + ARM,适合定制

2.5.2 烧录流程(以 Hikey 960 为例)

烧录分两步:先烧 bootloader,再烧系统镜像。

# 1. 进入 fastboot 模式
# 按住 Volume Down 键,然后按 Power 键

# 2. 烧录分区
sudo fastboot flash ptable prm_ptable.img
sudo fastboot flash xloader hisi-sec_xloader.img
sudo fastboot flash fastboot l-loader.bin
sudo fastboot flash fip fip.bin

# 3. 烧录系统
sudo fastboot flash boot boot.img
sudo fastboot flash system system.img
sudo fastboot flash userdata userdata.img

警告:烧录前一定要备份原始固件!我曾经手滑烧错了分区,板子直接变砖,最后用 JTAG 才救回来。血的教训。

2.6 验证环境是否正常

环境搭好了,怎么知道对不对?跑一个简单的 TA 调用试试。

# 在 QEMU Linux 终端里执行
# 进入示例目录
cd /usr/bin

# 运行 hello world TA
./optee_example_hello_world

如果看到类似下面的输出,说明环境正常:

Prepare session with TA
TA invoke: hello world
Success

验证清单:

  • ☐ QEMU 能正常启动,两个终端窗口都出现
  • ☐ Linux 终端能登录(用户名 root,无密码)
  • ☐ 运行 tee-supplicant -d 能看到守护进程在运行
  • ☐ 示例 TA 能正常调用并返回结果

2.7 常见问题与避坑

这里列几个我踩过的坑,你遇到了直接对照排查:

问题 原因 解决方法
编译报错 undefined reference to __aeabi_* 工具链不完整或版本不对 重新安装 gcc-arm-linux-gnueabihf
QEMU 启动后黑屏 缺少 xterm 或显示配置错误 安装 xterm,检查 DISPLAY 环境变量
TA 调用返回 TEEC_ERROR_NOT_SUPPORTED TA UUID 未注册或路径不对 检查 /lib/optee_armtz/ 下是否有对应 .ta 文件
烧录后板子无法启动 分区表或 bootloader 损坏 重新烧录原始固件,确认镜像文件完整

我的习惯:每次修改源码后,先 make clean 再 make。增量编译有时会留下旧的目标文件,导致奇怪的问题。干净编译虽然慢点,但省去了排查时间。