PMA 简介
PMA,即Physical Memory Attributes,物理内存属性
完整的系统物理内存映射包含各种地址空间范围:
- 常规内存区域
- MMIO区域
- 空洞
每个内存区域有自己的访问属性,可能是如下特性的某个集合
- 支持/不支持 读取、写入或者执行
- 支持/不支持 子字或者子块访问
- 支持/不支持 原子操作操作
- 支持/不支持 缓存一致性
- 具有不同的内存模型
常规内存区域具备上述某些指定的属性,比如可读、可写、可执行、支持原子操作等
I/O设备可以具有更多的属性
空洞被划分为IO区域,但是不支持任何访问
PMAs是底层硬件的固有属性,在系统运行过程中很少发生变化,取决于具体平台实现。
1. 一些内存区域的PMAs在芯片设计时固定,例如,对于芯片上的ROM。
2. 其他的在板设计时固定,这取决于连接到外部总线的其他芯片
例如,外部总线也可能支持在每次上电时更改的设备(冷插拔)或在系统运行时动态更改的设备(热插拔)。
3. 一些设备可能在运行时可配置以支持不同的用途,这意味着不同的PMAs
例如,一个芯片上的Scratchpad RAM可能在一个应用中被一个核心私有缓存,或者在另一个应用中被访问为共享的非缓存内存
对于RISC-V架构,将PMAs的规范和检查分开到一个单独的硬件结构中,称为PMA检查器(PMA checker)。 在许多情况下,这些属性在系统设计时对每个物理地址区域是已知的,并且可以硬连线到PMA检查器。
对物理内存的任何访问,包括经过虚拟到物理内存转换的访问,都会检查PMAs。
为了帮助系统调试,riscv指令集规格强烈建议在可能的情况下,RISCV处理器精确捕获未通过PMA检查的物理内存访问。 精确捕获的PMA违规行为会表现为指令、加载或存储访问故障异常,与虚拟内存页故障异常不同。
PMA会和下面介绍的PMP联合并行检查。
PMP介绍
物理内存保护(PMP)单元提供了每个hart机器模式控制寄存器,允许为每个物理内存区域指定物理内存访问权限(读、写、执行)。
PMP 用于强制执行对较低权限模式的访问限制,防止监管者模式和用户模式软件访问不需要的内存
最多 16 个区域,最小区域大小为 4 字节;具备锁定区域的能力
锁定的区域会对所有访问强制执行权限,包括 M 模式;解锁区域的唯一方式是进行复位
M模式是最高特权级别,通常具有对设备内存映射的整个内存范围的读取、写入和执行权限。
但是,低于M模式的特权级别在未经 PMP 允许的情况下,对设备内存映射的任何区域都没有读取、写入或执行权限。
对于较低特权级别,PMP 可以授予对设备内存映射的特定区域的权限,但在M模式下也可以撤销权限。
在编程时,当 hart 在S或U模式下运行时,PMP 将检查每次访问。
对于M模式,只有在特定区域的 pmpcfgY CSR 中设置了锁定位 (L) 时,PMP 检查才会发生。
当机器的上一个特权级别是S或U(mstatus.MPP=0x1 或 mstatus.MPP=0x0)并且设置了修改特权位(mstatus.MPRV=1)时,PMP 也会在加载和存储操作中进行检查。
对于虚拟地址转换,PMP 检查还应用于监管者模式下的页表访问。
PMP寄存器
PMP访问控制设置的粒度是特定于平台的,但标准的PMP编码支持最小为四个字节的区域。
某些区域的权限可以硬连线,例如,某些区域可能只在机器模式下可见,但在更低特权层中不可见。
不同平台对于物理内存保护的需求差异很大,一些平台可能除了或替代本节描述的方案之外还提供其他PMP结构。
PMP(物理内存保护)条目由一个8位配置寄存器和一个MXLEN(32/64)位的地址寄存器描述。
一些PMP设置还使用与前一个PMP条目相关联的地址寄存器。支持最多64个PMP条目。
实现可以实现零个、16个或64个PMP CSRs;必须首先实现最低编号的PMP CSRs。
所有PMP CSR字段均为WARL(写入后读取为零),并且可以是只读的零。
PMP CSRs只能由M模式访问。
PMP 配置寄存器被紧密打包到 CSR 中,以最小化上下文切换时间。
对于 RV32,有十六个 CSR,pmpcfg0 到 pmpcfg15,用于保存 64 个 PMP 条目的配置,如图 3.31 所示。
对于 RV64,有八个偶数编号的 CSR,pmpcfg0、pmpcfg2、…、pmpcfg14,用于保存 64 个 PMP 条目的配置,如图 3.32 所示。
对于 RV64,奇数编号的配置寄存器,pmpcfg1、pmpcfg3、…、pmpcfg15,是非法的,不可访问。
这减小了占用的空间,比如,pmpcfg2 已经包含了对于 RV32 和 RV64 的 pmp8cfg 到 pmp11cfg 的配置字段。
PMP地址寄存器
PMP 地址寄存器是名为 pmpaddr0–pmpaddr63 的 CSR。
每个 PMP 地址寄存器编码了 RV32 的 34 位物理地址的 33 到 2 位,如图 3.33 所示。
对于 RV64,每个 PMP 地址寄存器编码了 56 位物理地址的 55 到 2 位,如图 3.34 所示。
也就是说,对于一个34位地址,若要保存到PMP地址寄存器中,需要将数据右移两位后存储。
对于56位地址,将55~2位保存在PMP地址寄存器的53~0位。向右移动两位以实现最小4字节对齐。
PMP配置寄存器
图 3.35 显示了 PMP 配置寄存器的布局。
当设置了 R、W 和 X 位时,分别表示 PMP 条目允许读取、写入和执行指令。
当其中一个位清除时,相应的访问类型被拒绝。其中 R=0 和 W=1 的组合被保留。
A和L标记位稍后会介绍。
尝试从没有执行权限的 PMP 区域获取指令会引发指令访问故障异常。
尝试执行访问物理地址位于 PMP 区域内并且没有读权限的加载或加载保留指令会引发加载访问故障异常。
尝试执行访问物理地址位于 PMP 区域内并且没有写权限的存储、条件存储或 AMO 指令会引发存储访问故障异常。
地址匹配
PMP 条目配置寄存器中的 A 字段编码了相关的 PMP 地址寄存器的地址匹配模式。这些模式支持四字节粒度。该字段的编码如表 3.10 所示。
- 当 A=0 时,此 PMP 条目被禁用,并不匹配任何地址
- 自然对齐的 2 的幂次方区域(NAPOT)
- 自然对齐的四字节区域(NA4)的特殊情况
- 以及任意范围的顶部边界(TOR)
PMP配置寄存器 A 字段
NAPOT 区域利用相关地址寄存器的低位来编码范围的大小,如表 3.11 所示。
若pmpaddr值为yyyy…yyyy,此时控制的地址范围即是从yyyy…yyyy开始的4个字节,而pmpcfg.A的值为NA4,即Naturally Aligned Four-byte regions.
当pmpcfg.A为NAPOT时,从pmpaddr的低位开始寻找连续1的个数。
若pmpaddr值为yyyy…yyy0,即连续1的个数为0,则该PMP entry所控制的地址空间为从yyyy…yyy0开始的8个字节
若pmpaddr值为yyyy…yy01,即连续1的个数为1,则该PMP entry所控制的地址空间为从yyyy…yy00开始的16个字节
……
若pmpaddr值为y…y01…1,设连续1的个数为n,则该PMP entry所控制的地址空间为从y…y00…0开始的2^{n+3}个字节
这种控制地址范围的方式叫做自然对齐2的指数地址范围(Naturally Aligned Power-of-2 regions, NAPOT)
如果选择了 TOR 模式,相关的地址寄存器形成地址范围的顶部,而前一个 PMP 地址寄存器形成地址范围的底部。
即由前一个条目的尾地址和当前条目共同决定当前条目的地址范围。
如果 PMP 条目 i 的 A 字段设置为 TOR,该条目将匹配任何地址 y,满足 pmpaddr[i-1] ≤ y < pmpaddr[i](不考虑 pmpcfg[i-1] 的值)。
如果 PMP 条目 0 的 A 字段设置为 TOR,下界将使用零,因此它将匹配任何地址 0 ≤ y < pmpaddr0。
如果 pmpaddr[i-1] ≥ pmpaddr[i] 并且 pmpcfg[i].A = TOR,那么 PMP 条目 i 不匹配任何地址。
PMP配置寄存器 L 字段
L 位指示 PMP 条目被锁定,即配置寄存器和相关地址寄存器的写操作会被忽略。
锁定的 PMP 条目将保持锁定状态,直到 hart 被重置。
如果 PMP 条目 i 被锁定,对 pmpicfg 和 pmpaddr[i] 的写操作会被忽略。
此外,如果 PMP 条目 i 被锁定,并且 pmpicfg.A 被设置为 TOR,那么对 pmpaddr[i-1] 的写操作也会被忽略。
设置 L 位会锁定 PMP 条目,即使 A 字段被设置为 OFF 也是如此。
除了锁定 PMP 条目,L 位还指示 R/W/X 权限是否在 M 模式访问中执行。
- 当 L 位被设置时,这些权限对所有特权模式都执行。
- 当 L 位被清除时,与 PMP 条目匹配的任何 M 模式访问都会成功;R/W/X 权限仅适用于 S 和 U 模式。
优先级和匹配逻辑
PMP 条目具有静态优先级。匹配访问中的任何字节的最低编号的 PMP 条目决定了该访问是否成功或失败。
匹配的 PMP 条目必须匹配访问的所有字节,否则访问将失败,不考虑 L、R、W 和 X 位。
例如,如果一个 PMP 条目被配置为匹配四字节范围 0xC–0xF,那么对范围 0x8–0xF 的 8 字节访问将失败,假设该 PMP 条目是匹配这些地址的最高优先级条目。
如果一个 PMP 条目匹配访问的所有字节,那么 L、R、W 和 X 位决定了访问是否成功或失败。
如果 L 位被清除,并且访问的特权模式为 M,则访问成功。
否则,如果 L 位被设置或者访问的特权模式为 S 或 U,则仅当与访问类型对应的 R、W 或 X 位被设置时访问才成功。
如果没有 PMP 条目与 M 模式访问匹配,则访问成功。
如果没有 PMP 条目与 S 模式或 U 模式访问匹配,但至少一个 PMP 条目已经实现,访问将失败。
失败的访问会引发指令、加载或存储访问故障异常。
需要注意的是,单个指令可能会生成多个访问,这些访问可能不是相互原子的。
如果由指令生成的至少一个访问失败,将生成访问故障异常,尽管该指令生成的其他访问可能会成功,但可能会有可见的副作用。
值得注意的是,引用虚拟内存的指令会被分解为多个访问。
在某些实现中,不对齐的加载、存储和指令获取也可能会被分解为多个访问,在发生访问故障异常之前,其中一些访问可能会成功。
特别是,通过 PMP 检查的不对齐存储的一部分可能会变得可见,即使另一部分未通过 PMP 检查。
相同的行为也可能出现在比 XLEN 位更宽的浮点存储上(例如,RV32D 中的 FSD 指令),即使存储地址是自然对齐的。
物理内存保护和分页
物理内存保护机制被设计为与基于页的虚拟内存系统配合使用。
当启用分页时,访问虚拟内存的指令可能会导致多次物理内存访问,包括对页表的隐式引用。
所有这些访问都会应用 PMP 检查。隐式页表访问的有效特权模式是 S。
具有虚拟内存的实现允许在明确内存访问之前以及早于所需的时间进行地址转换,
并允许将它们缓存在地址转换缓存结构中,包括可能缓存有效地址到物理地址的身份映射,这些映射在 Bare 转换模式和 M 模式中使用。
生成的物理地址的 PMP 设置可以在地址转换和明确内存访问之间的任何时刻进行检查(并可能被缓存)。
因此,当修改 PMP 设置时,M 模式软件必须将 PMP 设置与虚拟内存系统以及任何 PMP 或地址转换缓存同步。
这可以通过在写入 PMP CSRs 后执行 SFENCE.VMA 指令,其中 rs1=x0 和 rs2=x0 来完成。
如果未实现基于页的虚拟内存,内存访问会同步检查 PMP 设置,因此不需要 SFENCE.VMA。
opensbi 探测 pmp
__check_csr_64
遍历 pmpaddr[0-63] 64个地址寄存器
如果某个地址寄存器未实现,或者说不支持 那么csr_read_allowed/csr_write_allowed时会触发异常__sbi_expected_trap_hext
那么该寄存器不会记录到hart features。
static int hart_detect_features(struct sbi_scratch *scratch)
{
......
/**
* Detect the allowed address bits & granularity. At least PMPADDR0
* should be implemented.
*/
val = hart_pmp_get_allowed_addr();
if (val) {
hfeatures->pmp_gran = 1 << (sbi_ffs(val) + 2);
hfeatures->pmp_addr_bits = sbi_fls(val) + 1;
/* Detect number of PMP regions. At least PMPADDR0 should be implemented*/
__check_csr_64(CSR_PMPADDR0, 0, val, pmp_count, __pmp_skip); // 遍历 pmpaddr[0-63] 64个地址寄存器
}
__pmp_skip:
......
}
mtvec原来存储的是_trap_handler,a5中原来是__sbi_expected_trap_hext
csrrw a5,mtvec,a5
执行完之后,二者数值互换,为pmpaddrX的检测做好准备
也就是说在读写pmpaddrX之前,先把 mtvec 指向 __sbi_expected_trap_hext
这样如果后续读写pmpaddrX 时如果发生异常,就会跳转到 __sbi_expected_trap_hext
该函数会把异常发生的原因及相关地址信息写入a3指向的 sbi_trap_info 结构体中
截取hart_detect_features
部分汇编代码
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK);
0x000000008000993c <+818>: srli a2,a2,0xa
0x000000008000993e <+820>: add a4,a3,zero // a3指向异常信息结构体首地址
0x0000000080009942 <+824>: csrrw a5,mtvec,a5 // a5 和 mtvec数值互换
0x0000000080009946 <+828>: csrw pmpaddr0,a2 // a2写入pmpaddr0
0x000000008000994a <+832>: csrw mtvec,a5 // _trap_handler重新写回mtvec
val = csr_read_allowed(CSR_PMPADDR0, (ulong)&trap);
0x0000000080009974 <+874>: add a4,a3,zero // a3指向异常信息结构体首地址
0x0000000080009978 <+878>: csrrw a5,mtvec,a5 // a5 和 mtvec数值互换
0x000000008000997c <+882>: csrr s6,pmpaddr0 // pmpaddr0 读取到s6
0x0000000080009980 <+886>: csrw mtvec,a5 // _trap_handler重新写回mtvec
0x0000000080009984 <+890>: ld a5,-104(s0)
__sbi_expected_trap_hext
扩展特性检测时触发异常的话,这块代码就会记录异常现场。比如检测PMP地址寄存器个数的时候。
.align 3
.global __sbi_expected_trap_hext
__sbi_expected_trap_hext:
/* With H-extension so, MTVAL2 and MTINST CSRs and GVA available */
csrr a4, CSR_MEPC
REG_S a4, SBI_TRAP_INFO_OFFSET(epc)(a3)
csrr a4, CSR_MCAUSE
REG_S a4, SBI_TRAP_INFO_OFFSET(cause)(a3)
csrr a4, CSR_MTVAL
REG_S a4, SBI_TRAP_INFO_OFFSET(tval)(a3)
csrr a4, CSR_MTVAL2
REG_S a4, SBI_TRAP_INFO_OFFSET(tval2)(a3)
csrr a4, CSR_MTINST
REG_S a4, SBI_TRAP_INFO_OFFSET(tinst)(a3)
/* Extract GVA bit from MSTATUS or MSTATUSH */
#if __riscv_xlen == 32
csrr a4, CSR_MSTATUSH
srli a4, a4, MSTATUSH_GVA_SHIFT
#else
csrr a4, CSR_MSTATUS
srli a4, a4, MSTATUS_GVA_SHIFT
#endif
andi a4, a4, 1
REG_S a4, SBI_TRAP_INFO_OFFSET(gva)(a3)
csrr a4, CSR_MEPC
addi a4, a4, 4
csrw CSR_MEPC, a4
mret
U74 core PMP介绍
U7 PMP 支持 8 个区域,最小区域大小为 64 字节。
PMP 功能描述
U7 PMP 单元具有 8 个区域,最小粒度为 64 字节。每个区域的访问由一个 8 位的 pmpXcfg 字段和一个对应的 pmpaddrX 寄存器控制。
允许有重叠的区域,其中较低编号的 pmpXcfg 和 pmpaddrX 寄存器优先于较高编号的区域。
U7 PMP 单元实现了体系结构定义的 pmpcfgY CSR pmpcfg0,支持 8 个区域。
pmpcfg2 已实现,但被硬连线为零。访问 pmpcfg1 或 pmpcfg3 会导致非法指令异常。
PMP 寄存器只能在 M 模式下编程。通常情况下,PMP 单元会强制执行 S 模式和 U 模式访问的权限。
但是,锁定的区域还会在 M 模式下强制执行其权限。
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 CSRs 中。
每个 8 位的 pmpXcfg 字段包括读取、写入和执行位,以及两位的地址匹配字段 A 和一个锁定位 L。允许有重叠的区域,其中最低编号的 PMP 条目获胜。
pmpcfgY 和 pmpaddrX 寄存器只能通过专门的 CSR 指令访问,例如 csrr 用于读取操作,csrw 用于写入操作。
在复位状态下,PMP 寄存器字段 A 和 L 被设置为 0。其他 hart 状态《RISC‑V 指令集手册,卷 II:特权架构,版本 1.10》并未明确指定。
PMP 编程示例
以下示例显示了仅在机器模式下进行配置的情况,其中 PMP 权限应用于三个感兴趣的区域,而第四个区域涵盖了其余的内存映射。
请记住,较低编号的 pmpXcfg 和 pmpaddrX 寄存器优先于较高编号的区域。
这一规则允许较高编号的 PMP 寄存器在整个内存映射上具有全面覆盖,同时允许较低编号的区域对感兴趣的特定区域应用权限。
以下示例显示了一个基地址为 0x0 的 64 KB 闪存区域,
一个基地址为 0x2000_0000 的 32 KB RAM 区域,
最后一个基地址为 0x3000_0000 的 4 KB 外设区域。
其余的内存映射是保留空间。
PMP 的限制
在一个包含多个 hart 的系统中,每个 hart 都有自己的 PMP 设备。
在多 hart 系统中,hart 上的 PMP 权限不能应用于来自其他 hart 的访问。
此外,SiFive 设计可能包含一个前端端口,允许外部总线主机访问系统的完整内存映射。
PMP 无法阻止来自前端端口的外部总线主机的访问。
对于没有 PMP 保护的区域的行为
如果内存映射的非保留区域没有应用 PMP 权限,那么默认情况下,监管者或用户模式的访问将失败,而机器模式的访问将被允许。
对设备内存映射内的保留区域(例如中断控制器)的访问将在读取时返回 0x0,并且写入将被忽略。
对于没有 PMP 保护的设备内存映射之外的保留区域的访问将导致总线错误。
总线错误可以通过使用总线错误单元(Bus-Error Unit,BEU)生成对 hart 的中断来处理。有关更多信息,请参阅第 12 章。
PMP 保护区域上的缓存刷新行为
当一行被装入缓存并且 PMP 被设置为保护该行的一部分的锁定(L)位被断言时,数据缓存刷新指令将在刷新包括受保护部分的行时生成存储访问故障异常。
缓存刷新指令执行无效和写回,因此它实际上尝试写回受保护的内存位置。
如果缓存刷新发生在未受保护的行的一部分上,刷新将成功并且不会生成异常。
如果需要不带写回的数据缓存刷新,请改用缓存丢弃指令,因为这将使行无效,但不会写回该行。
参考索引
The RISC-V Instruction Set Manual Volume II: Privileged Architecture 20211203
SiFive U74-MC Core Complex Manual
opensbi