MMU Virtualization via Intel EPT: Technical Details
概述
本文是5篇系列文章中的第一篇,涵盖使用Intel EPT实现内存管理单元(MMU)的虚拟化。该技术作为物理内存虚拟化的额外支持,允许hypervisor监控内存活动。本文将讨论扩展页表的动机、相关的性能问题,以及MMU虚拟化的不同架构组件。组件将详细覆盖,但大部分信息在Intel SDM中。本文不会讨论任何操作系统特定的内容——仅涵盖正确实现所需了解的架构细节。
免责声明 读者必须具有虚拟内存、分页、地址转换和页表的基础知识。这些信息在Intel SDM §4.1.0 V-3A中。
内存和MMU
在本概述中,我们将涵盖与内存管理单元和分页相关的一些重要概念。这绝不是对Intel架构分页和虚拟内存的完整论述,而更多是一个抽象概述,以帮助读者更好地连接点。 — 物理和虚拟内存 物理内存存在于物理卡如DIMM模块和存储设备如硬盘上。假设熟悉计算机科学的基本概念,回想一下,在任何进程执行之前,可执行文件必须映射到物理内存中。现在,在现代系统上,有一个称为虚拟内存的辅助内存存储空间。在理想情况下,运行程序所需的数据将直接映射到RAM中,以便处理器快速访问。遗憾的是,我们并不生活在完美世界中,系统的主内存可能会满。虚拟内存登场。这种辅助形式的内存利用存储设备如硬盘来释放物理内存中的空间。然而,我们暂时不关心虚拟内存。在设置EPT时,我们首先需要了解一些关于物理内存的关键细节。
当计算机开始其启动序列时,在引导处理器上执行的代码可以直接访问物理内存。这是因为处理器在实地址模式下运行。恰当地命名,因为实模式中的地址对应于它们的物理内存地址。此时,还有几个物理内存范围可供操作系统/引导加载程序使用。如果我们断点一个系统并转储存在的物理内存范围,我们将能够看到所谓的系统内存映射。以下是在MmInitSystem之前应用断点时物理内存范围的图像。
图像显示物理内存范围及其大小。第一个范围从1000h到A0000h可用作操作系统消耗的一般DRAM。这个内存范围也称为低内存——有时是DOS兼容内存。那么,这堆废话的目的是什么?在启动序列期间,BIOS做很多事情,但与本系列最相关的是将不同的缓存行为应用于物理内存范围。BIOS通过编程称为内存类型范围寄存器(MTRR)的东西来实现这一点。这些是一组控制寄存器,使系统能够控制特定内存范围的缓存方式。缓存要求的细节因系统而异。例如,物理内存范围1000h-9FFFFh是写回。而范围A0000h-BFFFFh是写组合或未缓存。
如果你想知道MTRR的相关性,别担心。我们会讲到那个… 𝛿 内存类型范围寄存器(MTRR) 物理内存有范围,每个范围在系统初始化期间应用缓存控制策略。为什么这很重要?首先,对内存区域应用适当的缓存策略对于确保系统性能不下降至关重要。如果一个频繁访问的内存区域未缓存,频繁的数据获取将显著降低系统性能。这是因为应用程序通常以高度的局部性访问数据。如果数据不在缓存中,则CPU将必须访问主内存来获取它——访问主内存很慢!这很重要,因为在分配内存和初始化EPT时,我们将必须构建所谓的MTRR映射。幸运的是,对于我们来说,已经有当前物理内存区域的MTRR映射,我们可以用作参考。
图0. MTRR编码表(Intel SDM)
图1. 物理机器上的MTRR映射。 从图像中,你可能注意到范围非常具体——这是因为Windows使用固定范围MTRR和一些可变范围MTRR。有了这些信息,很明显,在初始化期间对我们的扩展页表应用适当的缓存策略对于保持系统性能至关重要。也不需要担心,修改和为我们的VM创建MTRR映射是直接的。我们将在下一篇文章中构建MTRR映射时更详细地讨论。如果你渴望提前了解,请参阅推荐阅读。解决了这个问题,让我们谈谈MMU和页表的目的。
页属性表 除了MTRR,还有一个额外的缓存控制称为页属性表(PAT),供操作系统以更细的粒度(页面级别)控制缓存策略。这个缓存控制在下一篇文章中更详细。
— MMU 大多数现代处理器都实现了内存管理单元(MMU),提供访问保护和虚拟到物理地址转换。虚拟地址,简单来说,是软件使用的地址;物理地址是硬件在数据总线的地址线上输出的地址。Intel架构将虚拟内存划分为4KB页面(支持其他大小),物理内存划分为4KB帧。MMU通常包含转换后备缓冲区(TLB),并将在页表上执行操作,如硬件表遍历。一些MMU架构不会执行这些操作。这样做是为了让操作系统自由地以任何方式实现其页表。MMU架构为指令和数据缓存指定某些缓存策略,无论是将代码标识为可缓存或不可缓存,还是写回和写通数据缓存。这些策略也可能涵盖缓存访问权限。
MMU拆分 在某些处理器中,MMU可以拆分为指令内存管理单元(IMMU)和数据内存管理单元(DMMU)。第一个在指令获取时激活,后者在内存操作时激活。
Intel64架构的MMU架构提供覆盖16-EiB的物理地址空间。然而,在当前架构中,只有2^57个单位在新页表结构中可寻址。那仍然是约128-PiB的可用地址空间。MMU工作的简短和“简单”是这样的——MMU获取虚拟地址并使用它索引到一个表(TLB或页表)。这些表中的条目提供一个物理地址加上一些控制信号,可能包括缓存策略、条目是否有效、无效、受保护等。它也可能接收关于条目引用的内存是否被访问/修改的信号。如果条目有效,则虚拟地址转换为物理地址;MMU然后将使用控制信号中的信息来确定发生什么类型的内存事务。这些提到的表类似于目录结构。MMU将遍历页表以将虚拟地址转换为物理地址。现在在x86-64架构上,MMU通过一系列表映射内存——4或5个,取决于软件要求。
图1. 地址转换的简化图。
我们将在后面涵盖一些关于TLB及其在虚拟化上下文中的角色。既然我们现在知道了MMU的目的,让我们开始讨论Intel的EPT。
扩展页表(EPT)
Intel的扩展页表(EPT)技术,也称为二级地址转换(SLAT),允许VMM配置客户机感知的物理内存和真实物理内存之间的映射。它类似于虚拟页表,因为EPT使hypervisor能够指定客户机物理页面的访问权限。这允许hypervisor在客户机尝试访问无效或没有适当访问权限的页面时生成称为EPT违规的事件。这个EPT违规是我们将在本系列中利用的事件之一,因为它触发VM退出。
重要说明 IOMMU的虚拟化由称为VT-d的补充技术执行。本系列不会涵盖这一点。
这项技术非常有用。例如,可以利用EPT来保护hypervisor的代码和数据免受恶意代码修改。这将通过将VMM代码和数据的访问权限设置为只读来实现。除此之外,如果VMM用于白名单某些应用程序,它可以修改剩余物理地址空间的访问权限为只写。这将强制任何执行上的VM退出,以允许hypervisor验证故障页面。只是一个有趣的思维实验。
关于潜力已经足够了,让我们进入EPT的动机并解决相关的其他各种组件… — 动机 扩展Intel虚拟化技术的主要动机之一是减少所有VM退出的性能损失。这是通过在2008年向Nehalem处理器添加虚拟处理器标识符(VPID)来实现的。该领域的许多研究人员都知道,第一代技术强制在每个VMX转换时刷新转换后备缓冲区。这导致从VMX非根操作到根操作时性能显著下降。现在,如果你想知道TLB是什么或做什么,别担心——我们在下面的小节中简要介绍。如果VMM正在模拟对特定控制寄存器的存储或使用invlpg指令,这种性能损失也延伸到VM进入。
这种TLB条目无效发生在移动到CR3和CR4时,但还有其他与进程上下文标识符相关的条件,我们将在后面讨论。如果你不熟悉TLB是什么,那么我强烈建议重新访问Intel SDM中的地址转换部分。然而,下一节简要回顾它与EPT相关的内容。 — 转换后备缓冲区(TLB) 转换后备缓冲区(TLB)是一个缓存,存放虚拟到物理地址的映射——它遵循局部性原则,以减少CPU在转换虚拟地址时需要进行的分页结构遍历次数。为了简化,让我们看一个TLB填充、命中和未命中期间发生什么的例子。这将使后面的解释更容易理解。假设我们正在对虚拟地址0x00001ABC执行虚拟地址查找。这是三种情况下会发生什么的简化视图。 𝛿 TLB填充 当需要查找特定虚拟地址时,TLB是任何地址转换的第一站。然而,如果TLB为空,则需要一系列步骤以确保未来转换中更快的查找。在这种情况下,我们正在查找虚拟地址0x00001ABC。
第一步(1)将是转换单元检查TLB以确定虚拟地址的映射是否可用。转换单元将确定PTE不在TLB中,必须进行第二步(2),这将从主内存中的页表加载PTE。它使用虚拟页号0x00001索引到页表以定位PTE。你可以在页表中的索引1处看到值0xA。这个值代表物理页号(PPN),它将用于填充第一个TLB条目中的PPN字段。现在,由于TLB是虚拟地址到物理地址映射的缓存,我们将使用虚拟页号作为标签。这实现了VPN 0x1 -> PPN 0xA的映射要求。一旦TLB条目被填充,我们将使用物理页号0xA来完成转换,给我们物理地址0x0000AABC。这是TLB填充/TLB未命中过程的简化示例。最终结果如下。
𝛿 TLB未命中 + 驱逐 现在,当我们的TLB已满且我们的虚拟地址没有映射缓存在TLB中时会发生什么?这称为TLB未命中 + 驱逐,或只是TLB驱逐。使用与之前相同的虚拟地址,但TLB已满,让我们看一下完成地址转换的操作序列。
第一步与之前相同——转换单元去TLB查看虚拟页号1的映射是否可用(1)。然而,TLB已满,没有条目对应于虚拟地址的虚拟到物理映射。这意味着TLB将必须驱逐最旧的条目(2)。假设此之前的地址转换使用了虚拟页号3,因此驱逐将发生在标签0x4的第二个条目上。
驱逐之后,转换将继续通过遍历主内存中的页表并加载对应于虚拟页号1的PTE(3)。在定位到VPN 1的PTE后,被驱逐的TLB条目被替换为我们当前虚拟地址的映射(4)。物理页号将是0xA,标签0x1。
最后,地址转换将使用物理页号来完成地址转换,产生物理地址0x0000AABC。这似乎不是一个困难或繁琐的过程,但请记住,页表遍历并不这么简单,访问主内存很慢!如果在这个例子中虚拟页号是0会发生什么?如果你猜会发生页面错误,你是正确的,而页面错误非常慢。如果你在这个图中添加地址转换所需的所有表级别,你会看到TLB未命中将显著增加开销。以下是从这本书中取出的使用两个条目TLB的地址转换图像。
那么这与EPT有什么关系?嗯,如果你在使用EPT的虚拟化环境中,那么TLB未命中处理的成本会增加。这是因为将客户虚拟地址转换为主机物理地址的操作次数 dramatically 增加。硬件转换单元执行的内存引用的最坏情况可能比本地执行增加6倍。因此,对于虚拟化社区来说,减少与Intel VT-x和EPT相关的TLB未命中的频率和成本变得至关重要。有许多关于减少二维页表遍历长度、页面共享等的研究文章——但那是另一个时间的讨论。幸运的是,技术已经取得了飞跃,并引入了新机制。其中之一是虚拟处理器标识符(VPID)。 — 虚拟处理器标识符(VPID) 正如我们之前学到的,刷新TLB对性能是致命的。但Intel工程师意识到了这个问题,并在2008年在Nehalem架构中引入了虚拟处理器标识符。这个虚拟处理器标识符用作每个缓存的线性地址转换的标签(类似于上面的图)。这为处理器提供了一种识别(标签)不同虚拟处理器的不同地址空间的方法。更不用说,当使用VPID时,VM进入或VM退出不会发生TLB刷新。这具有显著的性能影响,因为当处理器尝试访问映射,其中VPID与TLB条目标签不匹配时,将发生TLB未命中——是否发生驱逐取决于情况。 当EPT和VPID激活时,逻辑处理器可以缓存VPID标签条目转换到的物理页号,以及关于访问权限和内存类型信息的信息。同样适用于VPID标签的分页结构条目,除了物理地址指向相关的分页结构而不是物理页帧。简要重要的是,每个客户CPU获得一个唯一的VPID,所有主机CPU使用VPID 0x0000。我们还将遇到一条指令invvpid,这对于将虚拟CPU迁移到新的物理CPU是必要的。该指令也可用于影子——例如当客户页表被VMM修改或客户控制寄存器被更改时。 关于VPID/EPT使用时可能缓存的信息,以及VPID的更多细节,在Intel SDM中有大量信息。这些信息的章节号在推荐阅读部分提供。这些小节旨在简要介绍你将在本系列中遇到的术语和功能。 — 哦,扩展页表如何 理解分页结构和地址转换而不混合虚拟化可能会令人困惑。一旦我们引入某种形式的SLAT,在这种情况下——EPT,缓存和转换过程的复杂性增加。这就是为什么在文章开头建议你有一些翻译过程的背景。注意到这一点,让我们看看在使用4级分页的系统上典型的转换过程是什么样的。
在这张图片中,你将看到将虚拟地址转换为物理地址的通常过程,没有任何形式的SLAT。虚拟地址被提供给MMU,TLB执行查找以确定是否存在VA→PA映射。如果映射存在,我们得到TLB命中,这导致上面TLB部分详细描述的过程。否则,我们有TLB未命中,需要遍历分页结构以获取VA→PA转换。如果我们引入EPT,我们的内存管理结构变得更复杂。
从上面的图片中我们可以看到,具有硬件支持MMU虚拟化的处理器必须具有扩展的分页缓存。这些缓存是“主TLB”的一部分,可以这么说,它缓存GVA→GPA和GPA→HPA。硬件能够使用我们之前解决的TLB标签VPID来跟踪这两个映射。使用VPID的结果,如前所述,是VM转换不刷新TLB。这意味着各种VM的条目可以在TLB中共存而不冲突。这个主TLB消除了不断更新任何类型的影子页表的需要。然而,这有一个缺点。使用EPT使虚拟到主机物理地址转换显著更复杂——最明显的是如果我们在TLB中发生未命中。现在,这个图没有很好地覆盖复杂性,所以让我们谈谈转换如何与EPT工作。
关于主TLB
这个“主”TLB包含客户虚拟到客户物理映射和客户物理到主机物理映射。它还使用虚拟处理器标识符(VPID)来确定哪个TLB条目属于哪个VM。
对于客户转换中的每个步骤,我们必须做VMM中的所有步骤。当使用EPT时,分页结构中的地址不用作引用内存的物理地址——它们被视为客户物理地址,并通过一组EPT分页结构转换为真实物理地址。这意味着当我们没有适当的TLB条目时,遍历需要16次查找,而不是非虚拟化环境中的4次查找——哎呀。这就是为什么我想强调TLB未命中……是坏的!同样值得一提的是,CPU上的TLB比前几代大得多。还有更多,但我想在实现之前介绍这个过程,这样你在下一篇文章中不会非常困惑。在结束本文之前,我们需要讨论另一个对于模拟MMU时提高转换速度至关重要的主题。 — 虚拟TLB(vTLB) 我们现在知道虚拟化环境中的虚拟到物理地址转换是昂贵的。遏制这种性能影响的方法之一是在软件中模拟TLB硬件。这就是实现虚拟TLB(vTLB)所需要的。我们不会在本系列中实现虚拟TLB,但值得知道它是一个可能的解决方案。虚拟TLB通常是一个完整的线性查找表。在具有vTLB支持的Intel处理器上,我们必须通过修改一些VMCS字段来启用虚拟TLB方案。我们必须陷阱#PF异常,在所有CR3写入时VM退出,并启用invlpg退出。然而,Intel SDM中指出,这些组合可能不会产生最佳性能。你可以在Intel SDM §32.3.4 Volume 3C中阅读更多关于利用虚拟TLB方案的信息。 如果你已经走到这一步,你准备开始在你的hypervisor中实现EPT的旅程。
结论
在本文中,我们涵盖了关于应用于内存的缓存策略的信息,以及它们在分配我们的分页结构时将如何被利用。我们讨论了MMU及其目的,以及一些对性能地址转换至关重要的组件。我们还讨论了EPT的动机,并在硬件TLB上比我预期的更详细。我想在本文中介绍这些主题,以便本系列未来文章的广度不会 overwhelming。重要的是你理解这些子主题背后的技术细节和目的。特别重要的是虚拟处理器标识符和TLB操作。本文中的抽象概述应该足够,但请准备在接下来的部分中更多细节。 在下一篇文章中,我们将直接深入构建MTRR映射并涵盖页属性表(PAT)。我将提供预制结构并解释在你的hypervisor中初始化EPT。我们将涵盖身份映射、为我们的EPTP设置PML4/PML5条目、分配我们的各种页目录,以及如何实现4KB页面 versus 2MB页面。除此之外,将提供关于EPT违规/EPT错误配置的细节,以及如何实现它们的VM退出处理程序。最简单的部分是将我们的EPTP插入我们的VMCS。不幸的是,下一篇文章将只是配置和初始化,而后续文章将提供监控内存活动的不同方法。
我提前为本文可能不稳定的结构道歉。当我写它时,我意识到可能缺少很多内容,并开始尝试找到一种自然覆盖主题的方式。已经有一段时间了,所以我必须再次伸展我的写作肌肉。一如既往,感谢阅读——请随时联系我或在下面的文章上留下评论。 做推荐阅读! 推荐阅读
- Intel SDM §32.3 Volume 3C – MMU Virtualization
- Intel SDM §28.3.1 Volume 3C – Information Cached with VPID
- Intel SDM §28.1 Volume 3C & §4.11.2 Volume 3A – Virtual Processor Identifiers
- Virtual Memory Videos – David Schaffer
- Pre-Nehalem TLB Splitting
- Hypervisor from Scratch – Part 7 (@Sinaei)