VMX 分为root模式和non root模式,
VMCS通过VMPTRST,VMPTRLD修改。 VMREAD,VMWRITE修改VMCS内容。 VMXON,VMXOFF打开关闭VMX。 VMLAUNCH,VMRESUME
VMCS状态的状态管理? VMPTRLD,转化为active,current
active,current
VMCS的几个区域: 1.Guest-state area 2.Host-state area 3.VM-execution cotrol fields 4.vm-exit control fields 5.vm-entry cotrol fields 6.vm-exit information fileds
Intel寄存器说明: IDTR:中断描述表指针 GDTR:全局段描述表指针 LDTR:局部段描述表指针
segment selector:包含index,LDTorGDT,访问权限。 CS,SS,DS,ES,FS,GS
TSS段:TSS段的作用。TR寄存器指定。 RSP RIP PFLAGS 栈指针,代码段指针,标示位。 EAX,EBX,ECX,等通用寄存器。 CR0,CR1,CR3,CR4等控制寄存器。CR3指向页表。
那些寄存器需要保留: 理论上需要保留所有的状态信息,以便虚拟机退出再进入的时候,可以正常运行。
实际上硬件VMCS会自动保存和恢复哪些寄存器呢? CR0,CR3,CR4
RSP RIP PFLAGS LDTR TR GDTR IDTR MSR: CS, SS, DS, ES, FS, GS
CR1,CR2为什么不保留? CR2保存的是导致缺页中断的线性地址,CR1目前没有使用。
有哪些寄存器,硬件没有自动保存呢? EAX,EBX,ECX,等通用寄存器。
为什么硬件没有自动保存呢? 因为这些是通用寄存器,需要通过这些寄存器,通过软件来模拟某些某些指令。
vmx_vcpu_run中的处理:
asm(
/* Store host registers */
"push %%" _ASM_DX "; push %%" _ASM_BP ";"
"push %%" _ASM_CX " \n\t" /* 在Host OS的栈中保存一个空间,当vm exit的时候,先保存cx到栈上 */
"push %%" _ASM_CX " \n\t" //保存hostos当前的寄存器信息到内核栈中
"cmp %%" _ASM_SP ", %c[host_rsp](%0) \n\t" //把HostOS的SP的保存在vmx中的rsp
"je 1f \n\t"
"mov %%" _ASM_SP ", %c[host_rsp](%0) \n\t"
__ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t" //把sp的值保存在vmcs中
"1: \n\t"
/* Reload cr2 if changed */ //判断vmx中的CR2和实际的CR2,如果不一致,修改CR2
"mov %c[cr2](%0), %%" _ASM_AX " \n\t"
"mov %%cr2, %%" _ASM_DX " \n\t"
"cmp %%" _ASM_AX ", %%" _ASM_DX " \n\t"
"je 2f \n\t"
"mov %%" _ASM_AX", %%cr2 \n\t"
"2: \n\t"
/* Check if vmlaunch of vmresume is needed */ //在这里判断是因为这里还可以访问vmx
"cmpl $0, %c[launched](%0) \n\t"
/* Load guest registers. Don't clobber flags. */ //把vmx数据结构中的ax,bx,dx,si,di,bp恢复到寄存器
"mov %c[rax](%0), %%" _ASM_AX " \n\t"
"mov %c[rbx](%0), %%" _ASM_BX " \n\t"
"mov %c[rdx](%0), %%" _ASM_DX " \n\t"
"mov %c[rsi](%0), %%" _ASM_SI " \n\t"
"mov %c[rdi](%0), %%" _ASM_DI " \n\t"
"mov %c[rbp](%0), %%" _ASM_BP " \n\t"
#ifdef CONFIG_X86_64 //64位增加的寄存器
"mov %c[r8](%0), %%r8 \n\t"
"mov %c[r9](%0), %%r9 \n\t"
"mov %c[r10](%0), %%r10 \n\t"
"mov %c[r11](%0), %%r11 \n\t"
"mov %c[r12](%0), %%r12 \n\t"
"mov %c[r13](%0), %%r13 \n\t"
"mov %c[r14](%0), %%r14 \n\t"
"mov %c[r15](%0), %%r15 \n\t"
#endif
"mov %c[rcx](%0), %%" _ASM_CX " \n\t" /* kills %0 (ecx) */ //为什么cx放在后面?因为cx本身存放vmx的指针
/* Enter guest mode */ //根据前面lanched的判断结果,进入non-root模式
"jne 1f \n\t"
__ex(ASM_VMX_VMLAUNCH) "\n\t"
"jmp 2f \n\t"
"1: " __ex(ASM_VMX_VMRESUME) "\n\t"
"2: "
/* Save guest registers, load host registers, keep flags */ 退出了
"mov %0, %c[wordsize](%%" _ASM_SP ") \n\t" //把cx保存在栈中遇留的寄存器
"pop %0 \n\t" //把cx从栈中恢复,这个时候cx指向vmx
"mov %%" _ASM_AX ", %c[rax](%0) \n\t" //把寄存器需要保存在vmx中
"mov %%" _ASM_BX ", %c[rbx](%0) \n\t"
__ASM_SIZE(pop) " %c[rcx](%0) \n\t" //cx为什么要特殊处理? cx存放在栈中
"mov %%" _ASM_DX ", %c[rdx](%0) \n\t"
"mov %%" _ASM_SI ", %c[rsi](%0) \n\t"
"mov %%" _ASM_DI ", %c[rdi](%0) \n\t"
"mov %%" _ASM_BP ", %c[rbp](%0) \n\t"
#ifdef CONFIG_X86_64
"mov %%r8, %c[r8](%0) \n\t"
"mov %%r9, %c[r9](%0) \n\t"
"mov %%r10, %c[r10](%0) \n\t"
"mov %%r11, %c[r11](%0) \n\t"
"mov %%r12, %c[r12](%0) \n\t"
"mov %%r13, %c[r13](%0) \n\t"
"mov %%r14, %c[r14](%0) \n\t"
"mov %%r15, %c[r15](%0) \n\t"
#endif
"mov %%cr2, %%" _ASM_AX " \n\t"
"mov %%" _ASM_AX ", %c[cr2](%0) \n\t" //保存cr2寄存器到vmx中的cr2
"pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" //从内核栈上恢复bp,dx
"setbe %c[fail](%0) \n\t"
".pushsection .rodata \n\t"
".global vmx_return \n\t"
"vmx_return: " _ASM_PTR " 2b \n\t"
".popsection"
: : "c"(vmx), "d"((unsigned long)HOST_RSP), 输入参数,vmx变量存在在ecx中,HOSTRSP存放在edx中。
[launched]"i"(offsetof(struct vcpu_vmx, __launched)),
[fail]"i"(offsetof(struct vcpu_vmx, fail)),
[host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)),
[rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])),
[rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])),
[rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])),
[rdx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDX])),
[rsi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RSI])),
[rdi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDI])),
[rbp]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBP])),
#ifdef CONFIG_X86_64
[r8]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R8])),
[r9]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R9])),
[r10]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R10])),
[r11]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R11])),
[r12]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R12])),
[r13]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R13])),
[r14]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R14])),
[r15]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R15])),
#endif
[cr2]"i"(offsetof(struct vcpu_vmx, vcpu.arch.cr2)),
[wordsize]"i"(sizeof(ulong))
: "cc", "memory" //被影响的寄存器,cx,dx,bp在栈中保存
#ifdef CONFIG_X86_64
, "rax", "rbx", "rdi", "rsi"
, "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
#else
, "eax", "ebx", "edi", "esi"
#endif
);
Guest non-register state PDPTES EPT页表,指向EPT页表 Guest interrupt status RVI VAPCI的支持,正在请求的最高优先级中断 SVI VAPCI的支持,正在处理的最高优先级中断
Host State VM Entry的时候,会自动保存的HostOS的状态。 CR0,CR3,CR4 RSP,RIP CS,SS,DS,ES,FS,GS,TR GDTR,IDTR
VM execution control
Pin-based VM execution Control,指定什么情况下中断导致虚拟机退出。 外部中断,NMI中断都会导致VM exits Process posted interrupts,VAPIC的支持, 通过posted-interrupt nodification vector,更新apic页表。
Process-based Vm execution control,控制那些指令会导致VM exit。 Excepion Bitmap,确定哪些异常导致VM exit IO Bitmap Address,确定哪些IO操作导致VM exit TSC Offest:TSC偏差 MSR-Bitmap Address:
VM-Entry controls fields event injection,触发向虚拟机中发中断
VM exit information fields Exit Reason: Exit qualification:更详细的推出原因 Guest Liner address Guest physical address:用于EPT页表失效的情况。
Vm-exit interuption information:中断退出情况下的信息 vm-exit interrupt error code:硬件异常的error code
vm-exit instruction length: 因为执行指令导致的退出
|