本节摘要:物理内存保护是站在物理地址层面的硬关卡:机器特权级用有限条目划出区域并绑定读写执行权限,监督级与用户级的一切访存都从这里过安检。本节拆解三种匹配模式的适用场景、优先级与锁定语义,并用一次"监督级莫名其妙访存故障"的排错把配置细节落成肌肉记忆。
很多人以为开了分页就有了一切保护——反例是裸机与固件世界:没有 MMU 的微控制器上,用户程序与固件共享同一个物理地址空间,页表根本不存在;即便在有页表的服务器芯片上,固件私有的安全内存也不该出现在操作系统的任何映射里,哪怕它误建了映射。这两类需求都指向同一件工具:不依赖页表、直接对物理地址生效的权限关卡。PMP 就是这件工具——一组数量有限(常见八到十六条)的区域条款,每条绑定一段物理地址范围与读、写、执行三个权限位,凡是监督级与用户级发出的访存,硬件在物理地址层自动核对。
规则的两条主线要先立住。其一,默认拒绝:监督级与用户级的一切访存,在没有条款放行时一律失败;条款是白名单,不是黑名单。其二,机器级默认畅通:M-mode 的访存不受 PMP 约束,除非命中一条带锁定位的条款——这条例外是固件给自己上锁的机制,锁上的区域连机器级自己都进不去,直到下次复位。优先级规则同样干脆:条款按编号从零开始依次核对,第一条匹配的条款说了算,后面的统统不再看——想要"窄区域例外于宽区域",就把窄的排在前面。
每条条款由一对寄存器描述:配置寄存器(pmpcfg0 到 pmpcfg15,每字节管一条)与地址寄存器(pmpaddr0 到 pmpaddr15)。配置字节从低到高是:读、写、执行三个权限位,两位匹配模式选择,一位锁定位。地址寄存器存的是物理地址第十位以上(粒度按四字节对齐折算),具体语义随匹配模式变化。
三种匹配模式各伺候一种场景。TOR(区间顶模式)语义最直白:当前条款的地址寄存器是区间顶,前一条条款的地址寄存器是区间底——任意起止的区间,适合划分形状不规则的区域,代价是消耗相邻两条才能精确框一段。NA4 是四字节精确匹配,最小粒度,适合给单个寄存器组或单个表项设防。NAPOT(二的幂次对齐区间)最常用:把地址低位写成 0111 形式,一表示范围加倍,一段任意大小的二的幂次区间只需一条条款——固件划大块安全区几乎都选它。
| 模式 | 地址语义 | 典型用途 |
|---|---|---|
| TOR | 本条地址为顶、前条地址为底 | 不规则起止的区间,如中断向量表 |
| NA4 | 精确四字节 | 单个外设寄存器、单条描述符 |
| NAPOT | 地址低位 0111 表达二的幂次区间 | 固件安全区、DMA 保留池 |
锁定位的语义值得单独强调:一旦置位,本条款连同其地址寄存器一起冻结,机器级自己也无法改写,直到复位。安全启动流程常用它实现"上电后固件划界、此后任何人(包括被攻陷的固件后续阶段)都无法扩权"的硬承诺。
两个补充语义能让配置工作少踩空。粒度对齐:硬件声明的最小保护粒度可能大于四字节(常见的是与内存管理粒度一致),NAPOT 编码的低位表示要按实际粒度折算,配置前先读实现说明。代行访存:当机器级替更低特权级执行访存(比如固件为用户程序搬运数据)时,权限检查按哪个身份算,由状态寄存器里的代行访存位决定——搬运用户数据前忘了开这一位,PMP 会按机器级身份放行,安全边界悄悄失效。这类开关平时不显眼,安全审计时都是必查项。

背景:某定制板卡上,固件为保护一段加密密钥存储区,用 NAPOT 条款把主存顶部十六 MiB 设为"机器级专有"(权限位给读与写、锁定位置一),其余区域放开。系统进入 Linux 后,驱动在读一帧 DMA 缓冲时稳定复现访存异常,mcause 报存储访问故障,mtval 指向的地址恰好落在主存顶部附近。
操作:按三层安检图定位。页表层:内核确认该虚拟地址有合法映射,翻译层洗清嫌疑;PMP 层:机器级读出全部条款逐条核对,发现条款一是一条早前调试时留下的宽域 TOR 条款,把主存顶部一直到物理内存末端都划给了机器级专有,且排在了正式的放行条款之前——按"第一条命中即裁决"的规则,放行条款永远轮不到说话。
结果:删除调试遗留条款、调整条款顺序(窄的例外在前、宽的放行在后),重新上电后驱动正常。顺带把全部 PMP 配置导出存档,纳入启动检查单。
解读:这个案例浓缩了 PMP 排错的三个要点。其一,页表正常不代表访存无恙——物理层的关卡独立于翻译层,三层安检必须各查各的。其二,条款顺序是语义的一部分:同一个配置集合,排列不同结果完全不同,审计时要按编号全序读。其三,故障地址与条款区间的对号入座是关键动作——把 mtval 的地址换算到条款区间图上,命中哪条一目了然,比反复试错高效得多。
变式:把密钥区的锁定位从一改零、再从监督级发起访问,观察异常消失——验证锁定位只管"谁能改条款"而权限位管"谁能进区域",两者正交;再把一条条款改成只执行、不读写,往该区域加载一段纯代码并执行,验证"不可写代码区"这一防注入手法在 PMP 上的落地。
收尾把两者的分工说透。页表管"虚拟世界的秩序":进程隔离、按需映射、换页调度,粒度细到页、数量近乎无限;PMP 管"物理世界的底线":条目少、粒度粗(细到四字节、粗到整个内存)、机器级专属。安全模型上它们是纵深防御的两道线——内核被攻破时,页表形同虚设,但 PMP 锁定的密钥区依然进不去;固件出漏洞时,PMP 条款本身又被锁定位保护。设计安全启动链时,惯例是越早的阶段划越多条款并上锁,把后续所有阶段的权限天花板钉死。对无 MMU 的微控制器,这套机制还要兼职页表的角色:用户任务与固件的隔离全靠几条 PMP 条款——这也是为什么低功耗芯片上 PMP 条目的数量与灵活性是选型时的硬指标。
物理地址的关卡立住了。但多核系统里还有一个更隐蔽的问题:同一份数据在多个缓存里的副本,谁先谁后、谁真谁假——下一节进入弱内存模型的世界。