SV39 多级页表的硬件机制 您所在的位置:网站首页 三级页表转换方法是什么意思 SV39 多级页表的硬件机制

SV39 多级页表的硬件机制

2024-07-08 18:37| 来源: 网络整理| 查看: 265

SV39 多级页表的硬件机制# 本节导读#

在上一小节中我们已经简单介绍了分页的内存管理策略,现在我们尝试在 RISC-V 64 架构提供的 SV39 分页硬件机制的基础上完成内核中的软件对应实现。由于内容过多,我们将分成两个小节进行讲解。本节主要讲解在 RISC-V 64 架构下的虚拟地址与物理地址的访问属性(可读,可写,可执行等),组成结构(页号,帧号,偏移量等),访问的空间范围,硬件实现地址转换的多级页表访问过程等;以及如何用Rust语言来设计有类型的页表项。

虚拟地址和物理地址# 内存控制相关的CSR寄存器#

默认情况下 MMU 未被使能(启用),此时无论 CPU 位于哪个特权级,访存的地址都会作为一个物理地址交给对应的内存控制单元来直接访问物理内存。我们可以通过修改 S 特权级的一个名为 satp 的 CSR 来启用分页模式,在这之后 S 和 U 特权级的访存地址会被视为一个虚拟地址,它需要经过 MMU 的地址转换变为一个物理地址,再通过它来访问物理内存;而 M 特权级的访存地址,我们可设定是内存的物理地址。

注解

M 特权级的访存地址被视为一个物理地址还是一个需要经历和 S/U 特权级相同的地址转换的虚拟地址取决于硬件配置,在这里我们不会进一步探讨。

上图是 RISC-V 64 架构下 satp 的字段分布,含义如下:

MODE 控制 CPU 使用哪种页表实现;

ASID 表示地址空间标识符,这里还没有涉及到进程的概念,我们不需要管这个地方;

PPN 存的是根页表所在的物理页号。这样,给定一个虚拟页号,CPU 就可以从三级页表的根页表开始一步步的将其映射到一个物理页号。

当 MODE 设置为 0 的时候,代表所有访存都被视为物理地址;而设置为 8 的时候,SV39 分页机制被启用,所有 S/U 特权级的访存被视为一个 39 位的虚拟地址,它们需要先经过 MMU 的地址转换流程,如果顺利的话,则会变成一个 56 位的物理地址来访问物理内存;否则则会触发异常,这体现了分页机制的内存保护能力。

39 位的虚拟地址可以用来访问理论上最大 \(512\text{GiB}\) 的地址空间,而 56 位的物理地址在理论上甚至可以访问一块大小比这个地址空间的还高出几个数量级的物理内存。但是实际上无论是虚拟地址还是物理地址,真正有意义、能够通过 MMU 的地址转换或是 CPU 内存控制单元的检查的地址仅占其中的很小一部分,因此它们的理论容量上限在目前都没有实际意义。

地址格式与组成#

我们采用分页管理,单个页面的大小设置为 \(4\text{KiB}\) ,每个虚拟页面和物理页帧都对齐到这个页面大小,也就是说虚拟/物理地址区间 \([0,4\text{KiB})\) 为第 \(0\) 个虚拟页面/物理页帧,而 \([4\text{KiB},8\text{KiB})\) 为第 \(1\) 个,以此类推。 \(4\text{KiB}\) 需要用 12 位字节地址来表示,因此虚拟地址和物理地址都被分成两部分:它们的低 12 位,即 \([11:0]\) 被称为 页内偏移 (Page Offset) ,它描述一个地址指向的字节在它所在页面中的相对位置。而虚拟地址的高 27 位,即 \([38:12]\) 为它的虚拟页号 VPN,同理物理地址的高 44 位,即 \([55:12]\) 为它的物理页号 PPN,页号可以用来定位一个虚拟/物理地址属于哪一个虚拟页面/物理页帧。

地址转换是以页为单位进行的,在地址转换的前后地址的页内偏移部分不变。可以认为 MMU 只是从虚拟地址中取出 27 位虚拟页号,在页表中查到其对应的物理页号(如果存在的话),最后将得到的44位的物理页号与虚拟地址的12位页内偏移依序拼接到一起就变成了56位的物理地址。

注解

RISC-V 64 架构中虚拟地址为何只有 39 位?

在 64 位架构上虚拟地址长度确实应该和位宽一致为 64 位,但是在启用 SV39 分页模式下,只有低 39 位是真正有意义的。SV39 分页模式规定 64 位虚拟地址的 \([63:39]\) 这 25 位必须和第 38 位相同,否则 MMU 会直接认定它是一个不合法的虚拟地址。通过这个检查之后 MMU 再取出低 39 位尝试将其转化为一个 56 位的物理地址。

也就是说,所有 \(2^{64}\) 个虚拟地址中,只有最低的 \(256\text{GiB}\) (当第 38 位为 0 时)以及最高的 \(256\text{GiB}\) (当第 38 位为 1 时)是可能通过 MMU 检查的。当我们写软件代码的时候,一个地址的位宽毋庸置疑就是 64 位,我们要清楚可用的只有最高和最低这两部分,尽管它们已经巨大的超乎想象了;而本节中我们专注于介绍 MMU 的机制,强调 MMU 看到的真正用来地址转换的虚拟地址只有 39 位。

地址相关的数据结构抽象与类型定义#

正如本章第一小节所说,在分页内存管理中,地址转换的核心任务在于如何维护虚拟页号到物理页号的映射——也就是页表。不过在具体实现它之前,我们先将地址和页号的概念抽象为 Rust 中的类型,借助 Rust 的类型安全特性来确保它们被正确实现。

首先是这些类型的定义:

// os/src/mm/address.rs #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct PhysAddr(pub usize); #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct VirtAddr(pub usize); #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct PhysPageNum(pub usize); #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct VirtPageNum(pub usize);

上面分别给出了物理地址、虚拟地址、物理页号、虚拟页号的 Rust 类型声明,它们都是 Rust 的元组式结构体,可以看成 usize 的一种简单包装。我们刻意将它们各自抽象出不同的类型而不是都使用与RISC-V 64硬件直接对应的 usize 基本类型,就是为了在 Rust 编译器的帮助下,通过多种方便且安全的 类型转换 (Type Conversion) 来构建页表。

首先,这些类型本身可以和 usize 之间互相转换,以物理地址 PhysAddr 为例,我们需要:

// os/src/mm/address.rs const PA_WIDTH_SV39: usize = 56; const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; impl From for PhysAddr { fn from(v: usize) -> Self { Self(v & ( (1 usize { self.0 & (PAGE_SIZE - 1) } 5} 6 7impl From for PhysPageNum { 8 fn from(v: PhysAddr) -> Self { 9 assert_eq!(v.page_offset(), 0); 10 v.floor() 11 } 12} 13 14impl From for PhysAddr { 15 fn from(v: PhysPageNum) -> Self { Self(v.0 PhysPageNum { PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) } }

我们暂时先介绍这两种最简单的类型转换。

页表项的数据结构抽象与类型定义#

第一小节中我们提到,在页表中以虚拟页号作为索引不仅能够查到物理页号,还能查到一组保护位,它控制了应用对地址空间每个虚拟页面的访问权限。但实际上还有更多的标志位,物理页号和全部的标志位以某种固定的格式保存在一个结构体中,它被称为 页表项 (PTE, Page Table Entry) ,是利用虚拟页号在页表中查到的结果。

上图为 SV39 分页模式下的页表项,其中 \([53:10]\) 这 \(44\) 位是物理页号,最低的 \(8\) 位 \([7:0]\) 则是标志位,它们的含义如下(请注意,为方便说明,下文我们用 页表项的对应虚拟页面 来表示索引到一个页表项的虚拟页号对应的虚拟页面):

V(Valid):仅当位 V 为 1 时,页表项才是合法的;

R(Read)/W(Write)/X(eXecute):分别控制索引到这个页表项的对应虚拟页面是否允许读/写/执行;

U(User):控制索引到这个页表项的对应虚拟页面是否在 CPU 处于 U 特权级的情况下是否被允许访问;

G:暂且不理会;

A(Accessed):处理器记录自从页表项上的这一位被清零之后,页表项的对应虚拟页面是否被访问过;

D(Dirty):处理器记录自从页表项上的这一位被清零之后,页表项的对应虚拟页面是否被修改过。

除了 G 外的上述位可以被操作系统设置,只有 A 位和 D 位会被处理器动态地直接设置为 1 ,表示对应的页被访问过或修过( 注:A 位和 D 位能否被处理器硬件直接修改,取决于处理器的具体实现)。让我们先来实现页表项中的标志位 PTEFlags :

// os/src/main.rs #[macro_use] extern crate bitflags; // os/src/mm/page_table.rs use bitflags::*; bitflags! { pub struct PTEFlags: u8 { const V = 1


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有