VMM 和 Guest OS共享底层的处理器资源,因此硬件需要一个物理内存区域来自动保存或恢复彼此执行的上下文。这个区域称为虚拟机控制块(VMCS),包括客户机状态区(Guest State Area),主机状态区(Host State Area)和执行控制区. VM entry 时,硬件自动从客户机状态区加载 Guest OS 的上下文。并不需要软件来保存 VMM 的上下文.当VMM 开始运行,就不会受到Guest OS的干扰,只有 VMM 将工作彻底处理完毕才可能自行切换到 Guest OS。而 VMM 的下次运行必然是处理一个新的事件,因此每次 VM entry 时, VMM 都从一个通用事件处理函数开始执行;VM exit 时,硬件自动将 Guest OS 的上下文保存在客户机状态区,从主机状态区中加载 VMM 的通用事件处理函数的地址,VMM 开始执行。而执行控制区存放的则是可以操控 VM entry 和 exit 的标志位,例如标记哪些事件可以导致 VM exit,VM entry 时准备自动给 Guest OS 注入哪种中断等等。
2.3.1 VMCS总表
字段名采用vmcs_field 中的定义
类型 |
字段 |
含义 |
VM-Execution 控制字段
|
PIN_BASED_VM_EXEC_CONTROL |
控制pin与INTR 和 NMI是否产生VM-Exit 与使能 |
CPU_BASED_VM_EXEC_CONTROL |
处理器的指令是否引起VM-Exit, bit31控制是否启用 SECONDARY_VM_EXEC_CONTROL |
|
SECONDARY_VM_EXEC_CONTROL |
EPT, virtual APIC 等使能位; invpcid等指令是否产生vm-exit |
|
EXCEPTION_BITMAP |
每位对应一个异常,为一时发生该异常产生vm-exit |
|
IO_BITMAP_A IO_BITMAP_A_HIGH IO_BITMAP_B IO_BITMAP_B_HIGH |
每位对应一个io port addr, 为一时访问该port产生vm-exit |
|
TSC_OFFSET TSC_OFFSET_HIGH |
Use TSC offset时, non-root下执行rdtsc, rdtscp, rdmsr时反会该寄存器的值 |
|
CR0_GUEST_HOST_MASK CR0_READ_SHADOW CR4_READ_SHADOW CR4_GUEST_HOST_MASK |
Mask为为一属于host, 否则属于guest
属于Host: Guest读该位返回shadow的值,写该位时如果与shadow值不等,产生vm-exit.
属于Guest时,读写不产生vm-exit |
|
CR3_TARGET_VALUE (0-3) CR3_TARGET_COUNT |
当Guest向CR3写时,不等于0 - 3中的值产生vm-exit |
|
APIC_ACCESS_ADDR APIC_ACCESS_ADDR_HIGH |
当virtualize apic为1时, 指向4K apic-access page |
|
VIRTUAL_APIC_PAGE_ADDR VIRTUAL_APIC_PAGE_ADDR_HIGH |
Use tpr shadow为1时,指向4K virtual apic-access page |
|
TPR_THRESHOLD |
Use tpr shadow为1时 bit[3:0] > VPTR[7:4]时产生vm-exit |
|
EOI_EXIT_BITMAP (0-3) EOI_EXIT_BITMAP(0-3)_HIGH |
Virtual-interrupt delivery为1时,用于控制Eoi时 是否产生vm-exit |
|
POSTED_INTR_DESC_ADDR |
指向posted-interrupt-descriptor结构的地址 |
|
POSTED_INTR_NV |
Process posted interrupts为1时允许处理器收到一个通知性的外部中断为不产生vm-exit |
|
MSR_BITMAP |
为1时,访问产生vm-exit |
|
EPT_POINTER |
EPT list的物理地址 |
|
VIRTUAL_PROCESSOR_ID |
Enable VPID为1时,提供vpid值 |
|
PLE_GAP PLE_WINDOW |
设置pause-loop exiting的超时 |
|
PAGE_FAULT_ERROR_CODE_MASK PAGE_FAULT_ERROR_CODE_MATCH |
PAGE fault异常时 (PEFC & PFEC_MASK) == PFEC_MACH时产生vm-exit |
|
Vm-ENTRY控制字段
|
VM_ENTRY_CONTROLS |
控制VM-Entry进入的模式SMM, IA-32E,等 |
VM_ENTRY_EXCEPTION_ERROR_CODE VM_ENTRY_INSTRUCTION_LEN VM_ENTRY_INTR_INFO_FIELD |
控制中断与异常注入 |
|
VM_ENTRY_MSR_LOAD_COUNT VM_ENTRY_MSR_LOAD_ADDR |
VM-Entry时加载load_addr提供的msr值 |
|
VM-EXIT控 制字段
|
VM_EXIT_CONTROLS |
VM-Exit时寄存的保存控制 |
VM_EXIT_MSR_STORE_COUNT VM_EXIT_MSR_LOAD_COUNT |
VM-Exit时保存msr |
|
Guest State字段 |
GUEST_CR0 GUEST_CR3 GUEST_CR4 |
|
GUEST_DR7 |
|
|
GUEST_RSP GUEST_RIP GUEST_RFLAGS |
|
|
GUEST_ES_BASE GUEST_CS_BASE GUEST_SS_BASE GUEST_DS_BASE GUEST_FS_BASE GUEST_GS_BASE GUEST_LDTR_BASE GUEST_TR_BASE GUEST_GDTR_BASE GUEST_IDTR_BASE |
|
|
GUEST_ES_LIMIT GUEST_CS_LIMIT GUEST_SS_LIMIT GUEST_DS_LIMIT GUEST_FS_LIMIT GUEST_GS_LIMIT GUEST_LDTR_LIMIT GUEST_TR_LIMIT GUEST_GDTR_LIMIT GUEST_IDTR_LIMIT |
|
|
GUEST_ES_AR_BYTES GUEST_CS_AR_BYTES GUEST_SS_AR_BYTES GUEST_DS_AR_BYTES GUEST_FS_AR_BYTES GUEST_GS_AR_BYTES GUEST_LDTR_AR_BYTES GUEST_TR_AR_BYTES |
|
|
GUEST_IA32_PAT GUEST_IA32_PAT_HIGH GUEST_IA32_EFER GUEST_IA32_EFER_HIGH GUEST_IA32_PERF_GLOBAL_CTRL = GUEST_IA32_PERF_GLOBAL_CTRL_HIGH |
|
|
|
GUEST_SYSENTER_ESP GUEST_SYSENTER_EIP GUEST_SYSENTER_CS |
|
GUEST_INTERRUPTIBILITY_INFO |
记录vcpu是否可以响应中断 |
|
GUEST_PENDING_DBG_EXCEPTION |
记录与设置guest peding的 #DB异常 |
|
GUEST_PDPTR (0-3) |
EPT时页目录指针 |
|
GUEST_BNDCFGS |
|
|
GUEST_IA32_DEBUGCTL GUEST_IA32_DEBUGCTL_HIGH |
|
|
VMCS_LINK_POINTER VMCS_LINK_POINTER_HIGH |
SMM时使用 |
|
GUEST_INTR_STATUS |
Virtual-interrupt delivery时,记录local apic的状态值 |
|
VMX_PREEMPTION_TIMER_VALUE |
Preemption timer的值, 0时产生vm-exit |
|
GUEST_ACTIVITY_STATE |
指示虚拟处理器的状态 active, hlt, shutdown, wait-for-sipi |
|
Host State |
HOST_CR0 HOST_CR3 HOST_CR4 |
|
HOST_FS_BASE HOST_GS_BASE HOST_TR_BASE HOST_GDTR_BASE HOST_IDTR_BASE |
|
|
HOST_ES_SELECTOR HOST_CS_SELECTOR HOST_SS_SELECTOR HOST_DS_SELECTOR HOST_FS_SELECTOR HOST_GS_SELECTOR HOST_TR_SELECTOR |
|
|
HOST_IA32_SYSENTER_ESP HOST_IA32_SYSENTER_EIP HOST_IA32_SYSENTER_CS |
|
|
HOST_IA32_PAT = HOST_IA32_PAT_HIGH = HOST_IA32_EFER = HOST_IA32_EFER_HIGH = HOST_IA32_PERF_GLOBAL_CTRL = HOST_IA32_PERF_GLOBAL_CTRL_HIGH |
|
|
HOST_RSP HOST_RIP |
|
|
VM-EXIT 信息字段 |
VM_EXIT_REASON |
导致vm-exit的原因 |
GUEST_PHYSICAL_ADDRESS |
导致vm-exit GPA值 |
|
EXIT_QUALIFICATION |
提供进一步的vm-exit信息 |
|
GUEST_LINEAR_ADDRESS |
导致vm-exit 线性地址 |
|
VM_EXIT_INTR_INFO VM_EXIT_INTR_ERROR_CODE |
外部中断信息 |
|
IDT_VECTORING_INFO_FIELD IDT_VECTORING_ERROR_CODE |
引起vm-exit的vector信息 |
|
VM_EXIT_INSTRUCTION_LEN VMX_INSTRUCTION_INFO VM_INSTRUCTION_ERROR |
引起vm-exit的指令信息 |
底层访问代码如下:
static void vmcs_writel(unsigned long field, unsigned longvalue)
{
u8 error;
asm volatile(__ex(ASM_VMX_VMWRITE_RAX_RDX) "; setna %0"
: "=q"(error) :"a"(value), "d"(field) : "cc");
if (unlikely(error))
vmwrite_error(field,value);
}
//64位访问
static void vmcs_write64(unsigned long field, u64 value)
{
vmcs_writel(field,value);
#ifndef CONFIG_X86_64
asm volatile("");
vmcs_writel(field+1,value >> 32);
#endif
}
//32位访问
static void vmcs_write32(unsigned long field, u32 value)
{
vmcs_writel(field,value);
}
2.3.2 到2.3.4将分别讲解控制字段的设置代码,信息字段将在VM-Exit与VM-Entry中分析。
2.3.2 KVM中VM-Execution控制字段的设置
Name |
Function Flow |
Set Bits |
Description |
Note |
PIN_BASED_VM_EXEC_CONTROL |
setup_vmcs_config |
BIT0 BIT3 BIT6 BIT7 |
外部中断 VM-Exit NMI时VM-Exit 启用 VMX preemtion timer 启用post-intrs处理虚拟中断 |
这些bit与MSR_IA32_VMX_PINBASED_CTLS能力取And |
CPU_BASED_VM_EXEC_CONTROL |
setup_vmcs_config |
BIT3 BIT7 BIT9 BIT10 BIT11 BIT15 BIT16 BIT25 BIT21 BIT23 BIT27 Bit28 BIT31 |
Use TSC offset Hlt产生vm-exit inpLPG产生vm-exit mwait产生vm-exit RDPMC产生vm-exit 读写CR3时 Use Iobitmap USE TPR SHADOW 读写DR寄存器 启用mtf调试功能 启用msr bitmap 启用second process base vm-execution |
1. 64位CR8 读写产生Vm-Exit(bit[19:18]
2. 这些bit与MSR_IA32_VMX_PROCBASED_CTLS 取and |
SECONDARY_VM_EXEC_CONTROL |
setup_vmcs_config |
BIT0 BIT1 BIT3 BIT4 BIT5 BIT6 BIT7 BIT8 BIT9 BIT10 BIT12 |
Enable apic-access page Enable EPT RDTSCP产生#UD异常 Enable x2APIC MSR Enable VPID Wbinvd产生vm-exit Guest可以使用非分页模式 支持virtual-apic page虚拟寄存器 支持虚拟中断delivery Pause timeout时vm-exit Invpcid产生vm-exit |
1. 这些bit与MSR_IA32_VMX_PROCBASED_CTLS2 and |
EXCEPTION_BITMAP |
update_exception_bitmap |
#PF #UD #MC #NM #DB |
|
当Guest模式发生变化时,update_exception_bitmap会调用来更新这些bit |
IO_BITMAP |
Vmx_init |
|
memset(vmx_io_bitmap_a, 0xff, PAGE_SIZE);
clear_bit(0x80, vmx_io_bitmap_a); memset(vmx_io_bitmap_b, 0xff, PAGE_SIZE); |
|
TSC_OFFSET |
kvm_arch_vcpu_load ==>kvm_x86_ops->compute_tsc_offse |
|
vcpu->arch.last_guest_tsc - native_read_tsc |
|
CR0_GUEST_HOST_MASK CR0_READ_SHADOW CR4_READ_SHADOW CR4_GUEST_HOST_MASK |
(1) vmx_cpu_reset: vmx_set_cr0(&vmx->vcpu, kvm_read_cr0(vcpu));
vmx_set_cr4(&vmx->vcpu, 0);
(2) kvm_arch_vcpu_ioctl_set_sregs==>kvm_x86_ops->set_cr0 cr4
(3)异常处理时 |
|
vmx_set_cr0 vmx_set_cr4 |
根据模式发生变化 |
CR3_TARGET_COUNT |
vmx_vcpu_setup |
0 |
|
非nested时为0 |
APIC_ACCESS_ADDR |
|
|
vmcs_write64(APIC_ACCESS_ADDR, page_to_phys(vmx->vcpu.kvm->arch.apic_access_page)); |
|
VIRTUAL_APIC_PAGE_ADDR |
Vmx_vcpu_reset |
|
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, __pa(vmx->vcpu.arch.apic->regs) |
|
TPR_THRESHOLD |
1. kvm_vcpu_ioctl_set_lapic ==》update_cr8_intercept
2.vmx_vcpu_reset 初始化为0 |
|
|
Local apic相关 |
EOI_EXIT_BITMAP |
vcpu_enter_guest ==>vcpu_scan_ioapic==> kvm_ioapic_scan_entry 取得 kvm_x86_ops->load_eoi_exitmap 设置 |
|
|
|
POSTED_INTR_NV |
vmx_vcpu_setup |
|
0xf2 |
|
|
|
|
|
|
POSTED_INTR_DESC_ADDR |
__apic_accept_irq ==>vmx_deliver_posted_interrupt |
|
vmx->pi_desc |
|
MSR_BITMAP |
变量地址初始化 vmx_set_msr_bitmap
vmx_init时初始化 |
|
msr_bitmap |
初始化根据irqchip_in_kernel apic_x2apic_mode 定义 |
VIRTUAL_PROCESSOR_ID |
Vmx_vcpu_reset时初始化 |
|
vmx->vpid 由allocate_vpid动态分配 |
|
PLE_GAP PLE_WINDOW |
static int ple_gap = KVM_VMX_DEFAULT_PLE_GAP;
static int ple_window = KVM_VMX_DEFAULT_PLE_WINDOW; |
|
Vmx模块初始化定义 #define KVM_VMX_DEFAULT_PLE_GAP 128 #define KVM_VMX_DEFAULT_PLE_WINDOW 4096 |
|
PAGE_FAULT_ERROR_CODE_MASK PAGE_FAULT_ERROR_CODE_MATCH |
vmx_vcpu_setup |
0 |
|
Page Fault固定产生vm-exit |
Note1:表中的使能位为KVM最大支持的bits
Note2: Nested Case是指在虚拟机上再跑虚拟机;本文由于篇幅原因,只讨论真实机上直接运行虚拟机的case.
2.3.3 KVM中VM-Entry & VM-Exit 控制字段设置
Name |
Function Flow |
Set Bits |
Description |
Note |
VM_ENTRY_CONTROLS |
setup_vmcs_config 设置 vmx_vcpu_setup ==>vm_entry_controls_init |
BIT1
BIT1 |
Vm-ENTRY时从 guest-state加载dr7 dbg_ctrl Vm-ENTRY时从 guest-state加载 IA32_PAT |
这些bit与MSR_IA32_VMX_ENTRY_CTLS and |
VM_ENTRY_EXCEPTION_ERROR_CODE VM_ENTRY_INSTRUCTION_LEN VM_ENTRY_INTR_INFO_FIELD |
vmx_queue_exception |
|
|
动态注入 |
VM_ENTRY_MSR_LOAD_COUNT VM_ENTRY_MSR_LOAD_ADDR |
vmx_vcpu_reset==》setup_msrs 和模式切换时 |
根据vmx_msr_index 变量 |
|
Vmx_init==> for (i = 0; i < NR_VMX_MSR; ++i) kvm_define_shared_msr(i, vmx_msr_index[i]); |
VM_EXIT_CONTROLS |
setup_vmcs_config 设置 vmx_vcpu_setup ==>vm_exit_controls_init |
BIT15 BIT18 BIT19 |
VM-EXIT时处理器响应中断控制器 Vm-exit时加载host-state IA32_PAT |
|
VM_EXIT_MSR_STORE_COUNT |
0 |
|
|
|
VM_EXIT_MSR_LOAD_COUNT |
vmx_vcpu_reset==》setup_msrs 和模式切换时 |
|
|
|
VM_ENTRY_INTR_INFO_FIELD :
bit[7:0] vector
bit[10:8] event type
bit11 error code
bit31 valid
2.3.4 VM-Exit Info
(1) Exit Reason
bit[15:0] Exit 退出原因arch\x86\include\asm\vmx.h
#define EXIT_REASON_EXCEPTION_NMI 0
#define EXIT_REASON_EXTERNAL_INTERRUPT 1
#define EXIT_REASON_TRIPLE_FAULT 2
.......
#define EXIT_REASON_XSETBV 55
#define EXIT_REASON_APIC_WRITE 56
#define EXIT_REASON_INVPCID 58
同时arch\x86\kvm\vmx.c定义了每个Vm-exit推出原因的处理函数
static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu*vcpu) = {
[EXIT_REASON_EXCEPTION_NMI] = handle_exception,
[EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt,
[EXIT_REASON_TRIPLE_FAULT] = handle_triple_fault,
.......
[EXIT_REASON_MWAIT_INSTRUCTION] =handle_mwait,
[EXIT_REASON_MONITOR_INSTRUCTION] = handle_monitor,
[EXIT_REASON_INVEPT] = handle_invept,
}
bit[28] 为1表明Vm-exit时,存在pending MTF事件
bit[29]为1表明 vm-exit有root-operation环境中产生
bit[31]为1表明, VM-Entry中发生vm-exit
(2) EXIT_QUALIFICATION
记录退出原因的明细信息, KVM支持的类别如下
a) 指令引发的:invplg 保存操作数线性地址, mwait 则为0或1,其它指令记录内存操作数偏移
b) #DB 引发: bit[3:0]对应断点0-3,bit3 异常由访问DR造成, bit14表示单步调试
c) #PF引发: 引起异常的线性地址
d) 接收SIPI引发 bit[7:0]为向量号
e) 接收SMI引发: bit[2:0] io operand size, bit[3] io direction; bit[4] charoperation, bit[5] rep prefix; bit[6] 1 立即数, 0 DX寄存器。 bit[31:16] io port.
f) 任务切换引发bit[15:0] TSS selector, bit[31:30] 0 call , 1 iret, 2 jmp, 3 task gate.
g) 访问控制寄存器引发
bit[3:0]访问控制寄存器; bit[5:4] 访问类型;lmswoperand 指令操作数;
bit[11:8]记录使用的通用寄存器; bit[31:16]指令的源操作数值
h) mov-dr指令引发: bit[2:0] 调试寄存器,bit4 方向。 bit[11:8]记录通用寄存器
i) 访问 I/O 引发: bit[2:0] io operand size, bit[3] io direction; bit[4] charoperation, bit[5] rep prefix; bit[6] 1 立即数, 0 DX寄存器。 bit[31:16] io port.
j) 访问apic-accesspage引发 bit[11:0] apic-access page 内偏移; bit[15:12] type
k) eptviolation引发
bit0read, bit1 write, bit2 execute
bit3readable, bit4 wirtable, bit5 executable
bit7:存在guest-linear address;
bit81 gpa->hpa 0 guest paging-structure
l) eoi虚拟化引发, bit[7:0]中断向量号
m) apic-write引发bit[11:0] apic-access page 内偏移
(3) 向量信息
直接向量
vm-exit interrupt information
bit[7:0]异常或中断号,bit[10:8]中断或异常类别,bit11: error code, bit12 表示blocking by nmi被解除, bi31 valid
vm-exit interruption error code
vm-exit interrupt information bit11 为1时有效,记录错误码
间接向量信息:当一个向量事件delivery间产生一个异常,那么产生一个直接向量(后面产生的异常)和一个间接向量事件(原始事件)。
idt-vector information; idt-vector error code
(4) 指令信息
vm-exit instruction length 分为两类
a. 指令引起vm-exit
b. 向量引起vm-exit
vm-exit instruction information, 根据指令不同而区别