泰晓科技 -- 聚焦 Linux - 追本溯源,见微知著!
网站地址:https://tinylab.org

儿童Linux系统,可打字编程学数理化
请稍侯

RISC-V 物理内存保护(PMP)机制探究

Kepontry 创作于 2023/10/13

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 字段用于指定地址区域的计算方式,将在“地址区域计算规则”一节中介绍。

76-54-3210
L0AXWR

尝试从没有执行权限的 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名称描述
0OFF区域为空(禁用)
1TOR紧邻下一边界
2NA44 字节大小区域
3NAPOT2 的幂次方大小区域,大小≥8 字节

在 NAPOT 规则中,PMP 地址寄存器的低位来用来表示区域的大小,高位表示以 4 字节为单位的基址,中间用一个 0 隔开。如下表所示,如果用 G 表示 0 的下标,则 PMP 访问控制区域大小为 2^(G+3) 字节。

pmpaddrpmpcfg.AMatch type and size
yyyy…yyyyNA44-byte NAPOT range
yyyy…yyy0NAPOT8-byte NAPOT range
yyyy…yy01NAPOT16-byte NAPOT range
yyyy…y011NAPOT32-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 与开发板,展开进一步的实验与测试。

参考资料



Read Album:

Read Related:

Read Latest: