2. TEE核心架构:ARM TrustZone 基础
好,我们正式开始聊 TEE 的核心架构。这一章我打算从 ARM TrustZone 讲起。为什么?因为它是目前移动设备、IoT 设备里最主流的硬件隔离方案。你想想看,手机里跑着、支付宝、银行 App,这些敏感数据怎么保护?TrustZone 就是答案之一。
我个人习惯把 TrustZone 理解成「一颗芯片里的两个世界」。嗯,这个比喻很关键。
2.1 安全世界与普通世界的划分
ARM 在 Cortex-A 系列处理器里引入了一个概念:两个虚拟的处理器核。它们共享同一个物理核,但运行在不同的安全级别上。
- 普通世界(Normal World):跑你的 Android、Linux、RTOS。所有 App、内核、驱动都在这里。这个世界默认不可信。
- 安全世界(Secure World):跑可信操作系统(如 OP-TEE、Trusty)。专门处理密钥、指纹比对、支付认证等敏感操作。
这两个世界是硬件强制隔离的。普通世界的代码无法直接访问安全世界的内存和寄存器。我在项目中遇到过有人试图通过 DMA 来绕过这个隔离——嗯,结果当然是失败了,因为 TrustZone 连总线访问都做了过滤。
核心原则:普通世界永远不能直接读写安全世界的资源。这是 TrustZone 的底线。
那它们怎么切换呢?这就引出了下一个关键角色。
2.2 Monitor 模式与 SMC 指令
两个世界之间不能直接跳转。需要一个「看门人」——Monitor 模式。
Monitor 模式是 ARM 架构里一个特殊的异常等级(EL3)。它运行着一小段固件,通常叫 ATF(ARM Trusted Firmware)。它的职责很简单:
- 接收切换请求
- 保存当前世界的上下文(寄存器、状态)
- 恢复目标世界的上下文
- 跳转执行
触发这个切换的指令就是 SMC(Secure Monitor Call)。普通世界执行 SMC #0,CPU 就会陷入 Monitor 模式,由 ATF 决定是否放行。
避坑指南:我曾经见过一个团队在普通世界直接调用 SMC 传了一个巨大的缓冲区地址,结果安全世界没做边界检查,直接读越界了。记住,SMC 只是通道,不是安全保证。参数校验必须在安全世界做。
2.3 两种切换场景
实际工作中,切换分两种:
| 场景 | 触发方式 | 典型用途 |
|---|---|---|
| 普通世界 → 安全世界 | SMC 指令 | App 请求加解密、指纹比对 |
| 安全世界 → 普通世界 | ERET 指令(异常返回) | 安全服务完成,返回结果 |
注意,安全世界也可以主动调用普通世界的函数吗?理论上可以,但实践中很少这么做。因为一旦安全世界主动暴露了调用接口,攻击面就扩大了。我建议你设计时坚持「单向调用」原则:普通世界发起请求,安全世界响应。
2.4 内存隔离与 TZASC
光有 CPU 隔离还不够。内存怎么办?普通世界的恶意程序能不能直接扫描物理内存,找到安全世界的数据?
ARM 提供了 TZASC(TrustZone Address Space Controller)。它是一块硬件,挂在内存总线前面。TZASC 把物理内存分成多个区域,每个区域可以配置为:
- 安全:只有安全世界的访问能通过
- 非安全:两个世界都能读,但普通世界不能写安全区域
说白了,TZASC 就是内存的「门禁系统」。我在调试一个支付芯片时,遇到过 TZASC 配置错误导致安全世界崩溃的情况——因为安全世界自己访问了一块被标记为「非安全」的内存。嗯,这种问题最难查,因为错误日志都写不进去。
警告:TZASC 的配置必须在安全世界启动早期完成,且之后不能再被普通世界修改。否则攻击者可以通过修改 TZASC 寄存器来关闭内存隔离。
2.5 整体架构图
下面这张图展示了 TrustZone 的核心架构。我把它画成了两个世界 + Monitor 的经典三层结构。
2.6 启动顺序与信任根
TrustZone 不是一上电就自动分好两个世界的。启动顺序很关键:
- ROM 代码:芯片上电,先执行固化在 ROM 里的启动代码。这是信任根(Root of Trust)。
- BL1(Boot Loader 1):加载 ATF 的第一阶段,初始化安全世界的基本环境。
- BL2(Boot Loader 2):加载安全世界操作系统(如 OP-TEE)。
- BL31(EL3 Monitor):启动 Monitor 固件,准备好 SMC 处理。
- BL32(Secure OS):安全世界操作系统启动完成。
- BL33(Normal World):最后才启动普通世界的 U-Boot 和 Linux。
为什么安全世界先启动?因为普通世界不可信,不能让它先初始化内存控制器——否则它可以篡改安全世界的内存布局。我调试过一个板子,就是因为启动顺序搞反了,安全世界连自己的栈都分配不了。
关键点:信任链必须从硬件 ROM 开始,逐级验证签名。任何一级被篡改,后续所有层级都不安全。
2.7 实际项目中的常见误区
最后,我总结几个我在项目中踩过的坑,希望能帮你少走弯路:
- 误区一:认为 SMC 调用是安全的。SMC 只是通道,参数校验、边界检查、权限验证必须在安全世界重新做。
- 误区二:忽略缓存一致性。两个世界共享同一块物理内存时,必须处理缓存同步。我见过安全世界解密完数据,普通世界读到的还是旧缓存。
- 误区三:TZASC 配置太宽松。有些人图省事,把所有内存都配成「安全」,结果普通世界直接崩溃。正确做法是只给安全世界分配最小必要内存。
- 误区四:中断处理不当。安全世界执行敏感操作时,如果被普通世界的中断打断,可能导致上下文泄露。建议在安全世界关键路径上屏蔽中断。
嗯,TrustZone 的基础就这些。它不复杂,但细节很多。你只要记住「两个世界 + 一个看门人」这个模型,后面理解其他 TEE 方案会轻松很多。