RISC-V SiFive U54内核——PMP物理内存保护

目录

    • PMP简介
    • PMP 功能描述
    • PMP 区域锁定
    • PMP 寄存器
    • PMP 和 PMA
    • PMP 配置
    • PMP 和 Paging
    • PMP 限制
    • 没有 PMP 保护的区域的行为
    • PMP 保护区的缓存刷新行为

PMP用于保护物理内存,例如在opensbi启动时,会将自己所占内存部分用PMP保护起来,不让其他程序访问。

PMP简介

机器模式是最高权限级别,默认情况下在设备的整个内存映射中具有读取、写入和执行权限。 但是,机器模式以下的特权级别对设备内存映射的任何区域没有读、写或执行权限,除非 PMP 特别允许。 对于较低的权限级别,PMP 可以授予对设备内存映射的特定区域的权限,但它也可以在机器模式下撤销权限。

当进行相应编程时,PMP 将在 hart 以管理员或用户模式运行时检查每次访问。 对于机器模式,除非在特定区域的 pmpcfgY CSR 中设置锁定位 (L),否则不会发生 PMP 检查。

当机器先前的权限级别是管理员或用户(mstatus.MPP=0x1 或 mstatus.MPP=0x0)并且设置了修改权限位时,PMP 检查也会发生在加载和存储上 (mstatus.MPRV=1)。 对于虚拟地址转换,PMP 检查也适用于管理模式下的页表访问。

U54内核 PMP 支持 8 个区域,最小区域大小为 4 字节

本期介绍 RISC‑V 架构中的 PMP 概念如何应用于 U54。 有关 PMP 的更多信息,请参阅 RISC‑V 指令集手册,第 II 卷:特权架构,版本 1.10。

PMP 功能描述

U54 PMP 单元有 8 个区域,最小粒度为 4 个字节。 对每个区域的访问由 8 位 pmpXcfg 字段和相应的 pmpaddrX 寄存器控制。 重叠区域 允许,其中较低编号的 pmpXcfgpmpaddrX 寄存器优先于较高编号的区域。 U54 PMP 单元实现架构定义的 pmpcfgY CSR pmpcfg0,支持 8 个区域。

pmpcfg2 已实现,但硬连线为零。 访问 pmpcfg1 或 pmpcfg3 会导致非法指令异常

PMP 寄存器只能在 M 模式下编程。 通常,PMP 单元强制执行对 S 模式和 U 模式访问的许可。

PMP 区域锁定

PMP 允许区域锁定,一旦区域被锁定,对配置和地址寄存器的进一步写入将被忽略。 锁定的 PMP 条目只能通过系统重置解锁。 可以通过设置 pmpXcfg 寄存器中的 L 位来锁定一个区域。

除了锁定 PMP 条目外,L 位指示是否对机器模式访问强制执行 R/W/X 权限。 当 L 位清零时,R/W/X 权限适用于 S 模式和 U 模式。

PMP 寄存器

每个 PMP 区域都由一个 8 位 pmpXcfg 字段描述,与一个 64 位 pmpaddrX 寄存器结合使用,该寄存器保存受保护区域的基地址。 每个区域的范围取决于下一节中描述的寻址 (A) 模式。 pmpXcfg 字段位于 64 位 pmpcfgY CSR 中。

每个 8 位 pmpXcfg 字段包括一个读、写和执行位,外加一个两位地址匹配字段 A 和一个锁定位 L。允许重叠区域,其中编号最小的 PMP 条目胜出该区域。

PMP 配置寄存器

对于 RV64 架构,未实现 pmpcfg1 和 pmpcfg3。 这减少了占用空间,因为 pmpcfg2 已经包含 RV32 和 pmp11cfg 的配置字段 pmp8cfg 和 RV64

RISC-V SiFive U54内核——PMP物理内存保护_第1张图片

pmpcfgY 和 pmpaddrX 寄存器只能通过 CSR 特定指令访问,例如用于读取的 csrr 和用于写入的 csrw。

img

RISC-V SiFive U54内核——PMP物理内存保护_第2张图片

复位后,PMP 寄存器字段 A 和 L 设置为 0。RISC‑V 指令集手册第 II 卷:特权架构版本 1.10 未指定所有其他 hart 状态。

下面是一些使用 NAPOT 地址模式的例子。

RISC-V SiFive U54内核——PMP物理内存保护_第3张图片

PMP 地址寄存器

PMP 有 8 个地址寄存器。 每个地址寄存器 pmpaddrX 都与相应的 pmpXcfg 字段相关联。 每个地址寄存器都包含右移两位的受保护区域的基地址,以实现最小 4 字节对齐。

根据 RISC‑V 指令集手册,第二卷:特权架构,版本 1.10,最大编码地址位为 [55:2]

img

PMP 和 PMA

PMP 值与物理内存属性 (PMA) 结合使用。 由于 PMA 是静态的且不可配置,如果这些权限已经静态应用,则 PMP 只能撤销对 PMA 区域的读取、写入或执行权限。

PMP 配置

PMP 寄存器只能在机器模式下编程。 pmpaddrX 寄存器应首先用受保护区域的基地址编程,右移两位。 然后,应该使用正确配置的 64 位值对 pmpcfgY 寄存器进行编程,其中包含每个正确对齐的 8 位 pmpXcfg 字段。 未使用的字段可以简单地写入 0,标记它们未使用。

PMP 配置例子

以下示例显示了仅机器模式的配置,其中 PMP 权限应用于三个感兴趣的区域,第四个区域覆盖剩余的内存映射。回想一下,较低编号的 pmpXcfgpmpaddrX 寄存器优先于较高编号的区域。 该规则允许更高编号的 PMP 寄存器全面覆盖整个内存映射,同时允许编号较低的区域将权限应用于特定的感兴趣区域。 以下示例显示基地址 0x0 处的 64 KB 闪存区域、基地址 0x2000_0000 处的 32 KB RAM 区域以及基地址 base 0x3000_0000 处的 4 KB 外设区域。 内存映射的其余部分是保留空间。

RISC-V SiFive U54内核——PMP物理内存保护_第4张图片

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 指令),即使存储地址自然对齐。

PMP 和 Paging

物理内存保护机制旨在与 RISC‑V 指令集手册,第二卷:特权架构,版本 1.10 中描述的基于页面的虚拟内存系统组合。 启用分页后,访问虚拟内存的指令可能会导致多次物理内存访问,包括对页表的隐式引用。 PMP 检查适用于所有这些访问。 隐式页表访问的有效特权模式是管理员模式。

允许使用虚拟内存的实现以推测方式执行地址转换,并且比显式虚拟内存访问所需的时间更早。 可以在地址转换和显式虚拟内存访问之间的任何时候检查结果物理地址的 PMP 设置。 到不可执行地址范围的错误预测分支不会生成陷阱。 因此,当以影响保存页表的物理内存或页表指向的物理内存的方式修改 PMP 设置时,M 模式软件必须将 PMP 设置与虚拟内存系统同步。 这是通过在写入 PMP CSR 后执行 rs1=x0 和 rs2=x0 的 SFENCE.VMA 指令来实现的。

如果未实现基于页面的虚拟内存,或者当它被禁用时,内存访问会同步检查 PMP 设置,因此不需要 fence。

PMP 限制

在包含多个 hart 的系统中,每个 hart 都有自己的 PMP 设备。 hart 上的 PMP 权限不能应用于多 hart 系统中其他 hart 的访问。 此外, SiFive 设计可能包含一个前端端口,以允许外部总线主控器访问系统的完整内存映射。 PMP 无法阻止前端端口上的外部总线主控器的访问。

没有 PMP 保护的区域的行为

如果内存映射的非保留区域没有应用 PMP 权限,则默认情况下,管理员或用户模式访问将失败,而机器模式访问将被允许。

访问设备内存映射中的保留区域(例如中断控制器)读取时将返回 0x0,写入将被忽略。 在没有 PMP 保护的情况下访问设备内存映射之外的保留区域将导致总线错误。 总线错误可以使用总线错误单元 (BEU) 对 hart 产生中断。

PMP 保护区的缓存刷新行为

当一条线被带入高速缓存并且 PMP 设置为断言锁定 (L) 位以保护该线的一部分时,数据高速缓存刷新指令将生成存储访问错误异常,如果冲洗包括受保护的线路的任何部分。 缓存刷新指令执行无效和回写,因此它实际上是在尝试写回受保护的内存位置。

如果缓存刷新发生在未受保护的部分行上,刷新将成功并且不会产生异常。 如果需要在没有回写的情况下刷新数据高速缓存,请改用高速缓存丢弃指令,因为这会使该行无效但不会回写。

你可能感兴趣的:(RISC-V,Linux,#,——内存管理篇,risc-v)