三种地址

  • 客户虚拟地址Guest Virtual Address
  • 客户物理地址Guest Physical Address
  • 主机物理地址Host Physical Address

GPAHPA进行管理,是VMM需要负责的工作。

内存虚拟化的方式

影子页表

set_cr3 (guest_page_table):
	for GVA in 0 to pow(2, 20)
        if guest_page_table[GVA] & PTE_P:
			GPA = guest_page_table[GVA] >> 12
            HPA = host_page_table[GPA] >> 12
            shadow_page_table[GVA] = (HPA << 12) | PTE_P
        else
            shadow_page_table[GVA] = 0
   	CR3 = PHYSICAL_ADDR(shadow_page_table)

​ 这里遍历了页表,将影子页表设置为了将GVA翻译为HPA的结构,这个过程需要遍历所有GVA项,最后将影子页表基地址设置到CR3中。

​ 这样VMM就可以拥有一个独立的影子页表,相应的,一旦guest OS修改页表,影子页表也要做相应的更新。

​ 为了达到这样的目的,这里使用一些手法,将这些页中的所有页都设置为只读,如果guest OS修改了这些页,那么硬件就会触发缺页异常。这样VMM就可以处理缺页异常,更新影子页表。如何将Guest OSGuest app进行隔离?在影子页表的实现中,需要同时维护两个页表,当guest os切换到UVMM也要做切换影子页表的操作。

​ 如果切换到U,我们需要调用set_ptp(current, 0), 这样只有标注为User,会被添加到影子页表中。

直接映射

​ 这是一种半虚拟化的方式,我们需要直接修改guest OS代码,只用GVAHPAGuest OS接着操纵它的HPA空间,然后使用hypercall将自己要修改的信息告诉VMMVMM来更新页表,CR3指向这个GUEST OS的页表。

硬件虚拟化对于内存翻译的支持

​ 创建一个新的页表将GPA翻译为HPA,这个表由VMM直接控制,每个VM一个表,Intel称之为EPTARM上叫Stage-2 Page Table。这样在翻译完成GVA->GPA后会自动进一步的翻译。

由于本身一次非虚拟化页表的页表访问需要4次内存访问,但是在虚拟化场景下,这里的地址都变为了GPA了,需要进行进一步的翻译。不同的是,每一级的都要将GPA翻译为HPA,总共需要24次访存。

​ 这里TLB可以缓存GVA->HPA,大大加速了翻译过程。同时一阶段页表缺失不会引起VM Exit.