先上一张图,简单认识一下涉及到的几种状态。
控制端称为Virtual-Machine Monitor(VMM);被控端称为Guest;当然还有Guset中的Guest这种情况,于是有了L1 Guest,L2 Guest。
CPU根据你提供的 virtual-machine control structure (VMCS)进行相关的控制,一个VMM可以包括多个VMCS,从数据结构上看一个VMCS对应一个Virtual logical processor。
看图可知,你要先 VMXON 之后才能执行其它的各种VMX Operation,这是梦开始的地方,当然也有失眠的。
1,检查 CR0.PE 和 CR0.PG 是否等于 1 (Windows和Linux 默认就是等于1的)
点击查看代码
#define CR0_PE (1 << 0)
#define CR0_PG (1 << 31)
cr0 = __readcr0(); //(CR0.PE = 1, CR0.PG = 1)
if (!(cr0 & CR0_PE))
{
//fail
}
if (!(cr0 & CR0_PG))
{
//fail
}
2,设置 CR4.VMXE 为 1;如果该值为 0,执行VMXON是会触发异常"invaild-opcode"
点击查看代码
#define CR4_VMXE ( 1 << 13)
cr4 = __readcr4();
cr4 |= CR4_VMXE;
__writecr4(cr4);
3,根据IA32_VMX_BASIC MSR确认VMCS region的长度以及标识(identifier);readmsr返回值为64bit,0-31 bits是标识符(Identifier),32-44 bits是VMCS所需的长度(vmcs size)。
这里分配的内存要4KB对齐,且为常驻non-pageable,即不可因分页机制而被换出;
点击查看代码
#define IA32_VMX_BASIC_MSR 0x480
msrBasicInfo = __readmsr(IA32_VMX_BASIC_MSR);
low32 = msrBasicInfo & 0x00000000ffffffff;
high32 = (msrBasicInfo & 0xffffffff00000000) >> 32;
revisionIdentifier = low32;
vmcs_size = high32 & 0x1fff; //44:32
vmcs = AllocMemory( 4_Kbyte_aligned(vmcs_size));//non-pageable memory
RtlZeroMemory(vmcs, vmcs_size);
vmcs->u32.revision_identifier = revisionIdentifier;
4,执行VMXON。注意需要使用vmcs对应的物理地址(Physical Address)。如果一切正确 这个CPU将进入 VMX root operation,此后可以使用其它的vmx_xx指令。
点击查看代码
vmcsPhysical = GetPhysicalAddress(vmcs);
__vmx_on(vmcsPhysical);
5,关于编程方面的提示:
A,你应该注意到了,这些寄存器和指令都是对应 the logical processor的,即以上的所有代码 包括上一篇介绍的,都应该在同一个logical processor上运行。而当前的CPU都是多核,Windows和Linux都是多任务的,所以在写驱动代码时,需注意格外注意。
- 在processor 1上执行 __vm_on 然后在 processor 2上执行 __vmx_off,显然是错的
- 以上强调的内容与一个VMCS和一个logical processor是否绑定终身,是两回事。
B,对于多颗CPU的场景,注意NUMA影响内存访问快慢的问题。