3. 安全隔离的硬件基础

在功能安全与信息安全融合的系统中,软件层面的隔离策略最终必须落到硬件机制上才能获得可验证的强制力。MMU(内存管理单元)与MPU(内存保护单元)是两类最基础、也最容易被混淆的硬件保护组件。理解它们的差异、配置方式,以及外设访问控制与DMA隔离的配合关系,是构建可信隔离边界的起点。

MMU与MPU:两种不同的保护哲学

MMU的核心能力是地址转换。它通过页表把CPU发出的虚拟地址映射为物理地址,并在页表项中携带访问权限位(读、写、执行、用户/特权)。由于映射粒度通常为4KB页,MMU天然支持虚拟内存、进程地址空间隔离和按需分页。代价是页表遍历会引入延迟,且页表本身需要被保护,否则攻击者篡改页表即可绕过全部隔离。

MPU不改变地址,只做权限检查。它把物理地址空间划分为若干区域(region),每个区域配置基址、大小和访问属性。CPU每次访问先与MPU区域比对,命中则按区域权限放行或触发异常,未命中则按默认策略处理。MPU区域数量有限(典型为8~16个),粒度较粗,但检查是确定性的、无页表遍历,适合硬实时与高完整性场景。

选择依据可以概括为:需要虚拟地址空间、动态加载、多进程隔离时用MMU;需要确定性、低开销、物理地址直控时用MPU。安全隔离系统常在同一芯片的不同核上分别使用两者,或在同一核上以MMU做粗隔离、以MPU做关键代码段的细保护。

MMU 与 MPU 保护路径对比 MMU:地址转换 + 权限检查 CPU 虚拟地址 0x8000_0000 页表遍历 TLB 缓存 物理 地址 权限位:R / W / X / U / P 粒度:4KB 页,支持多级页表 开销:TLB 命中低,未命中需遍历 MPU:物理地址区域权限检查 CPU 物理地址 0x2000_0000 区域比对 Region 0~N 放行 /异常 属性:R / W / X / 特权级 粒度:区域基址+大小,8~16 个 开销:确定性检查,无页表遍历 外设访问控制与 DMA 隔离 CPU 核 特权/用户态 系统总线 / 互连 外设寄存器 UART / SPI / GPIO DMA 控制器 通道/流 物理内存 安全区(受保护) 非安全区 DMA 需经 IOMMU/SMMU 检查 外设访问: MPU 区域 + 特权级

内存保护单元配置:从区域划分到属性设定

MPU配置的核心是“区域划分”与“属性设定”两件事。区域划分要回答:哪些地址范围属于安全关键代码、哪些属于安全数据、哪些属于非安全共享缓冲区、哪些属于外设寄存器。属性设定要回答:每个区域允许读、写、执行中的哪些操作,以及是否允许非特权访问。

以一个典型的Cortex-M类MCU为例,假设使用8个MPU区域,可以按如下思路分配:

  • Region 0:安全固件代码区,起始0x0800_0000,大小256KB,属性为只读+可执行,仅特权可访问。
  • Region 1:安全数据区,起始0x2000_0000,大小64KB,属性为读写,仅特权可访问,禁止执行。
  • Region 2:非安全共享缓冲区,起始0x2001_0000,大小16KB,属性为读写,允许非特权访问,禁止执行。
  • Region 3:外设寄存器区,起始0x4000_0000,大小512KB,属性为读写,仅特权可访问,禁止执行。
  • Region 4:栈保护区,起始0x2000_F000,大小4KB,属性为只读,用于检测栈溢出。
  • Region 5~7:保留或用于其他外设/通信缓冲区。

配置时需要注意几个易错点。第一,区域不能重叠,若硬件允许重叠则优先级必须明确,否则行为不确定。第二,区域大小必须是2的幂,且基址按大小对齐,例如64KB区域基址必须是64KB对齐。第三,默认背景区域(background region)的权限要设为最严格,通常为仅特权可访问,避免未覆盖地址被意外放行。第四,使能MPU前要先执行内存屏障指令,确保配置生效顺序正确。

下面是一段示意性的MPU配置代码,用于说明寄存器级操作顺序,参数为教学示例,并非某款芯片的实测值:

// 教学示例:MPU 区域配置顺序
MPU->RNR = 0;                          // 选择 Region 0
MPU->RBAR = 0x08000000 | (1 << 4);    // 基址 + VALID 位
MPU->RASR = (0x0B << 1)               // 大小 256KB
          | (0x1 << 24)                // 使能
          | (0x0 << 19)                // 特权只读
          | (0x1 << 28);               // XN 禁止执行位清零
__DSB();                               // 数据同步屏障
__ISB();                               // 指令同步屏障
MPU->CTRL = MPU_CTRL_ENABLE | MPU_CTRL_PRIVDEFENA;

配置完成后,验证方法是:在非特权模式下尝试写入安全数据区,应触发MemManage异常;尝试从非安全缓冲区执行代码,应触发执行错误。若异常未触发,说明区域属性或使能顺序有误。

外设访问控制:把寄存器也当作受保护资源

外设寄存器是常被忽视的攻击面。一个非特权任务若能直接写DMA控制器的源地址寄存器,就可以让DMA把安全内存搬运到非安全区,从而绕过CPU侧的全部检查。因此,外设访问控制必须与内存保护同等对待。

基本做法是把外设寄存器区纳入MPU区域,并设为仅特权可访问。非特权任务若需要操作外设,必须通过受控的系统调用陷入特权态,由内核代理完成。对于必须由非特权任务直接访问的外设,可以单独划分一个区域,只开放必要的寄存器偏移,并禁止写入控制类寄存器。

更严格的设计会引入外设访问防火墙(如某些SoC中的PPC或TZPC),在总线层面按主设备ID和地址范围做二次过滤。这样即使CPU侧MPU配置被篡改,总线防火墙仍能阻断非法访问。

DMA隔离机制:让搬运器也遵守规则

DMA控制器通常以物理地址直接访问内存,不经过CPU的MMU/MPU检查。这意味着DMA可以成为隔离边界的“后门”。DMA隔离的目标是让DMA的每一次内存访问也经过权限校验。

常见机制有三种。第一种是IOMMU/SMMU,为DMA提供地址转换和权限检查,DMA使用设备虚拟地址,由IOMMU映射到物理地址并校验权限。第二种是DMA通道级隔离,为每个DMA通道配置允许访问的地址范围,超出范围则拒绝传输并产生错误中断。第三种是内存区域标记,在内存控制器或总线层面标记安全/非安全属性,DMA访问时由硬件比对主设备的安全状态。

工程上,DMA隔离的验证要覆盖三个场景:安全DMA向安全内存搬运应成功;非安全DMA向安全内存搬运应被拒绝并产生错误;安全DMA向非安全内存搬运应按策略允许或拒绝。错误中断的响应时间和恢复路径也要纳入验证,确保隔离失败时系统能进入安全状态而不是静默继续。

MMU与MPU不是互斥选项。高完整性系统常在应用核使用MMU做进程隔离,在实时核使用MPU做确定性保护,并在总线层用IOMMU约束DMA,形成多层防御。
MPU区域数量有限,配置时优先保护安全关键代码、安全数据、栈保护区和DMA控制寄存器。未覆盖的地址必须由默认背景区域拒绝非特权访问,不能留空。

总结来看,硬件隔离的可靠性取决于三个层面的协同:CPU侧的MMU/MPU决定谁能访问什么,外设访问控制决定谁能操作哪些寄存器,DMA隔离决定搬运过程是否也受约束。三者缺一,隔离边界就存在可被绕过的路径。配置完成后,必须用非特权访问、越界访问和DMA越权搬运三类测试来验证边界是否真正生效。