首先,我们需要通过pmpaddrX寄存器设定受保护内存区域的起始地址,这要求将地址值右移两位。这样做是为了确保地址按照8字节边界对齐,满足PMP的配置要求。接下来,使用64位的pmpcfgY寄存器来设置访问权限。在这个寄存器中,每一位代表一个8字节的块,可以独立配置,未被使用的位则置为0,表示这些块未受到保护。
在实际应用中,我们可能需要保护内存中的不同区域。比如,一个64KB的闪存区域从基地址0x0开始,一个32KB的RAM区域从基地址0x20000000开始,还有一个4KB的外设区域从基地址0x30000000开始。通过对这些区域进行特定的PMP配置,我们可以精细地控制对这些内存区域的访问权限。
在设置PMP时,还需注意优先级规则。较低编号的pmpcfg和pmpaddr寄存器比高编号的有更高的优先级,这意味着高编号的寄存器可以覆盖低编号的设置,但低编号的可以针对特定的区域进行详细的权限分配。
PMP的访问控制是通过L、R、W和X这四个位来实现的,它们决定了是否有权限进行加载(读取)、读取、写入或执行。当这四个位的设置与访问的字节范围完全匹配时,访问才会成功。如果设置的权限不允许访问,将产生异常。
当处理器在机器模式下运行且锁定位(L位)被清零时,如果PMP条目匹配了所有被访问的字节,那么访问会被允许。如果锁定位被设置,访问是否成功则取决于PMP对该区域的权限设置。类似地,在管理员模式或用户模式下,访问权限也取决于区域设置。
如果一个读取或写入操作失败,会产生相应的异常,并且指令获取错误会在指令失败时发生。重要的是,异常发生在目标保护区,而非指令分支点。
此外,某些指令可能会触发多个访问,而这些访问可能不是原子性的。如果其中至少有一次访问失败,就会触发异常,其他成功的访问可能带来不可预见的副作用。
在某些处理器实现中,未对齐的加载和存储操作可能会分解为多个子操作,这些子操作可能不会同时触发异常。例如,即使某些部分未通过PMP检查,其他部分的成功存储也可能变得可见。
浮点数的存储操作也有其特殊性。例如,在RV32D架构中,一个FSD(浮点存储双精度)指令可能会存储一个双精度浮点数,即使存储地址不是自然对齐的,它也会按照PMP的规则进行检查。
通过深入了解PMP的工作原理及其配置细节,我们可以更安全、更有效地管理和保护系统内存,确保关键数据的安全性和系统的稳定性。
PMP 配置
PMP 寄存器只能在机器模式下编程。pmpaddrX 寄存器应首先用受保护区域的基地址编程,右移两位。然后,应该使用正确配置的 64 位值对pmpcfgY 寄存器进行编程,其中包含每个正确对齐的 8 位 pmpXcfg 字段。未使用的字段可以简单地写入 0,标记它们未使用。
PMP 配置例子
以下示例显示了仅机器模式的配置,其中 PMP 权限应用于三个感兴趣的区域,第四个区域覆盖剩余的内存映射。回想一下,较低编号的 pmpXcfg 和pmpaddrX 寄存器优先于较高编号的区域。该规则允许更高编号的 PMP寄存器全面覆盖整个内存映射,同时允许编号较低的区域将权限应用于特定的感兴趣区域。以下示例显示基地址 0x0 处的 64 KB 闪存区域、基地址0x2000_0000 处的 32 KB RAM 区域以及基地址 base 0x3000_0000 处的 4 KB外设区域。内存映射的其余部分是保留空间。
PMP 访问场景
L、R、W 和 X 位仅在访问的所有字节都被该 PMP 条目覆盖时确定访问是否成功。例如,如果 PMP 条目配置为匹配四字节范围0xC–0xF,那么对 0x8–0xF 范围的 8 字节访问将失败,假设 PMP 条目是与这些地址匹配的最高优先级条目。
在锁定位清零 (L=0) 的机器模式下运行时,如果 PMP 条目与访问的所有字节匹配,则访问成功。如果在机器模式下设置了锁定位(L=1),则访问取决于为该区域设置的权限。同样,在管理员模式或用户模式下,访问权限取决于为该区域设置的权限。
失败的读取或写入访问会生成加载或存储访问异常,并且指令访问错误会在失败的指令获取时发生。当尝试从没有执行权限的区域执行时发生异常时,错误发生在获取而不是分支上,因此mepc CSR 将反映目标保护区的值,而不是分支的地址。
一条指令可能产生多个访问,这可能不是相互原子的。如果一条指令产生的至少一次访问失败,则将发生异常。一条指令的其他访问可能会成功,但会产生明显的副作用。例如,对虚拟内存的引用可以分解为多个访问。
在某些实现中,未对齐的加载、存储和指令提取也可能被分解为多个访问,其中一些可能在访问异常发生之前成功。特别是,通过 PMP检查的未对齐存储的一部分可能变得可见,即使另一部分未通过 PMP 检查。对于比 XLEN 位宽的浮点存储(例如,RV32D 中的 FSD指令),即使存储地址自然对齐。