VMX,SVM编程指北——检查系统是否支持 VMX

Posted FE&FR&SH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VMX,SVM编程指北——检查系统是否支持 VMX相关的知识,希望对你有一定的参考价值。

VMX 这技术已经出现好多年了,市面上的CPU大多都支持了,那还有必要检查系统是否支持VMX吗?考虑一下还是要检查的,就算市面上的CPU都支持了,但各家的Bios/UEFI有别,VMX可能会被禁用,所以你的软件应该进行检查,如果发现VMX被禁用,应该恰当的提示用户。

现在让我们来过一遍流程:
1,确认当前的CPU是不是intel的,毕竟这里讨论的都是VMX; 使用指令CPUID.0,返回值按照ebx,edx,ecx排列,就是GenuineIntel,知道就好,写代码的时候没必要抠这种细节,去排这个序。示例代码如下:(注意:代码默认使用WDK风格,如int 默认长度为 32个bit,此类问题以后不再赘述)

点击查看代码
#define BASIC_INTEL_STR "GenuntelineI"

int cpuInfo[4] = {0};//eax, ebx, ecx, edx

__cpuid(cpuInfo, 0);
if (3*sizeof(int) != RtlCompareMemory(BASIC_INTEL_STR, cpuInfo+1, 3*sizeof(int))) {
//not match
}

2,检查CPU是否支持VMX,使用CPUID.1:ECX.VMX[bit 5]来判断,示例代码如下:

点击查看代码
#define CUPID_ECX_VMX (1 << 5)

int cpuInfo[4] = {0};//eax, ebx, ecx, edx

__cpuid(cpuInfo, 1);
if(cpuInfo[2] & CUPID_ECX_VMX)
{
//OK,cpu support vmx
}

3,检查BIOS/UEFI是否禁用了VMX,前面两个步骤中使用的 CPUID 可以在user mode中使用,但从这里开始 你将不得不写一些驱动代码。(若有人需要《驱动编程指北》,我可以另开一个章节) 示例代码如下:

点击查看代码
#define IA32_FEATURE_CONTROL_MSR 0x3a
#define IA32_FEATURE_CONTROL_LOCKED (1 << 0)
#define IA32_FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1 << 1)
#define IA32_FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1 << 2)

__int64 featureInfo = 0;
	
featureInfo = __rdmsr( IA32_FEATURE_CONTROL_MSR);
if(!(featureInfo & IA32_FEATURE_CONTROL_LOCKED))
{
//your BIOS/UEFI code maybe not strong. It\'s time to buy a new one.
//if the bit is clear, VMXON will causes a exception.
}

if(featureInfo & IA32_FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX)
{
//
}

if(featureInfo & IA32_FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX)
{
//
}

SMX 指 Safer Mode Extension, 所以IA32_FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX 和 IA32_FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX,就是判断 VMXON 能否在SMX内和外 执行。
IA32_FEATURE_CONTROL_LOCKED 一个powercycle只能写一次,通常由BIOS/UEFI填写,当然也有可能遇到没有填的,也就是说这个bit处于clear状态,那么我们也可以自己填写这部分,使用指令__wrmsr()按需设置IA32_FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX,IA32_FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX和IA32_FEATURE_CONTROL_LOCKED。

VMX,SVM编程指北——梦开始的地方 VMX ON

先上一张图,简单认识一下涉及到的几种状态。

控制端称为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();
if (!(cr0 & CR0_PE))
{
	//...
}
if (!(cr0 & CR0_PG))
{
	//...
}

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
if(NULL == vmcs)
{
	//...
}
	
RtlZeroMemory(vmcs, vmcs_size);
vmcs->u32.revision_identifier = revisionIdentifier;

4,执行VMXON。注意需要使用vmcs对应的物理地址(Physical Address)。如果一切正确 这个CPU将进入 VMX root operation,此后可以使用其它的vmx_xx指令。

点击查看代码
vmcsPhysical = GetPhysicalAddress(vmcs);

ret = __vmx_on(vmcsPhysical);
if(0 != ret)
{
	//vmxon failed.
}

5,关于编程方面的提示:
A,你应该注意到了,这些寄存器和指令都是对应 the logical processor的,即以上的所有代码 包括上一篇介绍的,都应该在同一个logical processor上运行。而当前的CPU都是多核,Windows和Linux都是多任务的,所以在写驱动代码时,需注意格外注意。
- 在processor 1上执行 __vm_on 然后在 processor 2上执行 __vmx_off,显然是错的
- 以上强调的内容与一个VMCS和一个logical processor是否绑定终身,是两回事。
B,对于多颗CPU的场景,注意NUMA影响内存访问快慢的问题。

以上是关于VMX,SVM编程指北——检查系统是否支持 VMX的主要内容,如果未能解决你的问题,请参考以下文章

centos7.4应用之KVM

KVM主机迁移

如何查看当前linux服务器是否支持虚拟化

CentOS7.4安装部署KVM虚拟机

kvm安装和配置

centos 7.2 kvm虚拟化配置以及常用操作