1. TrustZone 基础概念:ARM TrustZone 技术概述、安全世界与普通世界隔离原理、Monitor 模式与 SMC 指令
各位同学,咱们今天正式开讲。这门课叫《国密算法在 TrustZone 中的可信执行》,第一站,先把 TrustZone 的底子打牢。
TrustZone 是什么?说白了,它是 ARM 在硬件层面做的一个“双系统隔离”方案。我当年刚接触这个技术时,第一反应是——这不就是给 CPU 装了个“双系统开关”吗?后来踩了不少坑才明白,这个开关背后藏着整套硬件隔离哲学。
1.1 为什么需要 TrustZone?
先问一个问题:你的手机里存着指纹、支付密钥、人脸数据,这些敏感信息放在哪?
传统做法是依赖操作系统来保护。但操作系统本身漏洞多,一旦被攻破,所有数据都裸奔。你想想看,Linux 内核几千万行代码,谁敢保证没漏洞?
TrustZone 的思路很直接:在硬件层面把 CPU 的运行环境切成两半。一半跑普通应用(比如、抖音),另一半跑安全敏感任务(比如指纹比对、密钥运算)。两边的内存、外设、中断都是隔离的。普通世界就算被攻破,也碰不到安全世界的一根毛。
核心思想:TrustZone 不是软件补丁,而是硬件强制隔离。安全世界和普通世界共享同一个物理 CPU,但逻辑上完全独立。
1.2 安全世界与普通世界:两个“平行宇宙”
ARM 把 CPU 的运行模式分为两种:普通世界(Normal World)和安全世界(Secure World)。我习惯把它们比作两栋楼——普通世界是公共办公区,安全世界是金库。两栋楼共用同一个地基(CPU),但门禁系统完全分开。
具体怎么隔离的?看这几个维度:
| 隔离维度 | 普通世界 | 安全世界 |
|---|---|---|
| 内存 | 只能访问普通内存区域 | 可访问安全内存区域(硬件保护) |
| 外设 | 只能访问非安全外设 | 可访问安全外设(如安全键盘、指纹传感器) |
| 中断 | 普通中断(IRQ) | 安全中断(FIQ)优先处理 |
| 缓存 | 非安全缓存行 | 安全缓存行(物理标记位区分) |
| 系统寄存器 | 部分寄存器不可访问 | 全部寄存器可访问 |
嗯,这里要注意:安全世界不是“超级管理员”。它不能随意访问普通世界的内存,反过来也不行。两边是平级的,只是安全世界拥有更多硬件权限。
我在项目中遇到过一个问题:普通世界的 DMA 控制器试图直接读取安全内存。结果呢?硬件直接返回全零数据,连个中断都不给。这就是硬件隔离的硬核之处——不信任任何软件。
1.3 Monitor 模式:两个世界的“守门人”
两个世界怎么切换?靠的是 Monitor 模式。这是 ARM 架构里一个特殊的 CPU 模式,权限比安全世界还高。
我画了一张图,帮你理解这个切换流程:
流程其实很简单:
- 普通世界执行 SMC 指令,触发安全监控调用。
- CPU 陷入 Monitor 模式,保存普通世界上下文。
- Monitor 根据调用号,切换到安全世界,恢复安全上下文。
- 安全世界执行完任务后,再通过 SMC 返回。
我曾经犯过一个低级错误:在普通世界直接写了个死循环调用 SMC,结果 Monitor 模式频繁切换,把安全世界的任务饿死了。后来加了限流机制才解决。嗯,Monitor 模式不是无限调用的,它本身也是资源。
1.4 SMC 指令:两个世界的“电话线”
SMC(Secure Monitor Call)是 ARM 架构里的一条特殊指令。它的作用就一个:请求世界切换。
用法很简单:
// 在普通世界发起安全调用
// r0 = 功能号,r1-r6 = 参数
// 执行后,CPU 进入 Monitor 模式
SMC #0
// 返回后,r0 包含返回值
但这里有个坑:SMC 不是函数调用。它会导致 CPU 暂停当前世界的所有执行,切换到另一个世界。如果你在中断处理函数里调用 SMC,后果很严重——中断响应延迟会飙升。
警告:SMC 调用是同步的。普通世界调用 SMC 后,会阻塞等待安全世界返回。不要在性能敏感路径上频繁调用 SMC。
我建议你在设计系统时,把 SMC 调用看作“远程过程调用”。每次调用都有开销:上下文保存、世界切换、权限检查。我在一个项目中测过,单次 SMC 调用大约耗时 1-5 微秒(取决于硬件平台)。如果每秒调用上万次,性能损失就很明显了。
1.5 硬件隔离的底层机制
TrustZone 的隔离不是靠软件,而是靠硬件总线。ARM 在系统总线上加了一个 NS(Non-Secure)位。每个内存访问、外设访问,总线都会检查这个位:
- NS=0:安全访问,可以访问安全区域。
- NS=1:非安全访问,只能访问非安全区域。
这个 NS 位是硬件强制设置的。普通世界永远只能发出 NS=1 的访问。安全世界可以发出 NS=0 或 NS=1 的访问(取决于具体需求)。
举个例子:安全世界的 Trusted OS 想读取普通世界的某个内存块,它可以临时把 NS 位设成 1 去读。但反过来,普通世界永远无法把 NS 位设成 0。
小技巧:调试 TrustZone 问题时,可以用 JTAG 读取总线上的 NS 位状态。如果发现安全内存被非安全访问命中,说明硬件配置有问题。
1.6 实际项目中的经验
最后分享几个我在项目中踩过的坑:
- 中断处理:安全世界的中断(FIQ)优先级高于普通世界(IRQ)。如果你的安全任务处理时间过长,普通世界的实时性会受影响。我一般把安全任务控制在 100 微秒以内。
- 缓存一致性:两个世界共享同一个缓存系统。切换世界时,Monitor 模式必须做缓存刷写操作。否则安全世界可能读到普通世界的脏数据。
- 调试难度:TrustZone 的调试比普通系统难得多。普通世界的调试器看不到安全世界的内存。我习惯在安全世界里加一个“调试通道”——通过共享内存把日志传给普通世界输出。
好了,这一章的基础概念就讲到这里。TrustZone 的核心就是三个东西:两个世界、一个 Monitor、一条 SMC 指令。后面的章节我们会在这个基础上,把国密算法塞进安全世界去执行。