KVM循序渐进耳之原理篇
一半人生 发表于 浙江 二进制安全 1320浏览 · 2024-01-31 09:45

前言:

  基础篇主要是搭建应用层调试环境和了解虚拟化和KVM代码,本篇承上启下INIT、VMXON和VMCS等分享。

QEMU:

环境:

Centos6 2.6.32-754.35.1.el6.x86_64

QEMU PC emulator version 0.12.1 (qemu-kvm-0.12.1.2-2.506.el6_10.8)

QUEM-KVM Pre Environment:

./configure如果遇到未安装libpic,centos安装如下:

yum install pciutils-devel
# apt-get
apt-get install libpic libpic-devel

编译后有几个可执行文件比较重要:

  • qemu-kvm-0.12.1.2/x86_64-softmmu/qemu-system-x86_64

  • qemu-kvm-0.12.1.2/qemu-io

  • qemu-kvm-0.12.1.2/qemu-nbd

  • qemu-kvm-0.12.1.2/qemu-img

可以拷贝上述文件到/usr/bin下,或者建立软连接或者使用make install(建议)。

  如果使用的本文的环境,make几乎不会出问题。如果版本不匹配可能会遇到编译错误,需要加gcc选项或源码修改。遇到复杂依赖环境,可以先yum安装qemu-kvm lib等,然后卸载rmmod kvm/kvm-intel,编译自己qemu-kvm源码,可能会解决部分奇怪的依赖。

QUEM-KVM Init VM Analyze:

  qemu-system-x86_64拉起可用的Centos5-iso.x64-VM节点,这里最小的centos6-x86_64(vnc和网卡配置不描述):

  • 创建Qemu镜像,指令如下:
qemu-img create -f qcow2 centos6_vm1.img 10G  -- qcow2类型 实际大小没有10G。
  • 网卡配置和VNC这里不描述,创建虚拟机指令:
qemu-system-x86_64 -m 512 -smp 1 --enable-kvm  -boot d -hda /kvm_vm/centos6_vm1.img -cdrom /root/CentOS-6.10-x86_64-minimal.iso
  • 注意使用make install不会出现这个问题,qemu: could not load pc bios 'bios.bin'(这些都可以再Makefile去搞)。
find / -name bios.bin
查找结果:/xxx/qemu-kvm-0.12.1.2/pc-bios/bios.bin
执行:cp /root/qemu-kvm-0.12.1.2/pc-bios/bios.bin /usr/share/seabios/

  拷贝到/usr/share/seabios/和/usr/bin或者其目录仍然会报错,看看源码qemu_find_file负责查找文件,拼接绝对路径:

vl.c文件qemu_find_file函数中添加fprintf:
4703行:snprintf(buf, len, "%s/%s%s", data_dir, subdir, name);
添加:fprintf(stderr, "[+]bios path: %s\n", buf);

  上传修改后vl.c,重新打包编译拷贝到/usr/bin/,执行输出如下:

make clean
make
cp  /.../.../qemu-system-x86_64 /usr/bin/
qemu-system-x86_64 -m 512 -smp 1 --enable-kvm  -boot d -hda /kvm_vm/centos6_vm1.img -cdrom /root/CentOS-6.10-x86_64-minimal.iso

  输出路径'/usr/local/share/qemu/bios.bin',指令启动成功,但是qemu界面黑屏。启动输出Log信息,vapic.bin没有找到,解决方式和bios.bin:

cp /root/qemu-kvm-0.12.1.2/pc-bios/bios.bin /usr/local/share/qemu/(没有目录就创建)
cp /root/qemu-kvm-0.12.1.2/pc-bios/vapic.bin /usr/local/share/qemu/
还有依赖否则会黑屏,建议使用make & make install。

  上述手动比较麻烦,可以直接使用make install。重新编译先make uninstall卸载,这里为了方便手动将依赖的变量进行拷贝和使用(比较麻烦),qemu-kvm可用环境已经完成,下面分析qemu-system-x86_64如何创建/执行虚拟VM节点:

  vl.c-main函数,从for(;;)开始解析参数,这里不具体介绍,从kvm判断开始,如下:

if (kvm_enabled()) {
        int ret;

        ret = kvm_init(smp_cpus);
        if (ret < 0) {
#if defined(KVM_UPSTREAM) || defined(CONFIG_NO_CPU_EMULATION)
            fprintf(stderr, "failed to initialize KVM\n");
            exit(1);
#endif
            fprintf(stderr, "Could not initialize KVM, will disable KVM support\n");
            kvm_allowed = 0;
        }
    }

  kvm_init源码什么都没做,直接return 0

static inline int kvm_init(int smp_cpus)
{
    return 0;
}

  通过配置方案模块初始化来调用kvm_init(),还有初始化事件/socket/内存等代码,Qemu-kvm管理。Qemu-kvm并非kvm内核代码,使用--enable-kvm开启kvm支持,kvm模块也需要修改才可以,下载对应的Linux源码,只需要编译kvm模块即可,不然每次修改kvm都需要编译内核直接累死。

KVM-12 Issue:

KVM-12版本编译问题这里也总结,12版本初始化接口改变如下:

module_init(kvm_init) kvm_init_debug --> kvm_init_msr_list --> page_to_pfn
module_exit(kvm_exit)
// VT检测和初始化
EXPORT_SYMBOL_GPL(kvm_init_arch);  
EXPORT_SYMBOL_GPL(kvm_exit_arch);

  kvm-12编译完成后,insmod kvm.ko安装,/dev/下找不到但是lsmod | grep kvm模块已被加载,添加调试信息发现kvm_init以后没有进入kvm_init_arch,初始化再vmx_init中,所以还需要执行insmod kvm-intel.ko:

insmod kvm.ko
insmod kvm-intel.ko or insmod kvm-amd.ko

  kernel 2.6.32编译kvm-24 ./configure会遇到错误:cannot locate gcc 3.x. please install it or specify with --qemu-cc

yum -y install compat-gcc-*
yum -y install SDL-devel

  Centos5 kernel v2.6.18环境下Libkvm通过ioctl创建虚拟机VM Entry Error: 0x80000022,VM entry: failure due to MSR loading,VMentry Msr加载失败。分析VMCS-VM Entry_Setup入手,读取了IA32_VMX_ENTRY_CTLS(0x484)寄存器的数据,运算后写入VM_ENTRY_CONTROLS(0x4012)。

// VM-Entry Controls for MSRs
vmcs_write32_fixedbits(MSR_IA32_VMX_ENTRY_CTLS_MSR,VM_ENTRY_CONTROLS, 0);
// VM-Entry Controls for Event Injection
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);  /* 22.2.1 */ 这里是0,未开启

  通过打印VM_ENTRY_CONTROLS写入的值是0x11ff,0~8bit和12bit必须为1,二进制数据如下,没有发现错误,如下:

0x11ff = 0001000111111111

  因为是x64,更改为0x13ff,将9bit置为1,环境是64bit=1,如果x86第9 bit必须为0,如下:

0x13ff = 0001001111111111
unsigned int vm_entryctl_requested = 0;
vm_entryctl_requested |= 516;
vmcs_write32_fixedbits(MSR_IA32_VMX_ENTRY_CTLS_MSR,VM_ENTRY_CONTROLS,vm_entryctl_requested);

  VM-entry failure due to Msr loading,1 or 2其他导致,如果是PAE cr0.pg=1 cr4.pae=1 ia32efer=0。

A VM entry to a guest that does not use PAE paging does not check the validity of any PDPTEs.

  如果Guest x32 非PAE则不做检查,x64 PAE:

flags = vmcs_readl(GUEST_RFLAGS);vcpu->rmode.save_iopl = (flags & IOPL_MASK) >> IOPL_SHIFT;// X86_EFLAGS_VM = 00010111// IOPL_MASK = 0011000000000000flags |= IOPL_MASK | X86_EFLAGS_VM;vmcs_writel(GUEST_RFLAGS, flags);vmcs_writel(GUEST_CR4, vmcs_readl(GUEST_CR4) | CR4_VME_MASK);

  arch/x86/include/asm/pgtable.h:605: 错误:隐式声明函数‘IS_ENABLED’,按照谷歌说法:IS_ENABLED 宏Linux 3.1 之后才引入,kconfig.h头文件中定义,3.1内核之前引用都会报错。
  rflag是v86模式,kvm低版本vmcs只填充了16/32.....,vm虚拟节点也是对v86最初支持,并未对64bit做处理,拉起Guest-x64会有问题。文章还是将排错过程梳理下来,遇到问题从手册排查代码简单思路。

KVM_INIT

检测

  • 使用cpuid(1):ecx.vmx[bit 5]标志位检测是否支持VMX,如下所示:
static __init int cpu_has_kvm_support(void)
{
  unsigned long ecx = cpuid_ecx(1);
  return test_bit(5, &ecx); /* CPUID.1:ECX.VMX[bit 5] -> VT */
}

See: DISCOVERING SUPPORT FOR VMX

  • CR4.VMXE =1 , 如果再BIOS上开启VMX还需要设置lock和1/2bit(如果是0会导致一般保护异常),代码如下:
static __init int vmx_disabled_by_bios(void)
{
  u64 msr;
  rdmsrl(MSR_IA32_FEATURE_CONTROL, msr);
  return (msr & 5) == 1; /* locked but not enabled */
}

See: ENABLING AND ENTERING VMX OPERATION

VMXON

  • kvm_init执行了kvm_enable,创建vmxon区域是alloc_kvm_area(),创建过程中初始化vmxon结构(alloc_vmcs_cpu函数中赋值区域),revision_identifier字段要与msr_vmx_base一致,且有保留字段需要为0。

  • vmcs_descriptor/vmx_on全局变量,vmcs_descriptor结构被setup_vmcs_descriptor函数初始化,然后申请过程中给vmxon赋值。

static __init void setup_vmcs_descriptor(void)
{
  u32 vmx_msr_low, vmx_msr_high;
  rdmsr(MSR_IA32_VMX_BASIC_MSR, vmx_msr_low, vmx_msr_high);
  vmcs_descriptor.size = vmx_msr_high & 0x1fff;
  vmcs_descriptor.order = get_order(vmcs_descriptor.size);
  vmcs_descriptor.revision_id = vmx_msr_low;
};
  • 高版本应该可以检测'PpyH'来探测否支持hyper虚拟化, 进入VMX ROOT:
rdmsrl(MSR_IA32_FEATURE_CONTROL, old);
if ((old & 5) == 0)
  /* enable and lock */
  wrmsrl(MSR_IA32_FEATURE_CONTROL, old | 5);
//see: VMX-FIXED BITS IN CR4
write_cr4(read_cr4() | CR4_VMXE); /* FIXME: not cpu hotplug safe */
asm volatile ("vmxon %0" : : "m"(phys_addr) : "memory", "cc");

总结

  • VMX
1. 检测CPUID是否支持VMX
2. CR4.VMXE[bit 13] = 1,否则VMXON执行会出现invalid-opcode异常。
3. VMX root模式下不可以清除CR4,必须vmoff之后才可以清除cr4.VMXE[bit 13] = 0;
4. VMXon由MSR控制, IA32_FEATURE_CONTROL MSR (MSR address 3AH),逻辑处理器恢复时候会清0。
  • BIOS:
1. Msr.bit 0 lock:清除的话VMXON会导致常规保护异常,设置了WRMSR将导致通用保护异常。Bios可以设置该位禁用VMX,1或者2的时候才可以启动。
2. bit 1-smx中启用VMXON。
3. bit 2-smx外启用VMXON。

  Cpuid检测和CR4.VMXE &BIOS开启vmx支持,创建vmxon(4kbyte对齐),读取ia32vmxbase初始化vmxon结构,设置cr0/cr4,通过vmxon指令进入vmx,也就是vmx root模式。

KVM_VMCS:

概念:

  官方概念:VMX non-root operation and VMX transitions are controlled by a data structure called a virtual-machine control structure (VMCS).

  VMCS(Virtual Machine Control Structures),VMM可以支持多个节点VM使用不同的VMCS,指针必须再4KB(page)字节边界上对齐。
Guest 0表述VM节点和VMM之间关系如下:

  • VMXON进入VMX ROOT,VMOFF退出。

  • VMX root可以执行VMX指令,VMlaunch进入vmx non root(Guest0),这个过程叫做VMEntry。

  • Guest 0执行敏感指令-I/O-中断等动作,non root切换root模式这个过程叫做VMExit。

  • Guest 0虚拟节点,有用户层和内核层。guest 0用户层系统调用由guest 0内核层负责处理。guest 0内核层陷入会发生VMExit由VMM模拟处理。

  VM Exit和VM Entry切换多任务(Guest),环境上下文如何保存呢?VMCS就是为了解决该问题。既然是保存上线文,Guest-Host两点一线,vmcs应该具备必要的几个区域,分别记录Guest-host切换过程中的数据和状态,保证下次进入可以正确执行,这几个区域概述如下:

VMCS代码分析:

1) Ioctl: KVM_CREATE_VCPU,申请vmcs_page初始化(初始化版本标识/退出指示):

static struct vmcs *alloc_vmcs_cpu(int cpu)
{
  int node = cpu_to_node(cpu);
  struct page *pages;
  struct vmcs *vmcs;
  pages = alloc_pages_node(node, GFP_KERNEL, vmcs_descriptor.order);
  if (!pages)
    return 0;

  vmcs = page_address(pages);
  memset(vmcs, 0, vmcs_descriptor.size);
  vmcs->revision_id = vmcs_descriptor.revision_id; /* vmcs revision id */
  return vmcs;
}

31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE

2) VMREAD/VMWRITE/VMCLEAR三个指令用来操作VMCS,使用vmclear初始化Guest-vmcs区域,启动状态设置为clear,该指令还会检测vmcs是否有效指针。

vmcs_clear(vmcs);
asm volatile ("vmclear %1; setna %0"
        : "=m"(error) : "m"(phys_addr) : "cc", "memory" );

__vcpu_load执行vmptrld指令,使用新的guest-vmcs物理地址来初始化working-VMCS。

if (per_cpu(current_vmcs, cpu) != vcpu->vmcs) {
  u8 error;
  per_cpu(current_vmcs, cpu) = vcpu->vmcs;
  asm volatile ("vmptrld %1; setna %0"
            : "=m"(error) : "m"(phys_addr) : "cc" );
  if (error)
    printk(KERN_ERR "kvm: vmptrld %p/%llx fail\n",
            vcpu->vmcs, phys_addr);
}
Init TSS:
page = kmap_atomic(p1, KM_USER0);
memset(page, 0, PAGE_SIZE);
*(u16*)(page + 0x66) = TSS_BASE_SIZE + TSS_REDIRECTION_SIZE;
kunmap_atomic(page, KM_USER0);

page = kmap_atomic(p2, KM_USER0);
memset(page, 0, PAGE_SIZE);
kunmap_atomic(page, KM_USER0);

page = kmap_atomic(p3, KM_USER0);
memset(page, 0, PAGE_SIZE);
*(page + RMODE_TSS_SIZE - 2 * PAGE_SIZE - 1) = ~0;
kunmap_atomic(page, KM_USER0);
Guest-State Fields

Guest-state area 分为寄存器和非寄存器两种状态,寄存器设置如下:

  • 控制寄存器:cr0/cr3/cr4 cr0处理器各种模式和标志位,cr3页目录。
vmcs_writel(GUEST_CR3, 0);
  • 调试寄存器:dr7
//todo: dr0 = dr1 = dr2 = dr3 = 0; dr6 = 0xffff0ff0
vmcs_writel(GUEST_DR7, 0x400);
  • 寄存器:rsp/rip/rflags
vmcs_writel(GUEST_RFLAGS, 0x02);
vmcs_writel(GUEST_RIP, 0xfff0);
vmcs_writel(GUEST_RSP, 0);
  • 段选择子:cs/ss/ds/es/fs/gs
vmcs_write16(GUEST_CS_SELECTOR, 0xf000);
vmcs_writel(GUEST_CS_BASE, 0x000f0000);
vmcs_write32(GUEST_CS_LIMIT, 0xffff);
vmcs_write32(GUEST_CS_AR_BYTES, 0x9b);

#define SEG_SETUP(seg) do {                 \
    vmcs_write16(GUEST_##seg##_SELECTOR, 0);    \
    vmcs_writel(GUEST_##seg##_BASE, 0);     \
    vmcs_write32(GUEST_##seg##_LIMIT, 0xffff);  \
    vmcs_write32(GUEST_##seg##_AR_BYTES, 0x93);     \
  } while (0)
SEG_SETUP(DS);
SEG_SETUP(ES);
SEG_SETUP(FS);
SEG_SETUP(GS);
SEG_SETUP(SS);
  • 段描述符:ldtr/tr (32bit gdtr)
vmcs_write16(GUEST_TR_SELECTOR, 0);
vmcs_writel(GUEST_TR_BASE, 0);
vmcs_write32(GUEST_TR_LIMIT, 0xffff);
vmcs_write32(GUEST_TR_AR_BYTES, 0x008b);

vmcs_write16(GUEST_LDTR_SELECTOR, 0);
vmcs_writel(GUEST_LDTR_BASE, 0);
vmcs_write32(GUEST_LDTR_LIMIT, 0xffff);
vmcs_write32(GUEST_LDTR_AR_BYTES, 0x00082);

vmcs_writel(GUEST_GDTR_BASE, 0);
vmcs_write32(GUEST_GDTR_LIMIT, 0xffff);

vmcs_writel(GUEST_IDTR_BASE, 0);
vmcs_write32(GUEST_IDTR_LIMIT, 0xffff);
  • 非寄存器状态(活跃状态/中断能力状态/延迟调试异常状态),如下:
vmcs_write32(GUEST_ACTIVITY_STATE, 0);  
vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);  
vmcs_write32(GUEST_PENDING_DBG_EXCEPTIONS, 0);
Host-State Fields

  操作的寄存器几乎一样,当Vmentry保存,Vmexit加载状态且运行设置好的HOST_RIP,VMX root入口点kvm_vmx_return。

vmcs_writel(HOST_CR0, read_cr0());  /* 22.2.3 */
vmcs_writel(HOST_CR4, read_cr4());  /* 22.2.3, 22.2.5 */
vmcs_writel(HOST_CR3, read_cr3());  /* 22.2.3  FIXME: shadow tables */

vmcs_write16(HOST_CS_SELECTOR, __KERNEL_CS);  /* 22.2.4 */
vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
vmcs_write16(HOST_FS_SELECTOR, read_fs());    /* 22.2.4 */
vmcs_write16(HOST_GS_SELECTOR, read_gs());    /* 22.2.4 */
vmcs_write16(HOST_SS_SELECTOR, __KERNEL_DS);  /* 22.2.4 */

#ifdef __x86_64__
    rdmsrl(MSR_FS_BASE, a);
    vmcs_writel(HOST_FS_BASE, a); /* 22.2.4 */
    rdmsrl(MSR_GS_BASE, a);
    vmcs_writel(HOST_GS_BASE, a); /* 22.2.4 */
#else
    vmcs_writel(HOST_FS_BASE, 0); /* 22.2.4 */
    vmcs_writel(HOST_GS_BASE, 0); /* 22.2.4 */
#endif

vmcs_write16(HOST_TR_SELECTOR, GDT_ENTRY_TSS*8);  /* 22.2.4 */

get_idt(&dt);
vmcs_writel(HOST_IDTR_BASE, dt.base);   /* 22.2.4 */


vmcs_writel(HOST_RIP, (unsigned long)kvm_vmx_return); /* 22.2.5 */

rdmsr(MSR_IA32_SYSENTER_CS, host_sysenter_cs, junk);
vmcs_write32(HOST_IA32_SYSENTER_CS, host_sysenter_cs);
rdmsrl(MSR_IA32_SYSENTER_ESP, a);
vmcs_writel(HOST_IA32_SYSENTER_ESP, a);   /* 22.2.3 */
rdmsrl(MSR_IA32_SYSENTER_EIP, a);
vmcs_writel(HOST_IA32_SYSENTER_EIP, a);   /* 22.2.3 */
VM-execution control Fields
  • Pin-Based VM-Execution Controls基于针脚虚拟化管理异步中断事件,代码如下:
vmcs_write32_fixedbits(MSR_IA32_VMX_PINBASED_CTLS_MSR,
        PIN_BASED_VM_EXEC_CONTROL,
        PIN_BASED_EXT_INTR_MASK   /* 20.6.1 */
        | PIN_BASED_NMI_EXITING   /* 20.6.1 */
);

  代码中PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING其实二进制1001,也就开启了External-interrputexiting和NMI exiting,外部中断和不可屏蔽中断都可以导致VM exits。

  • Processor-Based VM-Execution Controls基于处理器虚拟化管理同步中断事件,代码如下:
vmcs_write32_fixedbits(MSR_IA32_VMX_PROCBASED_CTLS_MSR,
        CPU_BASED_VM_EXEC_CONTROL,
        CPU_BASED_HLT_EXITING         /* 20.6.2 */
        | CPU_BASED_CR8_LOAD_EXITING    /* 20.6.2 */
        | CPU_BASED_CR8_STORE_EXITING   /* 20.6.2 */
        | CPU_BASED_UNCOND_IO_EXITING   /* 20.6.2 */
        | CPU_BASED_INVDPG_EXITING
        | CPU_BASED_MOV_DR_EXITING
        | CPU_BASED_USE_TSC_OFFSETING   /* 21.3 */
);

  分为primary/secondary两个VM 执行控制,负责的不同。primary负责CR3/CR8/ INVLPG/I O bitmaps/HLT/MTF等产生退出时间,而secondary负责x2 APIC/EPT/RDTSCP/INVPCID等。

  • Exception Bitmap 异常位图处理。
vmcs_write32(EXCEPTION_BITMAP, 1 << PF_VECTOR);
  • I/O-Bitmap Addresses I/O位图设置,注意一点必须要在4kb页对齐位置。
vmcs_write64(IO_BITMAP_A, 0);   // 0000H through 7FFFH
vmcs_write64(IO_BITMAP_B, 0);     // 8000H through FFFFH
  • Time-Stamp Counter Offset and Multiplier 时间戳偏移值
rdtscll(tsc);
vmcs_write64(TSC_OFFSET, -tsc);
  • Guest/Host Masks and Read Shadows for CR0 and CR4虚拟机屏蔽和读cr0/cr4隐藏。
vmcs_writel(CR0_GUEST_HOST_MASK, KVM_GUEST_CR0_MASK);
vmcs_writel(CR4_GUEST_HOST_MASK, KVM_GUEST_CR4_MASK);
__set_cr0(vcpu, 0x60000010); // enter rmode
__set_cr4(vcpu, 0);
  • CR3-Target Controls
      一组4个cr3,如果当vmx non-root也就是Guest操作mov cr3,xxx,赋值不会导致vmexit操作,如果cr3目标值为0,那么mov的时候就会导致vmexit事件,KVM下面代码write写入的是0,所以再mvx non-root下对cr3赋值将会导致VMexit事件。
mcs_write32(CR3_TARGET_COUNT, 0);           /* 22.2.1 */
  • MSR-Bitmap Address Msr
      位图地址, 再Processor-Based VM-Execution Controls 且primary的第28bit=1才启用。
      启动vmx non-root执行rdmsr/wrmsr将会发生vmexits事件,而且需要对msr bitmap做处理,申请guest/host msr(4kb页对齐),设置VM进入/退出加载地址。
// 0x0000200a vm进入msr加载地址
vmcs_writel(VM_ENTRY_MSR_LOAD_ADDR,
  virt_to_phys(vcpu->guest_msrs + NR_BAD_MSRS));
vmcs_writel(VM_EXIT_MSR_STORE_ADDR,
  virt_to_phys(vcpu->guest_msrs + NR_BAD_MSRS));
vmcs_writel(VM_EXIT_MSR_LOAD_ADDR,
  virt_to_phys(vcpu->host_msrs + NR_BAD_MSRS));
  • KVM1.0没有对APIC做支持
vmcs_writel(VIRTUAL_APIC_PAGE_ADDR, 0);
vmcs_writel(TPR_THRESHOLD, 0);  // TRP需要开启21bit=1才起效
VM-Exit Control
vmcs_write32_fixedbits(MSR_IA32_VMX_EXIT_CTLS_MSR, VM_EXIT_CONTROLS,
  (HOST_IS_64 << 9));  /* 22.2,1, 20.7.1 */
vmcs_write32(VM_EXIT_MSR_STORE_COUNT, nr_good_msrs); /* 22.2.2 */
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, nr_good_msrs);  /* 22.2.2 */
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, nr_good_msrs); /* 22.2.2 */
VM-Entry Control
vmcs_write32_fixedbits(MSR_IA32_VMX_ENTRY_CTLS_MSR,VM_ENTRY_CONTROLS, 0);
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);  /* 22.2.1 */

总结

  Vmcs整个表完善填充比较复杂,VMlanch时候需要过一致性检测,否则进入vmx non-root会失败。剩下的是初始化MMU内存虚拟化(后面描述)。

  KVM调用VMLaunch使用Ioctl: KVM_RUN,执行了vmlaunch指令后,guest-vmcs从clear变为launched,设置条件都会导致guest发生vmexit,从而可以实现化一个最小化的vt,当然还要设置很多Host-VMM处理才可以完整运行。

书籍推荐:

《NewBluePill深入理解硬件虚拟机》

《QEMU/KVM源码解析应用》

0 条评论
某人
表情
可输入 255

没有评论