第二章 ARM架构基础:处理器模式、异常模型、内存管理单元(MMU)与系统寄存器概览

TrustZone技术的实现深度依赖于ARM处理器的底层架构机制。要真正理解TEE(可信执行环境)如何隔离安全世界与普通世界,必须先掌握处理器模式(Processor Modes)、异常模型(Exception Model)、内存管理单元(MMU)以及系统寄存器(System Registers)这四大核心支柱。本章将逐一拆解这些基础概念,并重点说明它们与TrustZone扩展之间的直接关联,为后续深入TEE实现打下坚实的硬件认知基础。

ARM架构核心机制与TrustZone关联图 ARM CPU Core Cortex-A 系列 处理器模式 • USR / SYS • FIQ / IRQ • SVC / ABT / UND • MON (TrustZone) • HYP (虚拟化) 异常模型 • 异常向量表 • 异常类型与优先级 • 异常返回机制 • SMC 指令触发 MMU 内存管理 • 页表转换 (VA→PA) • TLB 缓存 • 内存属性 (XN/RO) • NS位地址空间隔离 • 安全/非安全页表 系统寄存器 • SCR (安全配置) • VBAR (向量基址) • SCTLR (系统控制) • TTBR0/1 (页表基址) TrustZone 扩展 安全世界 (S) ↔ 普通世界 (NS) 切换 核心机制:SCR.NS + SMC + 安全MMU 图:ARM处理器模式、异常、MMU与系统寄存器协同支撑TrustZone隔离

上图展示了ARM架构中四个核心机制与TrustZone扩展之间的协同关系。处理器模式定义了CPU当前的特权级别和运行上下文;异常模型提供了事件响应和模式切换的通道;MMU负责虚拟地址到物理地址的转换以及内存访问权限控制;系统寄存器则保存了影响全局的配置状态。TrustZone正是通过在这些机制中引入"安全"与"非安全"两个世界的概念,实现了硬件级别的隔离。

2.1 处理器模式:特权级别与TrustZone世界切换

2.1.1 核心原理:ARM处理器模式全景

ARM处理器(Cortex-A系列)定义了多种处理器模式,每种模式对应不同的特权级别和寄存器组。在ARMv7-A架构中,共有七种基本模式:用户模式(USR)、快速中断模式(FIQ)、普通中断模式(IRQ)、管理模式(SVC)、中止模式(ABT)、未定义指令模式(UND)和系统模式(SYS)。其中,USR模式是唯一的非特权模式,应用程序运行于此;其余模式均为特权模式,供操作系统内核、中断处理程序等使用。

TrustZone扩展在此基础上增加了两个关键模式:监控模式(MON)和安全模式(S)。监控模式是唯一可以在安全世界和普通世界之间切换的模式,它拥有最高特权级别(PL3)。安全世界运行TEE内核(如OP-TEE、Trusty),普通世界运行Rich OS(如Linux、Android)。两个世界各自拥有独立的处理器模式集合,但共享同一个物理CPU核心。

模式名称特权级别典型用途TrustZone关联
USRPL0应用程序普通世界用户态
SYSPL1操作系统内核普通世界内核态
SVCPL1系统调用、复位普通世界内核入口
IRQ/FIQPL1中断处理两个世界各自独立
ABT/UNDPL1异常处理两个世界各自独立
MONPL3世界切换TrustZone核心模式
HYPPL2虚拟化可选,与TZ协同

2.1.2 判断方法:如何识别当前处理器模式

在ARM架构中,可以通过读取CPSR(当前程序状态寄存器)的低5位(M[4:0])来判断当前处理器模式。例如,M[4:0] = 0b10000表示USR模式,0b10011表示SVC模式,0b10110表示MON模式。在TEE开发中,经常需要确认当前代码运行在安全世界还是普通世界,此时可以检查SCR(安全配置寄存器)的NS位:NS=0表示安全世界,NS=1表示普通世界。

// 读取CPSR并解析模式
MRS R0, CPSR
AND R0, R0, #0x1F
// R0 = 0b10011 (SVC模式) 或 0b10110 (MON模式)

// 检查当前世界状态
MRC p15, 0, R0, c1, c1, 0  // 读取SCR
TST R0, #0x01               // 测试NS位
BNE normal_world            // NS=1,普通世界
B secure_world              // NS=0,安全世界

2.1.3 实战场景:TEE世界切换的完整流程

当普通世界需要调用安全世界服务时(如密钥生成、加解密操作),必须通过SMC(安全监控调用)指令触发世界切换。具体流程如下:

  1. 普通世界内核将参数放入寄存器(R0-R7),执行SMC指令。
  2. CPU切换到MON模式,保存普通世界上下文(包括CPSR、寄存器组)。
  3. 监控器固件(Monitor Firmware)检查SCR.NS位,确认切换方向。
  4. 监控器加载安全世界上下文,设置SCR.NS=0,切换到安全世界SVC模式。
  5. TEE内核处理请求,完成后再次执行SMC返回普通世界。

关键点:世界切换的开销约为几百个时钟周期,频繁切换会严重影响性能。因此,TEE设计时应尽量减少跨世界调用次数,采用批量处理或共享内存机制优化。

2.1.4 常见误区与总结

  • 误区1:认为TrustZone只是软件层面的隔离。实际上,它依赖硬件对处理器模式、MMU、中断控制器等进行了全面扩展。
  • 误区2:混淆MON模式与SVC模式。MON模式是唯一能切换世界的模式,而SVC模式只是安全世界或普通世界内部的异常入口。
  • 误区3:忽略FIQ在TEE中的特殊用途。在TrustZone设计中,FIQ通常被分配给安全世界使用,以确保即使普通世界被攻击,安全中断仍能及时响应。

2.2 异常模型:事件响应与向量表

2.2.1 核心原理:ARM异常向量表与优先级

ARM处理器将异常分为七类:复位(Reset)、未定义指令(Undefined Instruction)、SVC(系统调用)、预取中止(Prefetch Abort)、数据中止(Data Abort)、IRQ和FIQ。每个异常对应向量表中的固定偏移地址。在TrustZone环境中,安全世界和普通世界各自拥有独立的向量表,通过VBAR(向量基址寄存器)指向不同的内存位置。

异常优先级从高到低依次为:复位 > FIQ > IRQ > 数据中止 > 预取中止 > SVC > 未定义指令。当多个异常同时发生时,CPU按照优先级顺序处理。在TEE中,FIQ被配置为安全中断,确保安全世界的关键操作(如密钥管理)不被普通世界的频繁中断干扰。

2.2.2 判断方法:异常类型识别与处理流程

当异常发生时,CPU自动完成以下操作:保存CPSR到SPSR(保存的程序状态寄存器)、保存返回地址到LR、设置CPSR模式位、跳转到向量表对应入口。开发者在编写异常处理程序时,需要根据LR偏移量判断具体异常类型,并区分来自安全世界还是普通世界。

// 异常向量表示例(ARMv7-A)
.section .vectors, "ax"
B   reset_handler        // 0x00 复位
B   undef_handler        // 0x04 未定义指令
B   svc_handler          // 0x08 SVC
B   pabt_handler         // 0x0C 预取中止
B   dabt_handler         // 0x10 数据中止
NOP                      // 0x14 保留
B   irq_handler          // 0x18 IRQ
B   fiq_handler          // 0x1C FIQ

2.2.3 实战场景:SMC异常处理与TEE入口

SMC指令在ARM架构中被定义为触发监控模式异常的专用指令。当CPU执行SMC时,无论当前处于安全世界还是普通世界,都会切换到MON模式。监控器固件通过解析SMC功能ID(在R0寄存器中传递)来决定下一步操作:

  • 功能ID 0x0:标准TEE调用,转发给安全世界TEE内核。
  • 功能ID 0x1:安全世界返回请求,恢复普通世界上下文。
  • 功能ID 0x2:CPU电源管理操作。

经验提示:在开发TEE时,建议将SMC处理程序放在独立的代码段中,并确保该段内存被标记为安全属性。同时,监控器固件应尽可能精简,减少攻击面。

2.2.4 常见误区与总结

  • 误区1:认为所有异常都会触发世界切换。实际上,只有SMC异常和部分FIQ会触发切换,普通IRQ仍由当前世界自行处理。
  • 误区2:忽略向量表对齐要求。ARM要求向量表按32字节对齐,且VBAR寄存器必须指向正确的基址。
  • 误区3:在安全世界中使用普通世界的中断处理逻辑。两个世界的中断控制器配置(GIC)是独立的,必须分别初始化。

2.3 内存管理单元(MMU):地址转换与安全隔离

2.3.1 核心原理:MMU与TrustZone地址空间

MMU负责将虚拟地址(VA)转换为物理地址(PA),并控制内存访问权限。在TrustZone架构中,MMU的页表项(PTE)增加了一个关键的NS位,用于标记该页属于安全世界还是普通世界。安全世界可以访问所有内存(包括标记为NS的页面),而普通世界只能访问NS=1的页面。这种硬件级别的隔离确保了安全世界的数据无法被普通世界直接读取。

ARMv7-A使用两级页表结构:一级页表(L1)包含1MB或2MB的段描述符,二级页表(L2)包含4KB或64KB的页描述符。TTBR0和TTBR1分别指向两个不同的页表基址,通常TTBR0用于用户空间,TTBR1用于内核空间。在TEE中,安全世界和普通世界各自拥有独立的TTBR寄存器组。

页表属性安全世界普通世界
NS位0(安全)1(非安全)
访问权限可访问所有内存仅可访问NS=1内存
典型地址范围0x40000000-0x4FFFFFFF0x80000000-0x8FFFFFFF
页表基址TTBR0_S / TTBR1_STTBR0_NS / TTBR1_NS

2.3.2 判断方法:内存访问权限检查流程

当CPU访问一个虚拟地址时,MMU执行以下检查:

  1. 根据TTBR选择页表,查找对应的页表项。
  2. 检查页表项中的NS位与当前世界状态是否匹配。
  3. 检查访问权限位(AP[2:0])和XN位(禁止执行)。
  4. 如果所有检查通过,则完成地址转换并访问物理内存;否则触发数据中止异常。

风险提示:如果安全世界的页表被错误配置为NS=1,安全数据将暴露给普通世界。务必在TEE初始化时对页表进行严格校验,并定期检查关键页表项是否被篡改。

2.3.3 实战场景:TEE共享内存配置

TEE与普通世界之间需要交换数据(如加解密输入输出),通常通过共享内存实现。配置共享内存时,需要将物理内存页标记为NS=1,并确保两个世界的MMU映射都指向同一物理地址。具体步骤如下:

// 普通世界配置共享内存(Linux内核)
struct tee_shm *shm = tee_shm_alloc(&tee_ctx, size, TEE_SHM_MAPPED);
// 内核将物理页标记为NS,并映射到用户空间

// 安全世界配置共享内存(OP-TEE)
void *va = core_mmu_alloc(&size, &pa);
// 设置页表项NS=1,允许普通世界访问
// 注意:安全世界访问共享内存时,必须使用NS别名地址

2.3.4 常见误区与总结

  • 误区1:认为安全世界不能访问普通世界内存。实际上,安全世界可以访问所有内存,但普通世界不能访问安全内存。
  • 误区2:忽略TLB缓存一致性。修改页表后,必须执行TLB失效操作(如TLBIALL),否则旧映射可能被缓存。
  • 误区3:在TEE中使用普通世界的DMA缓冲区。DMA操作绕过MMU,必须使用物理地址,且要确保缓冲区物理内存被正确标记。

2.4 系统寄存器:配置与状态控制

2.4.1 核心原理:关键系统寄存器一览

ARM架构提供了丰富的系统寄存器,用于控制处理器行为。在TrustZone开发中,以下寄存器至关重要:

  • SCR(安全配置寄存器):控制世界切换、中断路由、异常处理等。NS位决定当前世界状态。
  • VBAR(向量基址寄存器):指向当前世界的异常向量表。
  • SCTLR(系统控制寄存器):控制MMU、I/D-Cache、对齐检查等。
  • TTBR0/TTBR1(页表基址寄存器):保存当前世界的页表基址。
  • CPSR/SPSR(程序状态寄存器):保存当前状态和异常返回状态。

2.4.2 判断方法:寄存器访问与配置流程

系统寄存器通过MRS(读)和MSR(写)指令访问。在TEE开发中,需要特别注意寄存器的访问权限:某些寄存器只能在特定模式下访问。例如,SCR只能在MON模式下写入,而SCTLR可以在任何特权模式下写入。

// 配置SCR寄存器(仅在MON模式下)
MRC p15, 0, R0, c1, c1, 0   // 读取SCR
ORR R0, R0, #0x01            // 设置NS=1,切换到普通世界
MSR p15, 0, R0, c1, c1, 0   // 写入SCR

// 配置VBAR寄存器
LDR R0, =secure_vectors
MCR p15, 0, R0, c12, c0, 0  // 设置安全世界向量表

2.4.3 实战场景:TEE启动时的寄存器初始化

TEE启动流程中,系统寄存器配置是关键步骤。典型初始化顺序如下:

  1. 设置VBAR指向TEE的异常向量表。
  2. 配置SCR,确保初始处于安全世界(NS=0)。
  3. 设置SCTLR,启用MMU和Cache。
  4. 配置TTBR0/TTBR1,建立安全世界页表。
  5. 设置GIC(通用中断控制器),将FIQ路由到安全世界。

最佳实践:在配置系统寄存器时,建议先读取当前值,再修改目标位,避免覆盖其他重要配置。同时,在切换世界之前,务必保存所有必要的上下文。

2.4.4 常见误区与总结

  • 误区1:在普通世界尝试修改SCR寄存器。这会导致权限异常,因为SCR只能在MON模式下修改。
  • 误区2:忽略寄存器访问的副作用。例如,修改SCTLR的MMU使能位后,需要等待流水线排空。
  • 误区3:未保存SPSR导致异常返回错误。每个异常入口都应立即保存SPSR,否则嵌套异常会覆盖它。