2、核心硬件架构:安全世界与非安全世界、Monitor 模式、异常向量表重映射
好,我们正式开始啃 TrustZone 最核心的硬件架构。
说实话,很多做安全的朋友看了几年 ARM 文档,还是搞不清「安全世界」和「非安全世界」到底是怎么切来切去的。我当年第一次接触 TrustZone 时也踩过这个坑——以为它就是个软件层面的权限管理。后来被硬件狠狠教育了一顿,才明白这一切的根基,都在芯片的硬件总线设计里。
2.1 两个世界的物理隔离
TrustZone 把整个 SoC 的运行环境切成了两个「世界」:安全世界(Secure World)和非安全世界(Normal World)。注意,这不是虚拟化,不是进程级别的隔离,而是硬件总线级别的物理隔离。
每个世界都有自己的:
- 物理地址空间——安全世界的内存,非安全世界的 CPU 根本看不到
- 系统寄存器——比如 CP15 寄存器,两个世界各有一套
- 异常向量表——这个我们后面细说
- 外设访问权限——通过 TrustZone Address Space Controller (TZASC) 控制
核心要点:非安全世界的代码,永远无法直接访问安全世界的物理内存。这不是软件能绕过的——总线上的硬件信号(NS bit)会直接截断非法访问。
我曾在某款 IoT 芯片上见过一个案例:工程师在非安全世界写了个驱动,试图通过 DMA 直接读取安全世界的密钥存储区。结果总线直接返回全 F,连个异常都没触发——硬件静默地拒绝了。这就是物理隔离的威力。
2.2 Monitor 模式:两个世界的桥梁
两个世界完全隔离,那怎么切换?
答案就是 Monitor 模式。它是 ARM 架构里一个特殊的 CPU 模式,优先级比所有异常模式都高。你可以把它理解成「两个世界之间的看门人」。
Monitor 模式的核心职责:
- 保存上下文——把当前世界的寄存器状态存起来
- 切换 NS bit——修改 SCR.NS 位,告诉硬件「我要去另一个世界了」
- 恢复上下文——加载目标世界的寄存器状态
- 返回目标世界——执行 ERET 指令跳过去
我的习惯:在 Monitor 代码里,我通常会额外保存 SCTLR 和 ACTLR 寄存器。因为有些芯片在切换世界时,MMU 和缓存配置会不一致,不保存这些寄存器,回来时系统可能直接挂掉。
切换流程可以用下面这张图来理解:
2.3 异常向量表重映射
这里有个非常关键的设计:异常向量表重映射。
在标准 ARM 架构里,异常向量表通常放在 0x00000000 或 0xFFFF0000。但在 TrustZone 里,每个世界都有自己的异常向量表,而且它们可以放在不同的物理地址。
为什么需要重映射?
- 防止非安全世界劫持安全世界的异常——如果两个世界共用同一个向量表,非安全世界可以通过修改向量表来截获安全世界的中断
- 支持独立的异常处理逻辑——安全世界需要处理安全中断(FIQ),非安全世界处理普通中断(IRQ),逻辑完全不同
- 实现 Monitor 模式的异常路由——某些异常(如 SMC)必须路由到 Monitor 模式,而不是直接交给当前世界
我曾经踩过的坑:在某次移植中,我忘了配置 VBAR(Vector Base Address Register)的重映射。结果安全世界的 FIQ 异常直接跳到了非安全世界的向量表里,非安全世界的 OS 以为是自己的中断,随手就处理了——密钥材料直接暴露。从那以后,我每次初始化 TrustZone 都会反复检查 VBAR 的配置。
异常向量表重映射的配置方式:
| 寄存器 | 作用 | 所属世界 |
|---|---|---|
| VBAR | 设置当前世界的异常向量表基地址 | 安全 / 非安全各一份 |
| MVBAR | 设置 Monitor 模式的异常向量表基地址 | 仅 Monitor 模式 |
| SCR | 控制异常路由(FIQ/IRQ/EA 等) | 仅安全世界可写 |
异常路由的决策逻辑是这样的:
- CPU 触发异常
- 硬件检查当前 SCR 寄存器的配置
- 如果异常是 SMC,直接进入 Monitor 模式,查 MVBAR
- 如果异常是 FIQ 且 SCR.FIQ=1,进入 Monitor 模式
- 否则,根据当前世界的 VBAR 处理异常
我建议:在实际项目中,把安全世界的向量表放在安全 SRAM 里,把非安全世界的向量表放在 DDR 里。这样即使非安全世界被攻破,攻击者也改不了安全世界的向量表。
2.4 硬件隔离的基石:NS bit
说了这么多,其实 TrustZone 硬件隔离的核心就一个东西——NS bit(Non-Secure bit)。
这个 bit 会出现在:
- CPU 内部——SCR.NS 寄存器位,表示当前 CPU 在哪个世界
- AMBA 总线——每个总线事务都带一个 NS 信号
- 内存控制器——TZASC 根据 NS 信号决定是否允许访问
- 外设——TZPC 根据 NS 信号控制外设的访问权限
你想想看,一个非安全世界的程序,即使它知道安全内存的物理地址,它发出的总线事务 NS bit 是 1。TZASC 一看:NS=1,但你这块内存是安全区域——直接拒绝。就这么简单粗暴。
嗯,这里要注意:NS bit 是硬件自动设置的,软件改不了。CPU 在非安全世界时,所有总线事务的 NS bit 自动为 1。只有通过 Monitor 模式切换到安全世界后,NS bit 才会变成 0。
这就是 TrustZone 最底层的信任根基——不是靠软件检查,而是靠硬件信号。