1. 课程导论与实验环境搭建:UEFI与TEE联合启动的背景、应用场景、开发板选型与交叉编译环境
各位同学,欢迎来到《UEFI与TEE联合启动实战》的第一章。
我先说说为什么开这门课。做了这么多年固件安全,我见过太多设备在启动阶段被攻破的例子。UEFI负责把系统拉起来,TEE负责保护敏感数据,但这两者之间如果没打通,安全就成了空话。说白了,联合启动就是让UEFI在引导过程中,把TEE也初始化好,并建立一条可信的通信管道。
核心观点:UEFI与TEE联合启动不是锦上添花,而是现代设备安全的刚需。从手机到服务器,从IoT到车机,只要涉及可信启动和敏感数据隔离,就绕不开这个组合。
1.1 为什么需要UEFI与TEE联合启动?
我简单梳理一下背景。传统UEFI启动只负责加载OS,安全方面主要靠Secure Boot验签。但验签之后呢?OS内核一旦有漏洞,整个系统就裸奔了。TEE(可信执行环境)提供了一个与Rich OS隔离的安全世界,但TEE自己也需要一个可信的启动过程。
联合启动要解决三个核心问题:
- 信任链传递:UEFI验证TEE固件签名,确保TEE没有被篡改
- 内存隔离:在启动阶段就把安全世界和非安全世界的内存划分好
- 通信通道建立:UEFI和TEE之间需要一条安全的IPC通道
我在项目中遇到过最典型的情况:某款车机使用OP-TEE做数字钥匙管理,但UEFI启动时没有正确配置TZASC(TrustZone地址空间控制器),导致安全内存被非安全世界访问。嗯,这个问题排查了整整两周。从那以后,我对联合启动的每个细节都不敢马虎。
1.2 应用场景概览
你想想看,哪些场景需要UEFI+TEE联合启动?我列几个常见的:
| 场景 | UEFI角色 | TEE角色 | 联合启动关键点 |
|---|---|---|---|
| 移动支付终端 | 验证TEE镜像,配置安全内存 | 保护PIN输入和密钥 | 启动时建立安全通道 |
| 智能汽车网关 | 多级Secure Boot | 隔离CAN总线密钥 | TEE必须在CAN通信前就绪 |
| 云服务器 | UEFI测量启动 | 保护平台密钥 | TPM+TEE联合度量 |
| 工业PLC | 锁定启动设备 | 运行安全控制逻辑 | 实时性要求下的启动时序 |
说白了,只要你的设备需要「启动时就安全」,而不是「启动后再安全」,就离不开联合启动。
1.3 开发板选型:QEMU vs 树莓派
做嵌入式开发,选对硬件能省一半时间。我个人建议初学者从QEMU开始,等流程跑通了再上树莓派。为什么?
- QEMU(虚拟方案):调试方便,gdb直接连,断点随便打。不用烧录,不用串口线,不用怕变砖。适合学习阶段反复折腾。
- 树莓派3B/4B(物理方案):真实硬件,有TrustZone支持(BCM2837/2711),能验证实际的内存隔离效果。但调试手段有限,出问题只能靠串口日志。
我的建议:先用QEMU把EDK2和OP-TEE的联合启动跑通,理解整个流程。然后切换到树莓派,解决真实硬件上的时序和内存配置问题。我在带团队时都是这个路线,效果很好。
两种方案的对比:
| 特性 | QEMU | 树莓派3B/4B |
|---|---|---|
| 成本 | 免费 | 约200-400元 |
| 调试能力 | 强(gdb+图形化) | 弱(串口+GPIO) |
| TrustZone支持 | 模拟(完整) | 硬件(真实) |
| 启动速度 | 快(秒级) | 慢(分钟级) |
| 适合阶段 | 学习、原型验证 | 产品化、性能测试 |
1.4 搭建EDK2与OP-TEE交叉编译环境
好,到了动手环节。搭建环境是这门课的第一个门槛,我尽量把步骤说清楚。
1.4.1 整体架构
先看一张图,理解EDK2和OP-TEE在联合启动中的位置:
1.4.2 环境准备
我习惯在Ubuntu 20.04/22.04 LTS上搭建,64位系统。需要安装的依赖包如下:
# 基础编译工具
sudo apt update
sudo apt install -y build-essential git python3 python3-pip \
uuid-dev nasm acpica-tools
# ARM交叉编译链
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# QEMU
sudo apt install -y qemu-system-arm qemu-efi-aarch64
# 其他工具
sudo apt install -y device-tree-compiler flex bison \
libssl-dev libncurses-dev
注意:交叉编译链版本很重要。我曾经因为用了gcc-arm-none-eabi(那是给裸机用的)去编译OP-TEE,结果链接阶段各种报错。一定要用gcc-aarch64-linux-gnu,这是针对Linux用户空间的。
1.4.3 源码获取与目录结构
我们需要三个主要仓库。我个人习惯把它们放在同一个工作目录下:
mkdir ~/uefi-tee-lab && cd ~/uefi-tee-lab
# 1. EDK2(UEFI固件)
git clone https://github.com/tianocore/edk2.git --recursive
cd edk2
git checkout edk2-stable202311
# 2. OP-TEE(可信执行环境)
cd ..
git clone https://github.com/OP-TEE/optee_os.git
cd optee_os
git checkout 4.1.0
# 3. ARM Trusted Firmware(ATF/BL31)
cd ..
git clone https://github.com/ARM-software/arm-trusted-firmware.git
cd arm-trusted-firmware
git checkout v2.10
目录结构应该是这样的:
~/uefi-tee-lab/
├── edk2/ # UEFI固件源码
│ ├── ArmPkg/
│ ├── MdeModulePkg/
│ └── ...
├── optee_os/ # OP-TEE内核
│ ├── core/
│ ├── ta/
│ └── ...
└── arm-trusted-firmware/ # ATF(提供BL31)
├── bl31/
├── plat/
└── ...
1.4.4 编译OP-TEE
先编译OP-TEE,因为UEFI需要引用它的头文件。这里以QEMU virt平台为例:
cd ~/uefi-tee-lab/optee_os
# 设置交叉编译环境变量
export CROSS_COMPILE=aarch64-linux-gnu-
export PLATFORM=vexpress-qemu_armv8a
# 编译
make -j$(nproc)
# 编译产物
# out/arm-plat-vexpress/core/tee-pager_v2.bin (分页版本)
# out/arm-plat-vexpress/core/tee-raw.bin (非分页版本)
小技巧:第一次编译时加上CFLAGS=-g,这样后续调试能看到源码级别的信息。我每次做联合启动调试都这么干,省去了反汇编的麻烦。
1.4.5 编译EDK2(带OP-TEE支持)
EDK2需要配置成支持OP-TEE的版本。关键是要在dsc文件中打开对应的Feature:
cd ~/uefi-tee-lab/edk2
# 设置环境
export EDK_TOOLS_PATH=$(pwd)/BaseTools
export GCC5_AARCH64_PREFIX=aarch64-linux-gnu-
source edksetup.sh
# 编译BaseTools
make -C BaseTools
# 配置(以QEMU为例)
# 修改 ArmVirtPkg/ArmVirtQemu.dsc
# 在[Defines]段添加:
# DEFINE OP_TEE_SUPPORT = TRUE
# 编译
build -a AARCH64 -t GCC5 -p ArmVirtPkg/ArmVirtQemu.dsc \
-b DEBUG -D OP_TEE_SUPPORT=TRUE
# 编译产物
# Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd
1.4.6 编译ATF(BL31)
BL31是EL3的固件,负责切换安全世界和非安全世界:
cd ~/uefi-tee-lab/arm-trusted-firmware
make CROSS_COMPILE=aarch64-linux-gnu- \
PLAT=vexpress_qemu \
ARM_LINUX_KERNEL_AS_BL33=0 \
SPD=opteed \
BL32=../optee_os/out/arm-plat-vexpress/core/tee-pager_v2.bin \
all fip
# 编译产物
# build/vexpress_qemu/release/bl31.bin
# build/vexpress_qemu/release/fip.bin
重要:SPD=opteed这个参数不能写错。它告诉ATF使用OP-TEE的调度器。我见过有人写成SPD=optee(少了个d),结果BL31启动后找不到TEE,直接卡死在EL3。检查了三天才发现是拼写问题。
1.4.7 启动QEMU验证
所有东西编译好后,用QEMU启动验证:
qemu-system-aarch64 \
-machine virt,secure=on,virtualization=on \
-cpu cortex-a57 \
-smp 2 \
-m 1024 \
-bios ~/uefi-tee-lab/arm-trusted-firmware/build/vexpress_qemu/release/fip.bin \
-device loader,file=~/uefi-tee-lab/edk2/Build/ArmVirtQemu-AARCH64/DEBUG_GCC5/FV/QEMU_EFI.fd,addr=0x2000000 \
-serial stdio
如果一切正常,你会看到UEFI启动画面,然后进入UEFI Shell。在Shell中可以通过SMC命令测试与OP-TEE的通信。
1.5 常见问题与避坑
我把这些年踩过的坑整理一下:
- QEMU版本太旧:QEMU 5.0以下对TrustZone模拟不完整,建议用6.0以上。我目前用7.2,很稳定。
- EDK2子模块拉不下来:国内网络问题,建议用代理或者手动下载。git clone时加--recursive容易失败,可以分步拉取。
- OP-TEE编译报错:检查Python版本,需要3.6以上。另外pip install pyelftools这个包必须装。
- 启动后看不到UEFI:检查fip.bin的地址是否正确。QEMU的secure=on参数必须加,否则BL31不会运行。
一句话总结:环境搭建是这门课最枯燥但最关键的一步。把EDK2、OP-TEE、ATF三者的编译和链接关系理清楚,后续的联合启动开发就会顺畅很多。别怕出错,每个错误都是学习的机会。