2. ARM TrustZone 技术基础:硬件架构与状态切换

TrustZone 并非一个单一的硬件模块,而是一套系统级的安全扩展方案。它通过在处理器、内存总线、外设总线等多个层面引入硬件隔离机制,构建了一个与“正常世界”(Normal World)并行的“安全世界”(Secure World)。理解其硬件架构是进行安全驱动移植的前提。

2.1 核心硬件组件:SCU、TZASC、TZPC

TrustZone 的硬件隔离能力依赖于几个关键的互联与控制组件。它们协同工作,确保安全世界资源对正常世界不可见或不可篡改。

2.1.1 窥探控制单元(SCU)

SCU 存在于多核 Cortex-A 处理器集群内部,负责管理多核之间的缓存一致性。在 TrustZone 环境中,SCU 需要识别每个核当前所处的安全状态,并确保安全世界与正常世界的数据在缓存层面不会发生非法的信息泄露或污染。

  • 状态感知: SCU 监控每个核心的 NS(Non-Secure)位,以区分安全与非安全事务。
  • 一致性过滤: 当正常世界核心访问一段内存时,SCU 不会去查询安全世界核心的私有缓存,反之亦然,从而实现了缓存级别的隔离。
驱动移植要点: 在编写安全世界驱动时,无需直接配置 SCU,但必须意识到其存在。如果驱动需要操作多核同步或缓存刷新指令(如 flush_dcache_all),必须确保这些操作不会意外地影响正常世界的缓存行,反之亦然。

2.1.2 TrustZone 地址空间控制器(TZASC)

TZASC 是内存隔离的核心。它位于内存控制器(如 DRAM Controller)之前,负责对物理内存区域进行安全属性划分。TZASC 将物理内存划分为多个区域,每个区域可以独立配置为“安全”或“非安全”。

  • 区域划分: 通过编程 TZASC 的寄存器,可以将一段连续的物理内存(如 0x8000_0000 ~ 0x8FFF_FFFF)标记为仅安全世界可访问。
  • 访问控制: 任何来自非安全总线主设备(如正常世界的 CPU、DMA)对安全内存区域的访问,都会被 TZASC 硬件直接阻止,并可能触发一个安全异常。
TZASC 区域配置关键参数
参数 描述 典型值(示例)
BASE_ADDRESS 区域的起始物理地址 0x8000_0000
SIZE 区域的大小 0x1000_0000 (256MB)
SECURITY 区域的安全属性 Secure (0) / Non-Secure (1)
ENABLE 区域使能位 Enabled (1)
关键约束: TZASC 的配置通常在系统启动早期(如 ATF/OP-TEE 启动阶段)由安全固件完成。驱动开发者通常不需要在运行时动态修改 TZASC 配置,但必须清楚驱动所操作的内存区域(如共享内存、安全内存)在 TZASC 中的安全属性定义。

2.1.3 TrustZone 保护控制器(TZPC)

TZPC 用于控制外设(Peripherals)的安全属性。与 TZASC 控制内存不同,TZPC 控制的是挂在 AHB/APB 总线上的外设。每个外设可以独立地被标记为“安全”或“非安全”。

  • 外设隔离: 例如,一个 UART 控制器可以被配置为安全外设,只有安全世界软件才能访问其寄存器。
  • 粒度: TZPC 通常以“组”为单位进行控制,每组包含多个外设。
驱动移植场景: 假设你正在将一个 UART 驱动从 Linux 移植到 TrustZone 安全世界。在 Linux 中,该 UART 被配置为正常世界外设。在安全世界中,你需要:
  1. 确认该 UART 对应的 TZPC 组已被配置为“安全”。
  2. 在安全世界驱动中直接操作该 UART 的寄存器。
  3. 确保 Linux 端的驱动不再访问该 UART,否则会触发总线错误。
TrustZone 硬件架构核心组件关系图 安全世界 (Secure World) 正常世界 (Normal World) Cortex-A (S) Cortex-A (S) Cortex-A (NS) Cortex-A (NS) 窥探控制单元 (SCU) 窥探控制单元 (SCU) TZASC (内存隔离) TZASC (内存隔离) 安全内存 非安全内存 非安全内存 非安全内存 TZPC 控制外设安全属性,未在图中显示,但作用于总线上的每个外设节点。

2.2 安全与非安全状态切换:Monitor 模式

安全世界与正常世界之间的切换并非简单的函数调用,而是通过一个特殊的处理器模式——Monitor 模式(监控模式)来完成的。Monitor 模式是 TrustZone 架构的基石,它拥有比安全世界更高的特权级。

2.2.1 Monitor 模式的作用

  • 世界切换的唯一入口: 无论是从正常世界进入安全世界(SMC 指令),还是从安全世界返回正常世界(ERET 指令),都必须经过 Monitor 模式。
  • 上下文保存与恢复: Monitor 模式负责保存当前世界的寄存器状态(如 R0-R14、SPSR、SCR),并恢复目标世界的寄存器状态。这个过程被称为“世界切换上下文”。
  • 安全配置寄存器(SCR): Monitor 模式通过读写 SCR 寄存器来控制当前处理器的安全状态(NS 位)以及异常路由策略。

2.2.2 世界切换流程

一个典型的从正常世界到安全世界的切换流程如下:

  1. 触发 SMC 指令: 正常世界软件(如 Linux 内核或用户态驱动)执行一条 SMC(Secure Monitor Call)汇编指令。
  2. 陷入 Monitor 模式: 硬件捕获 SMC 指令,强制将处理器切换到 Monitor 模式,并将异常向量表指向 Monitor 模式的入口。
  3. 保存上下文: Monitor 模式下的代码(通常是 ARM Trusted Firmware)保存正常世界的寄存器状态到栈中。
  4. 配置 SCR: 修改 SCR.NS 位为 0(安全状态),并配置异常返回地址为安全世界的目标函数。
  5. 执行 ERET: 执行 ERET 指令,处理器退出 Monitor 模式,进入安全世界(如 OP-TEE 内核),开始执行安全服务。
  6. 返回流程: 安全世界服务完成后,再次触发 SMC 或执行特定指令,Monitor 模式恢复正常世界的上下文,并通过 ERET 返回。
世界切换流程:SMC 调用与 Monitor 模式 正常世界 (NS=1) Linux 内核 / 驱动 执行 SMC #0 指令 参数通过寄存器传递 Monitor 模式 (最高特权) ARM Trusted Firmware (ATF) 1. 保存正常世界上下文 2. 设置 SCR.NS = 0 3. 执行 ERET 进入安全世界 安全世界 (NS=0) OP-TEE / 安全驱动 处理安全请求 返回结果并触发返回流程 SMC 异常 ERET

2.2.3 关键指令与寄存器

世界切换相关指令与寄存器
名称 类型 描述
SMC #imm 指令 触发安全监控调用,是进入 Monitor 模式的唯一软件方法。
ERET 指令 从异常返回,用于从 Monitor 模式退出到目标世界(安全或非安全)。
SCR 系统寄存器 安全配置寄存器。其中的 NS 位(bit 0)决定当前处理器的安全状态。
VBAR 系统寄存器 向量基址寄存器。Monitor 模式有自己的 VBAR,指向 Monitor 模式的异常向量表。
驱动开发者视角:
  • 在安全世界驱动中,你通常不需要直接编写 Monitor 模式代码(这部分由 ATF 完成)。
  • 你需要理解 SMC 调用约定(如 smc 指令的参数传递规则),以便在正常世界的客户端驱动中正确地调用安全世界服务。
  • 在安全世界驱动中,你运行在安全世界(NS=0),可以直接访问安全内存和外设,无需担心 Monitor 模式的上下文切换细节。

2.3 异常模型与路由

TrustZone 改变了 ARM 的异常模型。中断(IRQ/FIQ)和外部中止(External Abort)可以根据当前的安全状态被路由到不同的处理程序。

  • FIQ 路由: 通常配置为安全中断。无论当前 CPU 处于何种状态,FIQ 都会被路由到安全世界的异常向量表。这是安全世界抢占正常世界执行的关键机制。
  • IRQ 路由: 通常配置为非安全中断。当 CPU 处于安全世界时,IRQ 可以被配置为直接路由到 Monitor 模式,由 Monitor 模式决定是否切换到正常世界处理。
驱动中断处理注意事项:
  1. 安全世界驱动应使用 FIQ 来处理其外设中断,以确保实时性和安全性。
  2. 在安全世界驱动中,应避免长时间关闭中断,以免影响正常世界的 IRQ 响应。
  3. 中断路由的配置通常在系统启动时由安全固件完成,驱动开发者需要遵循既定的路由策略。