2. ARM TrustZone技术基础:TrustZone架构原理、安全世界与普通世界划分、异常模型、总线与内存隔离
TrustZone是ARM针对系统级安全提出的硬件隔离技术,其核心思想并非在操作系统层面增加安全模块,而是从处理器架构、总线互联、内存控制器到外设中断的完整硬件链路中,构建两个相互隔离的执行环境——安全世界(Secure World)与普通世界(Normal World)。在Cortex-M33上,TrustZone被实现为ARMv8-M架构的可选扩展,它不再依赖MMU(内存管理单元)级别的页表隔离,而是通过更轻量的SAU(安全属性单元)和IDAU(实现定义属性单元)完成内存与外设的安全属性划分。理解TrustZone的架构原理、世界切换机制、异常模型以及总线隔离方式,是后续实现安全启动、安全固件更新和可信运行环境的基础。
2.1 TrustZone架构核心原理
TrustZone的架构核心在于将处理器的工作状态划分为安全状态(Secure State)与非安全状态(Normal State),并通过一个称为NS位的状态标识来区分当前执行环境。在Cortex-M33中,这个NS位由处理器内部的特殊寄存器维护,当CPU处于安全世界时,NS位为0;处于普通世界时,NS位为1。与Cortex-A系列不同,Cortex-M33的TrustZone实现不依赖虚拟地址转换,而是直接对物理内存地址进行安全属性标记,因此其隔离粒度更细、切换延迟更低,非常适合资源受限的嵌入式场景。
安全属性的判定由两级硬件单元协同完成:IDAU(Implementation Defined Attribution Unit)和SAU(Security Attribution Unit)。IDAU由芯片厂商在流片时固定实现,定义了芯片内部每个内存区域和每个外设的默认安全属性,开发者无法修改。SAU则是一个可编程的单元,允许固件在运行时动态调整某些区域的安全属性。两者的判定逻辑为:当CPU发起一次内存访问时,硬件将IDAU和SAU的判定结果进行组合,最终确定该访问是安全访问还是非安全访问。如果非安全世界尝试访问被标记为安全的区域,总线会直接返回错误响应,阻止该访问。
| 特性 | IDAU(实现定义属性单元) | SAU(安全属性单元) |
|---|---|---|
| 实现方式 | 芯片厂商硬件固定 | 可编程(由安全固件配置) |
| 配置时机 | 流片时确定,不可修改 | 系统启动早期由安全固件初始化 |
| 作用范围 | 整个芯片内存映射 | 可配置的若干区域(通常8-16个) |
| 优先级 | 基础属性 | 可覆盖IDAU的部分区域 |
| 典型用途 | 固定安全ROM、安全外设 | 动态划分安全/非安全SRAM、Flash |
在Cortex-M33的实际应用中,IDAU通常将芯片内部的Flash和SRAM划分为两个物理区域:安全区域和非安全区域。例如,一颗具有2MB Flash和512KB SRAM的MCU,IDAU可能将前1MB Flash标记为安全,后1MB标记为非安全;SRAM则按256KB粒度划分。SAU则可以在这些粗粒度划分的基础上,进一步细分为更小的安全区域。例如,安全固件可以将非安全Flash中的某4KB区域重新标记为安全,用于存储密钥或关键配置参数。
2.2 安全世界与普通世界的划分机制
安全世界与普通世界的划分不仅体现在CPU状态上,还贯穿于内存、外设、中断和总线的每一个环节。从软件视角看,两个世界各自拥有独立的栈指针、异常向量表和系统控制寄存器。Cortex-M33为安全世界提供了独立的Secure Stack Pointer和Secure Vector Table,这意味着安全固件可以拥有完全独立的异常处理流程,不会受到普通世界异常的影响。
世界切换的入口是SG指令(Secure Gateway)。SG指令只能存放在被标记为安全的内存区域中,且该区域必须被配置为“安全调用入口”。当普通世界代码需要调用安全服务时,它通过一个特殊的函数调用流程:首先将参数放入寄存器,然后跳转到一个非安全可调用的安全函数入口(该入口地址被标记为SG指令)。CPU执行SG指令后,硬件自动完成以下操作:保存普通世界的上下文(包括返回地址、栈指针、状态寄存器),切换到安全栈,将NS位置0,然后跳转到安全异常向量表中对应的处理函数。安全函数执行完毕后,通过一条特殊的返回指令(如BXNS)回到普通世界,硬件自动恢复普通世界的上下文。
这种机制带来的一个重要特性是:普通世界无法直接访问安全世界的任何资源,包括安全内存、安全外设和安全寄存器。即使普通世界代码试图通过指令直接跳转到安全内存地址,也会因为安全属性不匹配而触发总线错误或HardFault异常。反过来,安全世界可以访问普通世界的所有资源,因为安全状态具有更高的权限。这种单向可见性设计,使得安全固件可以监控和管理普通世界的运行,而普通世界无法窥探安全世界的内部状态。
2.3 TrustZone异常模型与中断隔离
Cortex-M33的异常模型在传统ARMv8-M基础上增加了安全扩展。系统拥有两套独立的异常向量表:安全向量表(Secure Vector Table)和非安全向量表(Normal Vector Table)。当CPU处于安全世界时,异常从安全向量表中查找处理函数;处于普通世界时,从非安全向量表中查找。这种设计确保了安全世界的异常处理不会被普通世界的异常配置所干扰。
中断控制器(NVIC)也支持安全属性配置。每个中断源都可以被独立配置为安全中断或非安全中断。安全中断只能由安全世界的代码处理,即使普通世界尝试使能该中断,硬件也会忽略。非安全中断则可以被两个世界处理,但处理方式有所不同:如果CPU当前处于安全世界,非安全中断会被挂起,直到CPU切换到普通世界后才被响应。这种机制防止了普通世界的中断处理函数在安全世界上下文中执行,从而避免了潜在的安全漏洞。
异常模型中的另一个关键概念是安全异常优先级。Cortex-M33允许安全中断拥有比非安全中断更高的优先级,且安全中断可以抢占正在执行的非安全中断处理。这意味着即使普通世界正在处理一个关键任务,安全世界的高优先级中断(如安全定时器、安全通信接口)也能及时响应。在实际的安全启动场景中,安全固件通常会配置一个安全定时器,用于监控普通世界的启动超时,如果普通世界在规定时间内未能完成启动,安全定时器中断会强制接管控制权。
// 安全固件中配置SAU区域的典型代码(伪代码)
void sau_init(void) {
// 禁用SAU进行配置
SAU->CTRL = 0;
// 将0x10000000-0x1000FFFF(64KB SRAM)配置为安全区域
SAU->RNR = 0;
SAU->RBAR = 0x10000000;
SAU->RLAR = 0x1000FFFF | (SAU_RLAR_EN_MASK);
// 将0x00000000-0x0000FFFF(64KB Flash)配置为安全区域
SAU->RNR = 1;
SAU->RBAR = 0x00000000;
SAU->RLAR = 0x0000FFFF | (SAU_RLAR_EN_MASK);
// 启用SAU
SAU->CTRL = SAU_CTRL_EN_MASK;
}
2.4 总线与内存隔离机制
TrustZone的总线隔离通过AHB5总线和PPC(Peripheral Protection Controller)实现。AHB5总线在传统AHB协议基础上增加了安全事务标识,每个总线事务都携带一个安全属性位,用于标识该事务来自安全世界还是普通世界。总线互联逻辑根据这个安全属性位和目标的地址区域,决定是否允许该事务通过。
PPC是芯片级的外设保护控制器,它位于总线互联和外设之间,为每个外设提供独立的安全访问控制。PPC内部包含一组寄存器,用于配置每个外设的安全属性。例如,可以将UART0配置为安全外设,仅允许安全世界访问;将UART1配置为非安全外设,允许两个世界访问。当非安全世界尝试访问被PPC标记为安全的外设时,PPC会阻止该访问并返回错误响应。
内存隔离的最终效果是:安全世界可以访问所有内存和外设,普通世界只能访问被明确标记为非安全的区域。这种隔离不仅适用于CPU发起的访问,也适用于DMA等总线主设备。Cortex-M33的DMA控制器同样支持安全属性配置,可以限制DMA只能访问特定安全级别的内存区域,防止DMA被普通世界利用来读取安全内存。
| 隔离层级 | 实现方式 | 保护对象 | 典型配置 |
|---|---|---|---|
| CPU状态隔离 | NS位 + 独立栈/向量表 | 执行上下文 | 安全世界使用Secure SP |
| 内存隔离 | IDAU + SAU | Flash / SRAM | SAU划分安全/非安全区域 |
| 外设隔离 | PPC | UART / SPI / GPIO等 | 安全外设仅安全世界可访问 |
| 总线隔离 | AHB5安全事务标识 | 总线主设备访问 | DMA限制在非安全区域 |
| 中断隔离 | NVIC安全中断配置 | 中断源 | 安全定时器仅安全世界处理 |
在实际的Cortex-M33安全启动设计中,总线与内存隔离的配置顺序至关重要。系统上电后,CPU首先在安全世界运行,此时SAU尚未配置,所有内存默认按照IDAU的属性执行。安全固件必须在启动早期完成SAU和PPC的初始化,然后才能切换到普通世界。如果SAU配置过晚,普通世界可能在安全属性尚未建立时就开始运行,造成安全漏洞。
2.5 案例拆解:基于TrustZone的安全启动分区设计
以一个典型的物联网设备为例,该设备采用Cortex-M33内核MCU,具备2MB Flash和512KB SRAM,需要实现安全启动和固件防回滚功能。设备运行环境要求:安全固件负责验证非安全固件的签名和版本,非安全固件运行RTOS和应用程序。
初始现象与目标:设备在出厂后可能面临固件被篡改、回滚到旧版本等安全威胁。目标是通过TrustZone隔离,确保只有经过签名的固件才能被加载执行,且安全固件本身不可被非安全世界修改。
关键输入参数:Flash划分为4个区域:安全Bootloader(0x00000000-0x0001FFFF,128KB)、安全固件存储区(0x00020000-0x0003FFFF,128KB)、非安全固件区(0x00040000-0x001FFFFF,1.75MB)、非安全配置区(0x00200000-0x0020FFFF,64KB)。SRAM划分为安全数据区(0x20000000-0x20007FFF,32KB)和非安全数据区(0x20008000-0x2001FFFF,96KB)。
分析判断过程:根据TrustZone的隔离要求,安全Bootloader和安全固件存储区必须被IDAU或SAU标记为安全区域。由于IDAU在芯片出厂时已经固定,需要确认该MCU的IDAU是否将Flash前256KB标记为安全。如果IDAU只标记了前128KB为安全,则需要通过SAU将0x00020000-0x0003FFFF区域补充标记为安全。SRAM的安全区域划分同样需要SAU配置。
实际操作步骤:
- 在安全Bootloader启动早期,配置SAU,将安全固件存储区(0x00020000-0x0003FFFF)标记为安全,将非安全固件区(0x00040000-0x001FFFFF)标记为非安全。
- 配置PPC,将加密引擎、OTP、安全RTC等外设标记为安全外设,将UART、SPI、GPIO等通信外设标记为非安全外设。
- 安全Bootloader从安全Flash中加载安全固件,验证其签名后跳转执行。
- 安全固件通过SG指令切换到普通世界,启动非安全Bootloader。
- 非安全Bootloader从非安全固件区加载RTOS和应用程序,运行在普通世界。
结果对比与复盘:通过上述设计,即使非安全固件被完全逆向或篡改,攻击者也无法读取安全固件中的密钥,无法修改安全Bootloader的代码,也无法回滚到包含已知漏洞的旧版本固件。安全启动的信任链从芯片上电开始建立,一直延伸到非安全固件的加载。复盘经验表明,SAU区域的划分需要预留足够的余量,避免后续固件升级时安全区域不足;同时,PPC的配置需要与安全固件的功能需求匹配,避免将必要的通信外设误配置为安全外设,导致普通世界无法正常通信。