2. OP-TEE 开发环境搭建:QEMU 模拟器、ARM TrustZone 配置、源码编译与烧录

搭建一个可调试、可复现的 OP-TEE 开发环境是进入安全世界的第一步。本章将引导你完成从 QEMU 模拟器配置、ARM TrustZone 硬件虚拟化使能,到源码编译与镜像烧录的全流程。环境基于 ARMv8-A Foundation Model / QEMU,支持 REE + TEE 双世界 同时运行。

环境概览:主机 Ubuntu 22.04 LTS,交叉编译工具链 aarch64-linux-gnu,QEMU 7.2+,OP-TEE 4.2.0,Linux 6.1 LTS,TF-A 2.9。

2.1 环境依赖与工具链安装

首先安装构建 OP-TEE 所需的系统包和交叉编译工具链。以下命令在 Ubuntu 22.04 上验证通过:

sudo apt update
sudo apt install -y \
    git wget flex bison python3-pip python3-venv \
    libssl-dev libncurses-dev device-tree-compiler \
    xterm qemu-system-arm qemu-efi \
    gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
    gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
    cpio rsync unzip ccache
提示:建议使用 python3 -m venv 创建独立虚拟环境,避免 pip 包冲突。OP-TEE 构建脚本依赖 pyelftools 和 cryptodome。

2.2 QEMU 模拟器与 TrustZone 配置

QEMU 的 virt 平台原生支持 ARM TrustZone 扩展。关键配置参数如下:

参数 值 说明
-machine virt,secure=on secure=on 使能 TrustZone 安全扩展
-cpu cortex-a57 cortex-a57 ARMv8-A 64位核心,支持 EL3
-smp 2 2 核 至少 2 核以模拟多核安全中断
-m 1G 1 GB 内存分配,TZASC 分区用
-bios bl1.bin TF-A BL1 安全启动链入口

QEMU 启动时通过 -machine virt,secure=on 开启 TrustZone 后,CPU 自动获得 安全世界(EL3/S-EL1) 和 非安全世界(EL1/EL0) 的硬件隔离能力。下图展示了 QEMU 中 TrustZone 的地址空间划分:

QEMU virt 平台 TrustZone 地址空间划分 非安全世界 (REE) Linux 内核 + 用户态 EL1 / EL0 地址范围: 0x4000_0000 - 0x7FFF_FFFF 非安全中断 (IRQ) TZASC: 非安全可访问 安全世界 (TEE) OP-TEE + 安全驱动 S-EL1 / S-EL0 地址范围: 0x8000_0000 - 0xBFFF_FFFF 安全中断 (FIQ) TZASC: 安全可访问 EL3 监控模式 (TF-A / 安全监控调用 SMC)

2.3 源码获取与目录结构

OP-TEE 官方提供 repo 工具管理多仓库项目。推荐使用 manifest 方式统一拉取:

mkdir ~/optee-qemu && cd ~/optee-qemu
repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml
repo sync -j$(nproc)

同步完成后,关键组件目录如下:

目录 组件 版本
optee_os/ OP-TEE 内核 (TEE Core) 4.2.0
linux/ Linux 内核 (含 OP-TEE 驱动) 6.1
arm-trusted-firmware/ TF-A (ATF) 2.9
build/ 构建脚本与 Makefile -
out/ 编译产物 (镜像、dtb、rootfs) -

2.4 编译流程与关键参数

OP-TEE 的编译采用 分层构建 策略:先编译 TF-A,再编译 OP-TEE OS,最后编译 Linux 内核与文件系统。使用 build/ 目录下的 Makefile 一键构建:

cd build
make -j$(nproc) toolchains  # 首次需下载交叉工具链
make -j$(nproc) all         # 编译所有组件

关键编译参数说明:

参数 默认值 作用
PLATFORM vexpress-qemu_v8a 目标平台
O ../out 输出目录
DEBUG 0 调试模式 (1 开启)
CFG_TEE_CORE_LOG_LEVEL 2 日志等级 (0-4)
CFG_ARM64_core y 64位 TEE Core
注意:调试模式下 DEBUG=1 会关闭优化并增加符号表,镜像体积增大 3-5 倍,但支持 GDB 单步调试安全世界代码。生产环境务必关闭。

编译成功后,out/ 目录下生成以下关键镜像:

  • bl1.bin — TF-A 第一阶段 Bootloader
  • fip.bin — 包含 BL2、BL31、BL32 (OP-TEE)、BL33 (U-Boot) 的固件镜像包
  • tee-pager_v2.bin — OP-TEE 分页镜像
  • tee-pageable_v2.bin — OP-TEE 可分页部分
  • Image — Linux 内核镜像
  • vexpress-v2f-1xv7-ca53x2.dtb — 设备树
  • rootfs.cpio.gz — 初始 RAM 文件系统

2.5 启动 QEMU 与验证环境

使用 build/ 目录下的 QEMU 启动脚本:

cd build
make run-only  # 或直接运行 qemu 命令

启动后,QEMU 会打开两个终端窗口:

  • 串口 0 (UART0):非安全世界 Linux 控制台
  • 串口 1 (UART1):安全世界 OP-TEE 日志输出

在 Linux 控制台执行以下命令验证 OP-TEE 正常工作:

# 检查 OP-TEE 驱动是否加载
lsmod | grep optee

# 运行 xtest 测试套件
xtest

# 检查安全世界日志(UART1 窗口)
# 应看到 "OP-TEE: initialized" 和 "TEE-CORE: ..." 等输出

下图展示了 QEMU 启动后的双世界交互流程:

QEMU 启动流程与双世界交互 阶段 1: TF-A 启动 (BL1 → BL2 → BL31 → BL32 OP-TEE → BL33 U-Boot) 非安全世界 (REE) Linux 内核启动 加载 optee.ko 驱动 UART0 控制台 安全世界 (TEE) OP-TEE OS 初始化 注册安全中断 FIQ UART1 日志输出 SMC 调用 阶段 3: 验证 — 运行 xtest / 检查 /dev/tee0 / 查看 UART1 日志 GDB 调试: target remote :1234 (安全世界 + 非安全世界)

2.6 烧录与调试配置

QEMU 环境无需物理烧录,但为模拟真实嵌入式场景,我们使用 flash 镜像 方式加载固件。关键 QEMU 启动参数:

qemu-system-aarch64 \
    -machine virt,secure=on \
    -cpu cortex-a57 \
    -smp 2 \
    -m 1G \
    -bios bl1.bin \
    -semihosting \
    -serial stdio \
    -serial file:tee.log \
    -drive file=rootfs.ext4,if=none,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0 \
    -netdev user,id=net0 \
    -device virtio-net-device,netdev=net0 \
    -gdb tcp::1234
调试技巧:-gdb tcp::1234 开启 GDB 远程调试。在另一个终端运行 aarch64-linux-gnu-gdb vmlinux,然后 target remote :1234 即可同时调试 Linux 和 OP-TEE。

2.7 常见问题与排查

现象 原因 解决方案
QEMU 启动后无 UART1 输出 未正确映射串口或 OP-TEE 未初始化 检查 -serial file:tee.log 参数;确认 CFG_TEE_CORE_LOG_LEVEL ≥ 2
Linux 内核崩溃 设备树与内核版本不匹配 使用 make dtbs 重新编译设备树
xtest 测试失败 OP-TEE 驱动未加载或安全中断冲突 modprobe optee;检查 dmesg | grep optee
GDB 无法连接 QEMU 未开启 gdb 服务或端口被占用 确认 -gdb tcp::1234;netstat -tlnp | grep 1234
安全提醒:调试环境中的 OP-TEE 默认关闭了签名验证和加密保护。切勿将本环境配置直接用于生产或安全评估。生产部署必须启用 CFG_RPMB_FS=y 和 CFG_REE_FS=n。