虚拟化及地址空间转换(page translation table)

(全)虚拟化技术解决的问题:
如何让在虚拟硬件环境中运行的guest系统的代码在真实CPU上运行?
a. 无硬件虚拟化支持(软触发)
将guest系统中需要触发硬件的指令保存并替换成异常指令,以便陷入进而转入VMM进行处理
b. 有硬件虚拟化支持(硬触发)
将所有影响系统级配置(host系统有可能因为它们的改动而down掉)的操作作上虚拟化标记


VT-x地址空间转换(page translation table)
对于没有ept硬件支持的,需要维护如下几个表:
1. 真实页表:寄存器GDTR所指向的真实GDT表,完成guest读写内存时从线性地址到真实物理地址的转换,转换过程和普通情况一样,由硬件自动完成;
2. 虚拟页表:VMCS(virtual-machine control structure)中存储的GDTR的一份读拷贝(read shadow)所指向的GDT表,它是给guest系统“看”的(欺骗guest系统),每当guest需要读写page translation table(一级、二级映射表)时,首先会读取GDTR这个索引,再依次读取pdt、pt表项,读取GDTR时,CPU将返回VMCS中存储的read shadow,它与GDTR所指向的真实GDT表并非同一个,它存储的是从guest线性地址到VMM为guest分配的线性地址(guest用作物理内存)的转换,guest系统只看得到虚拟页表(每次读GDTR,返回的都是VMCS中的read shadow);

如果guest没有对页表的写操作(如修改页表项的读写权限、存在标志位、设置该entry为空等等),以上两个页表已经完全满足需求:当guest的某线程读/写某个内存地址,产生page fault时,触发VMX产生VM exit,切换到VMM,VMM将分配一块host的物理内存,将该物理内存地址填入guest的真实页表中,然后VMM向VM注入一个page fault异常,VM enter进入VM后,guest接收到该page fault,执行guest系统的page fault处理程序,这一过程将涉及到虚拟页表。
对于以Linux作为guest的系统,该page fault处理流程大致如下:首先从全局分配一块空闲的page物理内存(对VMM而言,该物理内存仅仅是guest所在VM线程线性地址空间中还未分配出去的空闲地址),对应guest内核的一个struct page结构体,然后guest将得到的地址填入虚拟页表(实际上这里还是有写操作!),下一次guest读取页表中的内容(如获取某个page entry的权限),就可以得到在这里分配出去的值。虚拟页表的作用仅仅是给guest“看”,而不会起到读写内存时从线性地址到真实物理地址转换的作用。

至此还有一个问题未解决,由于guest自认为虚拟页表就是自己真实页表,guest修改虚拟页表中的内容后,如修改页表项的读写权限、存在标志位、设置该entry为空等等,将预期产生相应的结果,例如设置页表项为只读后,对该页表项所指向的内存的写操作应当引发write page fault,然而由于真实页表中的内容仍然保持原状,guest依旧可以对该内存进行写操作,这样,guest系统的执行语义不能得到保证。

为解决这一问题,可以通过设置让guest系统写PDT和PT内容时产生异常,例如将虚拟页表(PDT和PT)地址在真实页表中对应的页表项的可写标记全部关闭。
要修改某个页(page)的属性,首先会在虚拟页表中找到对应的页表项(page table entry),该页表项所在的地址最终还需真实页表来转换到host物理地址,将该页表项的地址在真实页表中对应的页表项设置成只读,这样每次guest修改虚拟页表项都会被VMX捕捉,通过产生异常后的VM exit进入VMM后,VMM通过适当的方法判断出本次异常是更新虚拟页表的操作,于是完成虚拟页表和真实页表内容的更新,保持两个页表的同步,并在真实页表项中继续保留只读属性,以便下一次捕获。

真实页表由VMM来管理,虚拟页表由guest系统维护。










新的VMX中已经包含ept,硬件page translation table机制虚拟化变得更加完美。







~~end~~