Intel VT学习笔记(二)—— VMXE&VMXON

Intel VT学习笔记(二)—— VMXE&VMXON

VT生命周期

描述:VT生命周期可以参考Intel开发手册第3卷图23-1。
Intel VT学习笔记(二)—— VMXE&VMXON
周壑大大将VT生命周期总结为以下示意图:
Intel VT学习笔记(二)—— VMXE&VMXON
并将这个流程比喻为:

  1. 开锁(将Cr4的VMXE位置1)
  2. 开柜门(VMXON
  3. 拔电源(VMCLEAR,相当于初始化)
  4. 选中机器(VMPTRLOAD,选择需要处理的guest机)
  5. 装机(设置VMCS,通过VMWRITE
  6. 开机(VMLAUNCH
  7. 拔电源(依然是VMCLEAR
  8. 关柜门(VMXOFF
  9. 关锁(将Cr4的VMXE位置0)

VMXE

描述:在进入VMX模式前,需要将Cr4的VMXE位置1,告知他人系统已进入VMX模式,否则会失败。

typedef union
{
	struct{
		unsigned VME:1;
		unsigned PVI:1;
		unsigned TSD:1;
		unsigned DE:1;
		unsigned PSE:1;
		unsigned PAE:1;
		unsigned MCE:1;
		unsigned PGE:1;
		unsigned PCE:1;
		unsigned OSFXSR:1;
		unsigned PSXMMEXCPT:1;
		unsigned UNKONOWN_1:1;		//These are zero
		unsigned UNKONOWN_2:1;		//These are zero
		unsigned VMXE:1;			//It's zero in normal
		unsigned Reserved:18;		//These are zero
		//unsigned Reserved_64:32;
	};
}_CR4;

VMXON

描述:汇编指令,用于进入VMX操作模式。

准备工作

进入VMX模式前的准备工作可参考可参考Intel开发手册第3章31.5小节。
Intel VT学习笔记(二)—— VMXE&VMXON
简单来说,进入VMX模式前需要做的事情有:

  1. 使用CPUID指令查看系统是否支持
  2. 查看相关的MSR寄存器确定VMX支持能力
  3. 创建一个4KB对齐的内存,大小由IA32_VMX_BASIC MSR指定(称作VMXON region)。
  4. 清除申请内存的前四个字节,并设置版本标识符(IA32_VMX_BASIC MSR低4字节)
  5. 确保Cr0PE位、PG位为1
  6. 确保Cr4VMXE位为1
  7. 确保IA32_FEATURE_CONTROL MSR的Lock位为1(BIOS设置)
  8. 以申请内存的物理地址作为操作数,并在执行后检查CF位是否为0

其中,第1、5、6、7点已经学习过,第2点目前来说不重要,先重点关注3、4、8这三点。

VMXON region

描述:在执行VMXON前,需要申请一块内存,用于实现VMX相关功能,例如存储VMM相关信息,由CPU负责维护,这块内存被称为VMXON region。

具体可参考Intel开发手册第3章附录A.1
Intel VT学习笔记(二)—— VMXE&VMXON
这里是说,VMXON region具体大小可从IA32_VMX_BASIC MSR中读取(偏移为480H),第44:32位形成的整数即所需大小。
Intel VT学习笔记(二)—— VMXE&VMXON

申请完内存后,需要将前四个字节设置为版本标识符(第30:0位)
Intel VT学习笔记(二)—— VMXE&VMXON

在进入VMX模式时,需要将这块内存的物理地址作为VMXON的参数,并在执行后检查CF位是否为0。

代码实现

//vtasm.h
#ifndef VTASM_H
#define VTASM_H

typedef union
{
	struct
	{
		unsigned SSE3 : 1;
		unsigned PCLMULQDQ : 1;
		unsigned DTES64 : 1;
		unsigned MONITOR : 1;
		unsigned DS_CPL : 1;
		unsigned VMX : 1;
		unsigned SMX : 1;
		unsigned EIST : 1;
		unsigned TM2 : 1;
		unsigned SSSE3 : 1;
		unsigned Reserved : 22;
	};

}_CPUID_ECX;

typedef struct _IA32_FEATURE_CONTROL_MSR
{
	unsigned Lock : 1;		// Bit 0 is the lock bit - cannot be modified once lock is set
	unsigned Reserved1 : 1;		// Undefined
	unsigned EnableVmxon : 1;		// Bit 2. If this bit is clear, VMXON causes a general protection exception
	unsigned Reserved2 : 29;	// Undefined
	unsigned Reserved3 : 32;	// Undefined

} IA32_FEATURE_CONTROL_MSR;

typedef struct _VMX_BASIC_MSR
{
	unsigned RevId : 32;
	unsigned szVmxOnRegion : 12;
	unsigned ClearBit : 1;
	unsigned Reserved : 3;
	unsigned PhysicalWidth : 1;
	unsigned DualMonitor : 1;
	unsigned MemoryType : 4;
	unsigned VmExitInformation : 1;
	unsigned Reserved2 : 9;
} VMX_BASIC_MSR, * PVMX_BASIC_MSR;

typedef union
{
	struct
	{
		unsigned PE : 1;
		unsigned MP : 1;
		unsigned EM : 1;
		unsigned TS : 1;
		unsigned ET : 1;
		unsigned NE : 1;
		unsigned Reserved_1 : 10;
		unsigned WP : 1;
		unsigned Reserved_2 : 1;
		unsigned AM : 1;
		unsigned Reserved_3 : 10;
		unsigned NW : 1;
		unsigned CD : 1;
		unsigned PG : 1;
		//unsigned Reserved_64:32;
	};

}_CR0;

typedef union
{
	struct {
		unsigned VME : 1;
		unsigned PVI : 1;
		unsigned TSD : 1;
		unsigned DE : 1;
		unsigned PSE : 1;
		unsigned PAE : 1;
		unsigned MCE : 1;
		unsigned PGE : 1;
		unsigned PCE : 1;
		unsigned OSFXSR : 1;
		unsigned PSXMMEXCPT : 1;
		unsigned UNKONOWN_1 : 1;		//These are zero
		unsigned UNKONOWN_2 : 1;		//These are zero
		unsigned VMXE : 1;			//It's zero in normal
		unsigned Reserved : 18;		//These are zero
		//unsigned Reserved_64:32;
	};
}_CR4;

typedef union
{
	struct
	{
		unsigned CF : 1;
		unsigned Unknown_1 : 1;	//Always 1
		unsigned PF : 1;
		unsigned Unknown_2 : 1;	//Always 0
		unsigned AF : 1;
		unsigned Unknown_3 : 1;	//Always 0
		unsigned ZF : 1;
		unsigned SF : 1;
		unsigned TF : 1;
		unsigned IF : 1;
		unsigned DF : 1;
		unsigned OF : 1;
		unsigned TOPL : 2;
		unsigned NT : 1;
		unsigned Unknown_4 : 1;
		unsigned RF : 1;
		unsigned VM : 1;
		unsigned AC : 1;
		unsigned VIF : 1;
		unsigned VIP : 1;
		unsigned ID : 1;
		unsigned Reserved : 10;	//Always 0
		//unsigned Reserved_64:32;	//Always 0
	};
}_EFLAGS;

void Asm_CPUID(ULONG uFn, PULONG uRet_EAX, PULONG uRet_EBX, PULONG uRet_ECX, PULONG uRet_EDX);

ULONG64 Asm_ReadMsr(ULONG uIndex);

ULONG Asm_GetEflags();

ULONG Asm_GetCr0();
ULONG Asm_GetCr4();

void Asm_SetCr4(ULONG uNewCr4);

void Vmx_VmxOn(ULONG LowPart, ULONG HighPart);
void Vmx_VmxOff();

#endif

//vtasm.asm
.686p
.model flat, stdcall
option casemap:none

.data

.code

Asm_CPUID	Proc	uses ebx esi edi fn:dword, ret_eax:dword, ret_ebx:dword, ret_ecx:dword, ret_edx:dword
        mov	eax, fn
        cpuid
        mov	esi, ret_eax
        mov	dword ptr [esi], eax
        mov	esi, ret_ebx
        mov	dword ptr [esi], ebx
        mov	esi, ret_ecx
        mov	dword ptr [esi], ecx
        mov	esi, ret_edx
        mov	dword ptr [esi], edx
        ret
Asm_CPUID 	Endp

Asm_ReadMsr		Proc	Index:dword
        mov	ecx,Index
        rdmsr
        ret
Asm_ReadMsr		Endp

Asm_GetCr0		Proc
        mov 	eax, cr0
        ret
Asm_GetCr0 		Endp

Asm_GetCr4		Proc
        mov 	eax, cr4
        ret
Asm_GetCr4 		Endp

Asm_SetCr4		Proc	NewCr4:dword
        mov 	eax,NewCr4
        mov 	cr4, eax
        ret
Asm_SetCr4 		Endp

Vmx_VmxOn Proc LowPart:dword,HighPart:dword
        push HighPart
        push LowPart
        Vmxon qword ptr [esp]
        add esp,8
        ret
Vmx_VmxOn Endp

Vmx_VmxOff Proc
        Vmxoff
        ret
Vmx_VmxOff Endp

Asm_GetEflags PROC
        pushfd
        pop		eax
        ret
Asm_GetEflags ENDP

END
//vtsystem.h
#ifndef VTSYSTEM_H
#define VTSYSTEM_H
#include <ntddk.h>

/*MSR definition*/
#define MSR_IA32_FEATURE_CONTROL 		0x03a
#define MSR_IA32_VMX_BASIC              0x480

typedef struct _VMX_CPU
{
    PVOID               pVMXONRegion;
    PHYSICAL_ADDRESS    pVMXONRegion_PA;
    PVOID               pVMCSRegion;
    PHYSICAL_ADDRESS    pVMCSRegion_PA;
    PVOID               pStack;

    BOOLEAN             bVTStartSuccess;
}VMX_CPU, * PVMX_CPU;

//检查当前处理器是否支持VT
BOOLEAN IsVTEnabled();
//开启VT
NTSTATUS StartVirtualTechnology();
//关闭VT
NTSTATUS StopVirtualTechnology();

#define Log(message,value) {{KdPrint(("[MinVT] %-40s [%p]\n",message,value));}}

#endif
//vtsystem.c
#include "vtsystem.h"
#include "vtasm.h"

VMX_CPU g_VMXCPU;

BOOLEAN IsVTEnabled()
{
    ULONG       uRet_EAX, uRet_ECX, uRet_EDX, uRet_EBX;
    _CPUID_ECX  uCPUID;
    _CR0        uCr0;
    _CR4    uCr4;
    IA32_FEATURE_CONTROL_MSR msr;
    //1. CPUID
    Asm_CPUID(1, &uRet_EAX, &uRet_EBX, &uRet_ECX, &uRet_EDX);
    *((PULONG)&uCPUID) = uRet_ECX;

    if (uCPUID.VMX != 1)
    {
        Log("ERROR: 这个CPU不支持VT!", 0);
        return FALSE;
    }

    // 2. MSR
    *((PULONG)&msr) = (ULONG)Asm_ReadMsr(MSR_IA32_FEATURE_CONTROL);
    if (msr.Lock != 1)
    {
        Log("ERROR:VT指令未被锁定!", 0);
        return FALSE;
    }

    // 3. CR0 CR4
    *((PULONG)&uCr0) = Asm_GetCr0();
    *((PULONG)&uCr4) = Asm_GetCr4();

    if (uCr0.PE != 1 || uCr0.PG != 1 || uCr0.NE != 1)
    {
        Log("ERROR:这个CPU没有开启VT!", 0);
        return FALSE;
    }

    if (uCr4.VMXE == 1)
    {
        Log("ERROR:这个CPU已经开启了VT!", 0);
        Log("可能是别的驱动已经占用了VT,你必须关闭它后才能开启。", 0);
        return FALSE;
    }

    Log("SUCCESS:这个CPU支持VT!", 0);
    return TRUE;
}

NTSTATUS StartVirtualTechnology()
{
    PVOID pVMXONRegion;
    VMX_BASIC_MSR Msr;
    ULONG uRevId;
    _CR4 uCr4;
    _EFLAGS uEflags;

    if (!IsVTEnabled())
        return STATUS_NOT_SUPPORTED;

    *((PULONG)&uCr4) = Asm_GetCr4();		//设置Cr4的VMXE位
    uCr4.VMXE = 1;
    Asm_SetCr4(*((PULONG)&uCr4));

    pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool, 0x1000, 'vmon'); //申请一块4KB内存,给VMM用
    if (!pVMXONRegion)
    {
        Log("ERROR:申请VMXON内存区域失败!", 0);
        return STATUS_MEMORY_NOT_ALLOCATED;
    }
    RtlZeroMemory(pVMXONRegion, 0x1000);

    g_VMXCPU.pVMXONRegion = pVMXONRegion;
    g_VMXCPU.pVMXONRegion_PA = MmGetPhysicalAddress(pVMXONRegion);	//获得这块内存的物理地址

    *((PULONG)&Msr) = (ULONG)Asm_ReadMsr(MSR_IA32_VMX_BASIC);
    uRevId = Msr.RevId;

    Log("TIP:VMX版本号信息", uRevId);

    *((PULONG)g_VMXCPU.pVMXONRegion) = uRevId;		//设置版本标识符

	//将64位物理地址作为VMXON参数,执行
    Vmx_VmxOn(g_VMXCPU.pVMXONRegion_PA.LowPart, g_VMXCPU.pVMXONRegion_PA.HighPart);	

    *((PULONG)&uEflags) = Asm_GetEflags();
    if (uEflags.CF != 0)		//通过CF位判断是否调用成功
    {
        Log("ERROR:VMXON指令调用失败!", 0);
        return STATUS_UNSUCCESSFUL;
    }

    Log("SUCCESS:VMXON指令调用成功!", 0);

    return STATUS_SUCCESS;
}

NTSTATUS StopVirtualTechnology()
{
    _CR4 uCr4;
    
    Vmx_VmxOff();	//退出VMX模式
    
    *((PULONG)&uCr4) = Asm_GetCr4();		//设置VMXE为0
    uCr4.VMXE = 0;
    Asm_SetCr4(*((PULONG)&uCr4));

    ExFreePool(g_VMXCPU.pVMXONRegion);		//释放内存

    Log("SUCCESS:VMXOFF指令调用成功!", 0);

    return STATUS_SUCCESS;
}
//driver.c
#include <ntddk.h>
#include "vtasm.h"
#include "vtsystem.h"

VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
	StopVirtualTechnology();
	DbgPrint("Driver unload. \r\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
	DbgPrint("Driver load. \r\n");

	StartVirtualTechnology();

	pDriver->DriverUnload = DriverUnload;

	return STATUS_SUCCESS;
}

执行结果:
Intel VT学习笔记(二)—— VMXE&VMXON

参考资料

上一篇:VMware的三种连接方式


下一篇:使用VMware Workstation操作Linux系统提示Intel VT-x处于禁用状态