KVM在內核中有豐富的文檔,位置在Documentation/virtual/kvm/。
00-INDEX:整個目錄的索引及介紹文檔。
api.txt:KVM用戶空間API,所謂的API主要是通過ioctl來實現的。
cpuid.txt:KVM的cpuid相關API。
devices/:各種平台相關設備API。
hypercalls.txt:KVM的hypercall介紹,介紹了X86和S390的支持的hypercall詳細信息。
locking.txt:介紹了KVM用到的鎖、互斥量。
mmu.txt:介紹了Guest X86 MMU功能。
msr.txt:X86架構下的MSR用途。
nested-vmx.txt:使用X86特有的VMX來更簡潔高效的運行Guest OS。
ppc-pv.txt和說90-diag.txt:針對PPC和S390架構的特殊用途,忽略。
review-checklist.txt:KVM相關patch的review檢查列表。
timekeeping.txt:X86架構下時間虛擬化設備。
關於KVM API定義的文檔(api.txt)
1.概述
KVM API是一系列用來控制VM各方面的ioctl。它包括三個方面:
System ioctls:這些ioctl用來查詢或者設置影響整個KVM子系統的屬性。並且有一個system ioctl用來創建VM。
VM ioctls:這些ioctl用來查詢或者設置影響整個VM的屬性,其中一個VM ioctl用來創建vcpu。這些VM ioctl只能在用來創建此VM的進程中使用。
vcpu ioctls:這些ioctl用來查詢或者設置能夠控制單個vcpu的屬性。這些vcpu ioctl只能在創建此vcpu的線程中使用。
從上面描述,可以清晰看出三者的層級關系:System ioctls(整個KVM子系統) > VM ioctls(單個VM實體) > vcpu ioctls(單個vcpu)。
2.文件描述符
KVM API是圍繞文件描述符展開的。從打開/dev/kvm開始,獲得操作整個KVM子系統的句柄,這個句柄用來執行System ioctls。基於此System句柄執行KVM_CREATE_VM可以創建一個VM的文件句柄,VM句柄用來執行VM ioctls。基於VM句柄的KVM_CREATE_VCPU用來創建一個vcpu句柄。vcpu句柄執行系列vcpu ioctls用來控制vcpu。
常見的文件句柄的特性在KVM並不適用,比如fork操作等。KVM里面只支持一個VM一個進程,一個vcpu一個線程。
3.API描述
每個API,即ioctl都包含一些信息,比如能力、所屬架構、類型(System、VM、vcpu)、參數和返回值。這些ioctl非常多,並且龐雜,根據類型分為3類。有一些特殊架構專有的ioctl活在Architecture中列出,如果通用則是all。
下面重點分析Architecture為all或者x86,Capability為basic類型的ioctl。
System ioctls
API值 | 說明 |
KVM_GET_API_VERSION | 目前情況下返回值,只有12。如果返回12,表示所有能力為basic的ioctl都能否使用。其他值是不被允許的。 |
KVM_CREATE_VM | 創建一個VM,返回的句柄可以用來控制創建的VM。但此時VM還沒有vcpu和memory。 |
KVM_GET_MSR_INDEX_LIST | 返回Guest支持的MSRs |
KVM_CHECK_EXTENSION | |
KVM_GET_VCPU_MMAP_SIZE | 返回運行vcpu的KVM_RUN使用的共享Memory Region大小。 |
VM ioctls
API值 | 說明 |
KVM_CAP_CHECK_EXTENSION_VM | |
KVM_CREATE_VCPU | 在VM里添加一個vcpu,但是總數不會超過max_cpus。vcpu的id在[0, max_vcpu_id)之間。 |
KVM_GET_DIRTY_LOG | |
KVM_SET_MEMORY_ALIAS | |
KVM_CREATE_IRQCHIP | |
KVM_GET_IRQCHIP | |
KVM_SET_IRQCHIP | |
KVM_GET_CLOCK | |
KVM_SET_CLOCK | |
KVM_GET_VCPU_EVENTS | |
KVM_SET_VCPU_EVENTS | |
KVM_SET_USER_MEMORY_REGION | |
KVM_CAP_ENABLE_CAP_VM | 不是所有的能力都被默認打開,可以使用此ioctl來擴展。 |
vcpu ioctls
API值 | 說明 |
KVM_RUN | 用於運行一個Guest的vcpu。 |
KVM_GET_REGS | 返回vcpu的通用寄存器值,通過struct kvm_regs結構體返回,根據不同架構有所不同。 |
KVM_SET_REGS | 設置vcpu的通用寄存器。 |
KVM_GET_SREGS | 讀取不同架構vcpu的特殊寄存器。 |
KVM_SET_SREGS | 設置不同架構vcpu的特殊寄存器。 |
KVM_TRANSLATE | |
KVM_INTERRUPT | |
KVM_DEBUG_GUEST | |
KVM_GET_MSRS | |
KVM_SET_MSRS | |
KVM_SET_CPUID | |
KVM_SET_SIGNAL_MASK | |
KVM_GET_FPU | 獲取vcpu的FPU狀態。 |
KVM_SET_FPU | 設置vcpu的FPU狀態。 |
KVM_ENABLE_CAP | 不是所有的能力都被默認打開,可以使用此ioctl來擴展。 |
下面一段代碼很好的詮釋了KVM->VM->vcpu之間的關系:
int ret, kvmfd = -1, vmfd = -1, cpufd = -1; kvmfd = qemu_open("/dev/kvm", O_RDWR); |
4.kvm_run結構體
KVM Hypercall(hypercalls.txt)
這里重點關注x86架構下的Hypercall。
KVM hypercall最多支持四個參數,通過rbx、rcx、rdx、rsi。Hypercall調用號通過rax傳遞,返回時rax存放返回值。
一般情況下,其他寄存器不參與Hypercall。
適用於X86架構的Hypercall有三個:KVM_HC_VAPIC_POLL_IRQ、KVM_HC_MMU_OP、KVM_HC_KICK_CPU。
Hypercall的定義在include/uapi/linux/kvm_para.h中:
#define KVM_HC_VAPIC_POLL_IRQ 1 |
KVM_HC_VAPIC_POLL_IRQ:Host檢查關起的中斷。
KVM_HC_MMU_OP:支持MMU操作,比如PTE、flushing TLB、release PT。
KVM_HC_KICK_CPU:喚醒HLT狀態下的vcpu。如果Guest內核模式下一個vcpu等待時間超時而執行HLT指令,另一個同Guest下vcpu可以通過觸發KVM_HC_KICK_CPU來喚醒。
這些Hypercall都在kvm_emulate_hypercall中處理:
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) kvm_x86_ops->skip_emulated_instruction(vcpu); if (kvm_hv_hypercall_enabled(vcpu->kvm)) nr = kvm_register_read(vcpu, VCPU_REGS_RAX); Hypercall調用號 … switch (nr) { |
支持Guest的MMU功能(mmu.txt)
X86 KVM的shadow MMU功能提供一個標准的MMU功能給Guest,將Guest的物理地址轉換成Host物理地址。
關於Nested VMX是一種嵌套式虛擬功能,能夠使一台虛擬機具有物理機CPU特性,支持VMX/SVM硬件虛擬化。參考《Nested VMX》。這樣虛擬機可以使自己成為一個Hypervisors,並在其上安裝虛擬機。
所謂Nested就是運行在Guest上的嵌套Guest,Guest作為Hypervisor。
術語 |
解釋 |
PFN |
Host page frame number |
HPA |
Host physical address |
HVA |
Host virtual address |
GFN |
Guest page frame number |
GPA |
Guest physical address |
GVA |
Guest virtual address |
NGPA |
Nested guest physical address |
NGVA |
Nested guest virtual address |
PTE |
Page table entry |
GPTE |
Guest page table entry |
SPTE |
Shadow page table entry |
TDP |
Two dimensional paging |
此處KVM MMU功能主要工作就是配置處理器的MMU以達到將Guest地址轉變到Host。有下面三種不同的轉變需求:
-當Guest關閉分頁功能時,將Guest物理地址轉變成Host物理地址。GPA->HPA
-當Guesst使能分頁功能時,將Guest虛擬地址,轉變成Guest物理地址,進而Host物理地址。GVA->GPA->HPA
-當Guest又虛擬化一個嵌套Guest時,將嵌套的Guest虛擬地址轉變成嵌套的物理地址,進而Guest物理地址,最后是Host物理地址。NGVA->NGPA->GPA-HPA
GPA是運行KVM進程的用戶地址空間的一部分。用戶空間定義了Guest地址到用戶空間地址的轉變(GPA->HVA)。兩個不同的GPA可以映射到一個HVA,但反之不成立。