前言:
基础篇主要是搭建应用层调试环境和了解虚拟化和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源码解析应用》
没有评论