VMX(VirtualMachine Extension)是Intel 64和IA-32架构处理器级别的功能,用于支持虚拟化。VMX支持两种类型的软件:

    Virtual-machinemonitor(VMM),VMM对处理器和平台硬件具有完全的控制权限。VMM为客户软件提供虚拟处理器的抽象,从而让客户软件能够直接在逻辑CPU上跑。
    客户软件(Guest software)。每个虚拟机(VM)就是一个客户软件运行环境。
    系统软件可以通过CPUID指令来判断处理器是否支持VMX,如果CPUID.1.ECX.VMX[bit5] = 1,那么当前处理器支持VMX,否则,当前处理器不支持VMX功能。



    VMX包括两种VMX操作操作模式:

    VMX根操作(VMXroot operation)模式,通常VMM将会在这种模式下运行;
    VMX非根操作(VMX non-root operation),通常客户软件(虚拟机)将在这种环境下运行。
    两种类型的操作之间的转换称作VMX转换,从根操作模式转换到非根操作模式称作VMX进入(VMX Entry),相反从非根操作模式转换到根操作模式称作VMX退出(VMX Exit)。

    当处理器运行于VMX根操作模式的时候,它类似于运行在没有打开VMX功能的环境下,跟不打开VMX功能的主要区别是,在VMX根操作模式中,可以执行VMX相关指令,并且有某些制寄存器的写被限制了

    当处理器运行于VMX非根操作模式的时候,处理器的某些行为被限制住或者被更改以便实现虚拟化,即某些指令或者事件会引起VM exits,从而让处理器从VMX非操作模式退出到根操作模式的VMM里,最终让VMM去模拟这些指令的行为从而实现虚拟化。

    另外,VMX的两种操作模式和处理器的运行级别是正交的,即在VMX根操作模式或者VMX非根操作模式中,都有相应的从ring0 ~ ring3的运行级别,而且两种模式下的各种运行级别是相互独立的。

    VMX功能尽量让软件不能从某个控制寄存器中得知当前自己正运行在VMX非特权模式下,这样就可以防止软件知道自己是否运行在一个虚拟机中。但是VMM其实是可以通过对某些指令的模拟,绕过这个机制的,比如在KVM中,KVM模块对CPUID指令的模拟,让虚拟机中执行CPUID指令的时候返回KVM相关的信息。



    从最高层次来看处理器使用VMX的步骤如下:

    通过CPUID指令判断VMX功能是否被当前处理器支持;
    如果当前处理器支持VMX功能,那么通过设置CR4.VMXE[bit13] = 1来打开VMX功能;
    通过VMXON指令开启VMX功能,并且激活VMM;
    在VMX中可以通过VM Entry和VM Exit来进入客户虚拟机或者返回到VMM中,即切换到VMX非根操作模式或者切换到VMX根操作模式,也可以利用此操作来切换运行的客户虚拟机;
    通过VMXOFF来关闭VMX功能,从而终止VMX的操作。

    VMX详解(网转我也没看懂) - 图1

    VMX非根操作模式和VMX转换都由位于内存的VMCS(Virtual Machine Control Structure)数据结构控制,每个逻辑CPU都有一个包含指向VMCS指针的寄存器。VMCS指针通过VMPTRST和VMPTRLD指令来读和写,并且VMM可以通过VMREAD、VMWRITE和VMCLEAR指令来配置VMCS。

    在虚拟化中,为了实现vCPU,既要模拟CPU的运行,又要记录vCPU的状态(包括对vCPU运行的控制信息),在Intel x86处理器的VMX(Virtual Machine Extension)功能中,通过引入根运行模式(VMX root operation)和非根模式(VMX non-rootoperation),直接让vCPU运行在逻辑CPU上,在软件上省去了对vCPU运行的模拟,同时也大大提升了性能。剩下的就是对vCPU状态的记录了,为此Intel引入了VMCS(Virtual Machine Control Structure)功能。

    VMCS(VirtualMachine Control Structure)是Intel x86处理器中实现CPU虚拟化,记录vCPU状态的一个关键数据结构。该数据结构主要包含如下六方面的信息:

    Guest-statearea,即vCPU的状态信息,包括vCPU的基本运行环境(如通用寄存器等)和一些非寄存器信息,如当前vCPU是否接收中断,是否有挂起的exception,VMCS的状态等等。
    Host-statearea,即主机物理CPU的状态信息,因为物理CPU是在主机CPU和vCPU之间来回切换运行的,所以在VMCS中既要记录vCPU的状态,也需要记录主机CPU的状态,这样vCPU才能有足够的信息恢复到原来主机CPU的状态,继续主机CPU的运行。其包含的具体信息和前面记录的vCPU的状态信息大体相同。
    VM-executioncontrol fields,该方面信息主要用于对vCPU的运行行为进行控制,这是VMM对vCPU进行配置最复杂的一部分,如
    控制vCPU在接收到某些中断事件的时候,是否直接在vCPU中处理掉,即虚拟机直接处理掉该中断事件还是需要退出到VMM中,让VMM去处理该中断事件
    是否使用EPT(Extended Page Table)功能
    是否允许vCPU直接访问某些I/O口,MSR寄存器等资源
    vCPU执行某些指令的时候,是否会出发VM Exit等等
    VM-exitcontrol fields,即对VM Exit的行为进行控制,如VM Exit的时候对vCPU来说需要保存哪些MSR寄存器,对于主机CPU来说需要恢复哪些MSR寄存器。
    VM-entrycontrol fields,即对VM Entry的行为进行控制,如需要保存和恢复哪些MSR寄存器,是否需要向vCPU注入中断和异常等事件(VM Exit的时候不需要向主机CPU注入中断/异常事件,因为可以让那些事件直接触发VM Exit)。
    VM-exitinformation fields,即记录下发生VM Exit发生的原因及一些必要的信息,方便于VMM对VM Exit事件进行处理,如vCPU访问了特权资源造成VM Exit,则在该区域中,会记录下这个特权资源的类型,如I/O地址,内存地址或者是MSR寄存器,并且也会记录下该特权资源的地址,好让VMM对该特权资源进行模拟。
    VMCS有点类似于Linux进程中的上下问,只是它保存的是vCPU/CPU的状态和控制信息,而不是进程/线程的状态和控制信息。

    每个vCPU都有自己的一份VMCS数据结构,并且在同一个VM中的vCPU可能会通过VMCS中的指针共享一些数据,如I/O bitmap,MSR bitmap和EPT表等。VMCS的大小需要查看VMX capability MSR IA32_VMX_BASIC寄存器来决定,不同的处理器可能要求的不一样,最大不超过4KB。
    每个VMCS都会有一个8字节的头部,前面4字节主要用来表示VMCS的版本号并且表示VMCS是否为shadow VMCS。然后从偏移量8开始,就是VMCS的数据区,不同版本号的VMCS数据区分布会不一样。



    为了为不同版本的VMCS(数据分布有区别)提供统一的、安全的访问接口,VMX功能提供了VMREAD和VMWRITE两个指令,用于对VMCS数据结构进行访问,该指令的第一个操作数就是VMCS数据结构中各个数据域的索引(注意:并不是偏移量),在Intel SDM(Software Developer Manuel)手册上,会定义各个数据区域的索引值。





    当VMCS数据结构被load到逻辑CPU上(即执行VMPTRLD指令)后,处理器并没法通过普通的内存访问指令去访问VMCS,如果那样做的话,会引起处理器报错,唯一可用的方法就是通过VMREAD和VMWRITE指令去访问。

    VMCS数据结构也会记录下当前vCPU的状态,基本上VMCS的状态由三个域来表示:

    Active或Inactive,表示该VMCS是否处于激活状态,即对于逻辑CPU来说,该VMCS是否是直接可用的,有点类似于Linux进程的可运行状态(虽然可运行,但并不意味着现在就必须运行)
    Current或NonCurrent,该状态表示当前逻辑CPU执行VMX指令(如VMCLEAR,VMLAUNCH,VMRESUME等)时的目标VMCS(或者可以理解为目标vCPU),这些VMX指令的目标都是状态为Current的VMCS所代表的vCPU。
    Clear或者Launched,用于表示VMCS所对应的vCPU是否有在逻辑CPU上执行过,如果没有,则为Clear状态,如果有,则为Launched状态,Launched状态的VMCS也可以通过VMCLEAR指令恢复到Clear状态。当一个vCPU从一个逻辑CPU迁移到另外一个逻辑CPU的时候,需要先将相应VMCS的状态恢复到Clear,撇清该VMCS根原本逻辑CPU的关系,然后在另外一个逻辑CPU上执行VMPTRLD,将该VMCS和新的逻辑CPU建立联系。


    对于VMM而言,配置并运行一个VM的最基本步骤如下所示:

    根据IA32_VMX_BASIC MSR寄存器提供的VMCS region大小信息,在内存中创建一个4KB对齐的VMCS region(为了防止被直接内存访问到,可以将该page frame从页表中删除);
    根据IA32_VMX_BASIC MSR寄存器提供的VMCS版本信息,初始化新创建的VMCS region中的版本信息,并且将VMCS前4字节的bit31设置为0(表示该VMCS不是shadow VMCS);
    针对新创建的VMCS region执行VMCLEAR指令,这会对新创建的VMCS region进行初始化,并且将VMCS的状态设置为Clear;
    针对新创建的VMCS region执行VMPTRLD指令,该指令会将新创建的VMCS region设置为逻辑CPU的当前VMCS,即新创建的VMCS从原来的Not Current状态变为Current状态,后面所有的VMX相关指令操作的对象都将是新创建的VMCS;
    通过一系列的VMWRITE指令对VMCS中的Host-state area进行初始化,该区域将记录VM返回到VMM时,或者是VM Entry不成功时,逻辑CPU的状态及执行位置等信息;
    通过VMWRITE指令对VMCS中VM-Exit control field,VM-Entry controlfield和VM execution control field等区域进行设置;
    通过WMWRITE指令对VMCS中guest state area区域进行设置,该区域表示了VM中vCPU的执行时vCPU的状态,起始执行位置等信息;
    执行VMLAUNCH指令,开始vCPU的执行。通过VMM需要判断VMLAUNCH的返回结果,确定vCPU是否真正被执行,还是因为某些逻辑冲突导致vCPU没有被执行就返回。
    注意:在设置VMCS的各个数据区域的时候,是有顺序讲究的,需要先设置Host-state area,这样可以防止后边步骤出现问题,CPU无法返回到正确的状态和位置,另外页可以减少后面设置对Host CPU状态的干扰。

    vCPU的直奔执行逻辑如下图所示:
    VMX详解(网转我也没看懂) - 图2



    不同的VMCS的切换就像Linux中不同的进程在逻辑CPU上切换,通过时分复用的方式共享着物理CPU的资源。

    对于Intel x86处理器,在打开VMX(Virtual Machine Extension),即执行VMXON指令的时候需要提供一个4KB对齐的内存区间,称作VMXON region,该区域的物理地址作为VMXON指令的操作数。该内存区间用于支持逻辑CPU的VMX功能,该区域在VMXON和VMXOFF之间一直都会被VMX硬件所使用。

    VMX详解(网转我也没看懂) - 图3
    VMX详解(网转我也没看懂) - 图4



    VMXONRegion内存区域的要求跟VMCS类似,也是需要4KB对齐,并且在使用之前,需要先初始化一下VMCS revision,即从IA32_VMX_BASIC MSR寄存器中获取到的31bit的VMCSrevision identifier。对于每个支持VMX功能的逻辑CPU而言,都需要一个相应的VMXON Region。
    对于软件而言,除了在执行VMXON之前初始化一下VMCS的版本号之外,不需要再做其他事情,特别是在VMXON和VMXOFF之间,软件不应该访问,甚至是更改VMXON Region这段内存,否则可能会造成不可预知的后果。


    在KVM模块中,KVM的模块加载过程vmx_init()中,会调用到alloc_kvm_area()函数,为每个逻辑CPU分配一个VMXONRegion。

    VMX详解(网转我也没看懂) - 图5
    VMX详解(网转我也没看懂) - 图6

    源码中,分配VMXON Region的时候完全是以分配VMCS的方式进行分配的,这个可能容易让人将VMXON Region和VMCS Region混起来。
    注意:VMXON Region和VMCS Region是不一样的两个内存区域,VMXON是针对逻辑CPU的,每个逻辑CPU都会有一份,并且在整个VMX功能使用期间硬件都会使用;而VMCS Region则是针对vCPU的,每个vCPU都会有一份VMCS Region,用于辅助硬件对vCPU的模拟。

    VMXONRegion看样子完全是为了满足VMX功能在硬件上的需求,里面具体有什么数据,Intel并没有公开,软件要做的就是分配一个这样的数据区,标明VMCS revision,让后扔该硬件自个儿玩去,VMM没有必要也没有资格管
    ——————————-
    作者:河马虚拟化
    来源:CSDN
    原文:https://blog.csdn.net/lindahui2008/article/details/81973853
    版权声明:本文为博主原创文章,转载请附上博文链接!