在qeum/kvm系列文章中分析了Intel VT的实现框架, 这里对AMD的虚拟化技术框架做一个对比性的小结。
(1) 基本指令
首先是判断 cpu是否支持svm:
if (CPUID 8000_0001.ECX[SVM] == 0)
return SVM_NOT_AVAIL;
if (VM_CR.SVMDIS == 0)
return SVM_ALLOWED;
实现代码位于:
下面是虚拟化指令及其opcode:
#define SVM_VMLOAD ".byte 0x0f, 0x01, 0xda"
#define SVM_VMRUN ".byte 0x0f, 0x01, 0xd8"
#define SVM_VMSAVE ".byte 0x0f, 0x01, 0xdb"
#define SVM_CLGI ".byte 0x0f, 0x01, 0xdd"
#define SVM_STGI ".byte 0x0f, 0x01, 0xdc"
#define SVM_INVLPGA ".byte 0x0f, 0x01, 0xdf"
与intel vt类似,AMD-V VMM 通过执行 VMRUN 指令使 CPU 进入“guest”操作模式而执行Guest Os的代码; Guest在运行时,遇到敏感指令或事件,硬件就执行 VMEXIT 行为,使 CPU 回到“host”模式而执行 VMM 的代码。 VMRUN 指令运行的参数是一个物理地址指针,其指向一个Virtual Machine Control Block (VMCB) 的内存数据结构. VMRUN 命令以 VMCB 为参数,使 CPU 进入“guest”状态, 按 VMCB.SAVE 的内容恢复虚拟机的 CPU 寄存器状态,并按 VMCB.SAVE 中 CS:RIP 字段指示的地址开始执行虚拟机 的代码, 并将之前 VMM 的 CPU 状态保存在 MSR_VM_HSAVE_PA 寄存器所指向的物理内存区域中。VMRUN 所保存的 VMM 的CPU 状态的 CS:RIP 实际上就是 VMM 的代码中 VMCB 的下一个指令, 当虚拟机因某种原因而导致 #VMEXIT 时,VMM 会从 VMRUN 后的一条指令开始执行。 CPU 执行 #VMEXIT 行为时,会自动将虚拟机的状态保存到 VMCB.SAVE 区,并从 MSR_VM_HSAVE_PA 指定的区域加载 VMM 的 CPU 状态。
svm_vcpu_run (svm.c) ==>
"push %%" _ASM_BP "; \n\t"
"mov %c[rbx](%[svm]), %%" _ASM_BX " \n\t" //从svm结构体中设置的相应寄存器的值加载到实际寄存器中
"mov %c[rcx](%[svm]), %%" _ASM_CX " \n\t"
"mov %c[rdx](%[svm]), %%" _ASM_DX " \n\t"
"mov %c[rsi](%[svm]), %%" _ASM_SI " \n\t"
"mov %c[rdi](%[svm]), %%" _ASM_DI " \n\t"
"mov %c[rbp](%[svm]), %%" _ASM_BP " \n\t"
#ifdef CONFIG_X86_64
"mov %c[r8](%[svm]), %%r8 \n\t"
。。。。。。
#endif
/* Enter guest mode */
"push %%" _ASM_AX " \n\t"
"mov %c[vmcb](%[svm]), %%" _ASM_AX " \n\t" //设置vmrun的vmcb地址
__ex(SVM_VMLOAD) "\n\t"
__ex(SVM_VMRUN) "\n\t"
__ex(SVM_VMSAVE) "\n\t"
"pop %%" _ASM_AX " \n\t"
/* Save guest registers, load host registers */
"mov %%" _ASM_BX ", %c[rbx](%[svm]) \n\t"
"mov %%" _ASM_CX ", %c[rcx](%[svm]) \n\t"
"mov %%" _ASM_DX ", %c[rdx](%[svm]) \n\t"
"mov %%" _ASM_SI ", %c[rsi](%[svm]) \n\t"
"mov %%" _ASM_DI ", %c[rdi](%[svm]) \n\t"
"mov %%" _ASM_BP ", %c[rbp](%[svm]) \n\t"
#ifdef CONFIG_X86_64
"mov %%r8, %c[r8](%[svm]) \n\t"
。。。。。。
#endif
"pop %%" _ASM_BP
VMLOAD 和 VMSAVE 指令是对 VMRUN 的补充,他们用来加载和恢复一些并不需要经常使用的 CPU 状态,如 FS, GS, TR, LDTR 寄存器以及其相关的隐含的描述符寄存器的内容,VMLOAD 和 VMSAVE 可以让 VMM 的实现对 “guest”进入和退出的过程进行优化
(2) VMCB
vmcb定义如下,分为控制区(该区域也包含vm-exit的原因信息)与保存区:
struct __attribute__ ((__packed__)) vmcb {
struct vmcb_control_area control;
struct vmcb_save_area save;
};
vmcb_control_area_control又分为控制信息与vm-exit info两类
a. 控制信息
name | description | Linux default setting | Note |
intercept_cr | 控制CR寄存器的读写vm-exit控制 | CR0 : RW CR3: RW CR4:RW CR8:W |
|
Intercept_dr | 控制DR寄存器的读写vm-exit控制 | D0 to D7 RW |
|
intercept_exceptions | 异常导致vm-exit | #UD #PF, #MC |
|
intercept | 指令或条件引起vm-exit | Intr, 受保护的io,msr, invplga, task切换, shutdown, vmrun,vmcall,vmload, vmsave,stg,clg,skinti,wbinvd,monitor,mwait,xsetbv smi,nmi,rdpmc,cpuid,hld,invd,invplg |
|
iopm_base_pa | Io permission map address |
|
|
msrpm_base_pa | MSR permission map address |
|
|
tlb_ctl | Guest tlb entry flush 控制 | 01b: Flush entire TLB |
|
Int_ctl | 中断控制 | V_INTR_MASKING |
|
int_vector | 注入的中断向量号 | 用于中断注入 |
|
nested_ctl | 嵌套虚拟化控制 |
|
|
asid | Address Space Identifier | 用于区分不同虚拟机的地址空间 |
|
lbr_ctl | 分支预测虚拟化控制 | svm_enable_lbrv() |
|
event_inj | 用于异常注入 | bit[63:32] ErrorCode bit[10:8] type: 0: External or virtual interrupt 1: NIM 2:Excetpion 3:Software Interrup bit[7:0] vector |
|
b. VM-exit信息
exit_code 参看Appendix C SVM Intercept Exit Codes
小结:AMD-V与Intel-VT基本类似,但没有intel-vt对APIC access page的实现(本文针对Linux 3.16)
(3) 内存虚拟化
Intel VMX支持EPT技术来加速内存虚拟化,同样AMD提供了nested paing机制(NPT)
首先,在Guest OS中,当系统开启一个进程时,OS会为这个进程配置一个分页表。此时,Guest Os的线性地址位址)透过gPT (Guest Page Tables)映射到Guest的物理位址(Guest Physical Address)。其分页表(gPT)位于Guest的物理内存中,gCR3(Guest中的CR3)相当于硬件的暂存器,负责执行Guest OS中的内存映射。接下来,VMM物理内存中的nPT(nested page tables),将GPA映射到HPA。nPT位于VMM Host中,硬件上则由nCR3(相对于Guest,nCR3可看成是Host OS中的CR3)负责执行映射任务。
由此可见Intel EPT与AMD NPT工作原理完全相同。