在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工作原理完全相同。