[置顶] 泰晓 RISC-V 实验箱,配套 30+ 讲嵌入式 Linux 系统开发公开课
RISC-V 物理内存保护(PMP)机制探究
Corrector: TinyCorrect v0.1 - [codeinline tables urls pangu autocorrect] Author: Kepontry Kepontry@163.com Date: 2023/7/26 Revisor: Falcon falcon@tinylab.org; Bin Meng bmeng@tinylab.org Project: RISC-V Linux 内核剖析 Proposal: VisionFive 2 开发板软硬件评测及软件 gap 分析 Sponsor: PLCT Lab, ISCAS
前言
本文是物理内存保护(Physical Memory Protection,PMP)系列的第一篇文章,主要从官方数据手册出发,介绍 PMP 机制的原理与设置。
物理内存保护(即限制 hart 上软件可以访问的物理地址范围)可以提供安全处理和故障隔离功能,对现代处理器来说是非常重要的。PMP 机制适用于所有特权模式为 S 或 U 的指令和数据访问,通过在 M 态下修改每个 hart 对应的控制寄存器,可以指定每个物理内存区域的读、写和执行等访问权限。此外,PMP 机制也可用于 S 态中的页表访问。违反 PMP 机制的访存将被处理器捕获并触发异常。
物理内存保护机制
在 RISC-V 特权指令集手册 的第 3.7 节中介绍了内存保护机制。每一个 PMP 项定义了一个访问控制区域。一个 PMP 项包括一个 8 位控制寄存器和一个地址寄存器,仅可在 M 态下访问,处理器支持最多 64 个 PMP 项。PMP 访问控制区域的大小是可设置的,最小可支持 4 字节大小的区域。
PMP 控制寄存器
在处理器实现中,为了最小化上下文切换时间,PMP 项中的控制寄存器被打包存储在 CSR 寄存器中。在 RV32 中,每个 CSR 寄存器为 32 位,能存储 4 个 PMP 控制寄存器,共有 16 个 CSR 寄存器(pmpcfg0-pmpcfg15),包含 64 个 PMP 控制寄存器(pmp0cfg-pmp63cfg)。在 RV64 中,每个 CSR 寄存器能存储 8 个 PMP 控制寄存器,只需要 8 个 CSR 寄存器即可实现相同功能。为了与 RV32 保持一致,降低软件支持成本,RV64 中的 CSR 寄存器编号均为偶数,即 pmpcfg0,pmpcfg2,…,pmpcfg14,奇数编号的寄存器是非法的。
如下表所示,PMP 控制寄存器共包括 L、A、X、W 和 R 共五个字段,R、W 和 X 位表示对应的地址区域是否具有读、写和指令执行权限。其中,R=0 且 W=1 表示该内存区域被保留。L 位用于锁定区域,上锁后,对相应控制和地址寄存器的写入将被忽略。已锁定的 PMP 区域只能通过系统复位解锁。A 字段用于指定地址区域的计算方式,将在“地址区域计算规则”一节中介绍。
7 | 6-5 | 4-3 | 2 | 1 | 0 |
---|---|---|---|---|---|
L | 0 | A | X | W | R |
尝试从没有执行权限的 PMP 区域中提取指令,从没有读取权限的 PMP 区域中读取数据,向没有写入权限的 PMP 区域执行写入、条件写入或 AMO 指令,都将将引发异常。
PMP 地址寄存器
每个 PMP 地址寄存器对应一个 CSR 寄存器,命名为 pmpaddr0-pmpaddr63。在 RV32 中,寄存器存储 34 位物理地址的第 2 至第 33 位(使用 Sv32 分页机制)。在 RV64 中,寄存器存储 56 位物理地址的第 2 至第 55 位(使用 Sv39 或 Sv48 分页机制)。
地址区域计算规则
PMP 地址寄存器中存放区域的基地址,而 PMP 控制寄存器中的 A 字段用于确定地址计算规则,从而确定区域的范围。
- 当 A=0 时,此 PMP 条目被禁用,不匹配任何地址。
- 当 A=1 时,计算规则为“紧邻边界”(Top of Range,TOR)即区域 X 的结束地址紧邻下一区域的基地址寄存器 pmpaddrX 的值。该地址区域可表示为 [pmpaddr(X-1), pmpaddrX)。特别的,PMP 条目 0 表示的地址区域为 [0, pmpaddr0)。如果 pmpaddri−1 ≥ pmpaddri,则 PMP 条目 i 不匹配任何地址,即区域为空。
- 当 A=2 时,区域大小固定为 4 字节(Naturally aligned four-byte region,NA4)。
- 当 A=3 时,区域大小可变,为 2 的幂次方大小区域(Naturally aligned power-of-two region,NAPOT)。
A | 名称 | 描述 |
---|---|---|
0 | OFF | 区域为空(禁用) |
1 | TOR | 紧邻下一边界 |
2 | NA4 | 4 字节大小区域 |
3 | NAPOT | 2 的幂次方大小区域,大小≥8 字节 |
在 NAPOT 规则中,PMP 地址寄存器的低位来用来表示区域的大小,高位表示以 4 字节为单位的基址,中间用一个 0 隔开。如下表所示,如果用 G 表示 0 的下标,则 PMP 访问控制区域大小为 2^(G+3) 字节。
pmpaddr | pmpcfg.A | Match type and size |
---|---|---|
yyyy…yyyy | NA4 | 4-byte NAPOT range |
yyyy…yyy0 | NAPOT | 8-byte NAPOT range |
yyyy…yy01 | NAPOT | 16-byte NAPOT range |
yyyy…y011 | NAPOT | 32-byte NAPOT range |
. . . . . . . | . . . . . | . . . . . . . . . . . . . . . |
权限检查
如果一个 PMP 条目匹配访问的所有字节,则由 L、R、W 和 X 位确定访问是成功还是失败。除了锁定 PMP 条目外,L 位还指示对 M 模式访问是否执行 R/W/X 权限检查。如果 L 位被清除,且访问的特权模式为 M,则访问成功。否则,如果 L 位被设置或访问的特权模式为 S 或 U,则只有与访问类型对应的 R、W 或 X 位被设置时访问才成功。
如果没有 PMP 条目与 M 模式访问匹配,则访问成功。如果没有 PMP 条目与 S 模式或 U 模式访问匹配,但至少实现了一个 PMP 条目,则访问失败。如果实现了至少一个 PMP 条目,但所有 PMP 条目的 A 字段都被设置为 OFF,则所有 S 模式和 U 模式内存访问都将失败。
优先级机制
PMP 条目间采用静态优先级,当区间重叠时,编号小的条目优先级更高。匹配到访问中任何字节的最低编号的 PMP 条目决定本次访问是成功还是失败。
原子性
匹配的 PMP 条目必须包含访问的所有字节,否则无论 L、R、W 和 X 位的值是什么,访问都将失败。例如,如果一个 PMP 条目被配置为匹配 4 字节区域 0xC–0xF,且为最高优先级条目,那么对区域 0x8–0xF 的 8 字节访问将失败。
需要注意的是,单条指令可能会生成多次访问,这些访问可能不是相互原子的。如果其中一个访问未通过 PMP 检查并产生异常,该指令的其他访问可能通过 PMP 检查、成功执行并最终可见,从而带来潜在危害。例如在某些实现中,不对齐的读取、写入和指令获取可能被分解成多个访问。
SiFive U7 系列 CPU 实现
在 SiFive 提供的 U74 数据手册 中介绍了 U7 系列 CPU 的物理内存保护机制实现,它共有 8 个地址寄存器,打包存储在一个 CSR 寄存器 pmpcfg0 中,支持 8 个访问控制区域。
该系列 CPU 支持的最小物理内存保护区域为 4KB,所以不支持 NA4 地址计算规则,NAPOT 地址计算规则也受此限制。通过分析 OpenSBI 源码 可以发现,在设置物理内存保护功能时,首先需要获取当前处理器支持的 PMP 粒度 pmp_gran_log2
,并判断即将设置的区域大小 reg->order
是否大于等于它(二者都已取为以 2 为底的对数)。如果满足条件,则执行 pmp_set
函数设置 PMP 寄存器,否则设置失败。
// lib/sbi/sbi_hart.c:392
int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
{
...
pmp_gran_log2 = log2roundup(sbi_hart_pmp_granularity(scratch));
...
sbi_domain_for_each_memregion(dom, reg) {
...
pmp_addr = reg->base >> PMP_SHIFT;
if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max)
pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
else {
sbi_printf("Can not configure pmp for domain %s", dom->name);
sbi_printf(" because memory region address %lx or size %lx is not in range\n",
reg->base, reg->order);
}
}
...
}
总结
本文简要介绍了 RISC-V 中的物理内存保护机制,后续将基于 QEMU 与开发板,展开进一步的实验与测试。
参考资料
猜你喜欢:
- 我要投稿:发表原创技术文章,收获福利、挚友与行业影响力
- 知识星球:独家 Linux 实战经验与技巧,订阅「Linux知识星球」
- 视频频道:泰晓学院,B 站,发布各类 Linux 视频课
- 开源小店:欢迎光临泰晓科技自营店,购物支持泰晓原创
- 技术交流:Linux 用户技术交流微信群,联系微信号:tinylab
支付宝打赏 ¥9.68元 | 微信打赏 ¥9.68元 | |
请作者喝杯咖啡吧 |
Read Album:
- TinyBPT 和面向 buildroot 的二进制包管理服务(1):设计简介与框架
- RISC-V Linux 内核及周边技术动态第 118 期
- RISC-V Linux 内核及周边技术动态第 117 期
- 实时分析工具 rtla timerlat 介绍(二):延迟测试原理
- 实时分析工具 rtla timerlat 介绍(一):交叉编译及使用