MPU/PMP 内存保护区域:特权软件如何隔离任务与外设
更新时间:2026-09-19。前置:MMU/TLB 虚拟内存、TCM 紧耦合内存、地址译码。跑 RTOS 的 MCU 通常没有页表/虚拟地址,但仍需要防止一个任务踩坏内核或别人——这由 MPU/PMP 提供。
本文要回答的问题
- 没有 MMU 时硬件怎么做内存保护,region 怎么定义?
- MPU(ARM)和 PMP(RISC-V)在机制上有什么异同?
- 权限违例怎样触发异常,软件如何响应?
- region 的粒度、对齐、重叠优先级有哪些坑?
一、MPU/PMP 的基本模型
两者都把物理地址空间划成数量有限的保护区域(region),每个区域有基址、大小和权限/属性,由特权软件(内核/Hypervisor/安全固件)配置,用户任务不能自行放开。
ARM MPU(以 Cortex-M 为例):
- 8/16 个 region(可更多),每 region 配 RBAR(基址)、RASR(使能、大小、子区域禁用、XN 执行禁止、AP 读写特权、内存类型)。
- 权限维度:特权/非特权(S/U)× 读/写、执行允许/禁止(XN)。
- 内存类型属性:Normal(可 cache/缓冲)、Device(不推测、严格顺序)、Shareable(多核一致性),这些直接决定访问排序与缓存行为(与 内存屏障 配套)。
- 违例触发 MemManage fault / HardFault,故障状态寄存器给出访问类型、地址(BFAR/MMFAR)。
RISC-V PMP(Physical Memory Protection):
- 16/64 个表项,每表项
pmpaddr(地址寄存器)+pmpcfg(权限、地址匹配模式、锁定位 L)。 - 权限:R/W/X 三位。
- 地址匹配模式:NAPOT(自然对齐 2 的幂区域,最常用)、NA4(4 字节)、TOR(两地址之间的范围,top-of-range)。
- 主要约束 M 模式以外(S/U)的访问;L 位锁定后 M 模式也受限且表项在 SMP 软复位前不可改,用于安全固件锁死配置。
- 违例触发 load/access 或 instruction page/access fault(mcause=1/2/5/7/12/13/15),mtval 给故障地址。
二、典型区域布局
一个 RTOS 常见配置:
region 0: Flash 代码 只读 + 可执行(非特权读)
region 1: 内核/特权 RAM 仅特权 RW,非特权不可访问
region 2: 当前任务栈 仅该任务 RW,XN(不可在栈上执行)
region 3: 任务私有数据 按任务切换重配
region 4: 共享内存/IPC 非特权可访问的小窗口
region 5: 外设区 Device/non-cacheable,仅特权或特定驱动可写
默认/背景: 拒绝(deny)- 外设地址必须标为 Device/non-cacheable + 非执行,防止编译器/硬件缓存寄存器读写、防止代码在外设地址执行(见 地址窗口属性)。
- 栈和堆设为 XN 可显著降低代码注入风险;任务切换时内核更新 MPU/PMP 指向新任务的内存,实现任务隔离。
- 关键内核代码/数据可配成对非特权完全不可见。
三、匹配与优先级
- ARM MPU:地址可落在多个 region,区域编号高者优先(高编号覆盖低编号),允许用小的高优先级 region 在大区域里「打洞」(配合子区域禁用)。
- RISC-V PMP:编号小的表项优先匹配(PMP 检查从低到高,第一个命中即决定权限);要「开小洞」时把小区域放在低编号。
- 默认策略:没有任何 region 命中的访问由「背景(background region)/全允许或全拒绝」决定。安全做法是默认拒绝(ARM MPU 可让非特权无背景权限;RISC-V 中未匹配区域对 S/U 拒绝)。默认允许是常见漏洞来源。
- region 必须自然对齐到自身大小:一个 64KB region 的基址必须 64KB 对齐;硬件只比较高位,低位当作"don't care"。这与 地址译码窗口 的对齐约束同源。
四、MPU/PMP vs MMU
| 维度 | MPU/PMP | MMU |
|---|---|---|
| 地址转换 | 无,直接物理地址 | 虚拟→物理,分页/分块 |
| 表项数量 | 少(个位数~几十) | 大量页表项/TLB |
| 粒度 | 2 的幂大块(NAPOT/region) | 4KB 页(可大块) |
| 目标 | RTOS/固件任务隔离、外设保护 | 富 OS 进程隔离、按需分页、碎片透明 |
| 复杂度/成本 | 小、确定延迟 | 大,含 TLB(见 MMU/TLB) |
MPU/PMP 提供的是「分区隔离」而非地址虚拟化:任务仍用物理地址,但越界/提权访问会被硬件 fault 拦下。它不防物理总线上的 DMA——那是总线防火墙/IOPMP的职责。
五、常见的坑
- 默认允许:未匹配区域放行,新外设/堆扩展落在保护外成为漏洞;应默认拒绝、显式白名单。
- 区域未自然对齐:基址不是大小整数倍,硬件行为不符预期或配置被忽略。
- 优先级方向记反:RISC-V PMP 小编号优先、ARM MPU 高编号优先,打洞时放错位置导致小区域不生效。
- 区域数太少:任务多而 region 有限,需要在上下文切换时重配或用 TOR 合并相邻段。
- 外设标成 Normal/cacheable:寄存器读被缓存、写被缓冲合并,驱动时序错乱、中断丢失(应 Device + 屏障)。
- 栈/堆未设 XN:可在数据内存执行,栈溢出/注入可执行。
- 任务切换忘记重配:新任务沿用旧任务权限,隔离失效;MPU 重配通常还要 DSB/ISB 屏障使其生效(见 内存屏障)。
- PMP L 位锁定过早:锁了之后 M 模式也无法改,调试/后续初始化受阻;应在安全配置最后锁定。
- 把 fault 当复位处理:合法的写时复制/探测访问在无 MMU 时不存在,但驱动确实可能探测地址,应有受控 fault 处理而非直接重启。
- R/W/X 给得过宽:代码段给写权限、外设给执行权限;最小权限原则。
- 粒度比数据结构大:最小保护块比任务栈还大,多个任务被迫共块,隔离不彻底,需布局上凑齐对齐。
- 只靠 MPU 防 DMA:MPU 管核内、防火墙管总线,二者都要配(见 总线防火墙)。
相关与延伸
需要虚拟地址/分页时见 MMU/TLB;确定性内存见 TCM;区域对齐同源见 地址译码/重映射;总线侧隔离见 总线防火墙;配置生效顺序见 内存屏障。
总结
MPU(ARM)和 PMP(RISC-V)在没有 MMU 的 MCU 上把物理地址空间划成数量有限、必须按自身大小自然对齐的保护区域,用特权/非特权×读/写/执行权限和 Normal/Device 内存类型把内核、任务栈、共享内存和外设分开:ARM MPU 高编号 region 优先且外设应标 Device+XN,RISC-V PMP 用 NAPOT/TOR/NA4 匹配且小编号优先、L 位可连 M 模式一起锁定,越界访问由硬件直接触发 MemManage fault 或 access fault 并给出故障地址;任务切换时重配区域、默认策略必须拒绝、配置后加屏障生效,但它只隔离 CPU 核内访问,DMA 等外部主控还要靠总线防火墙——默认放行、不对齐、优先级方向反和把外设标成 cacheable 是四大常见坑。