KVM在内核中有丰富的文档,位置在Documentation/virtual/kvm/。linux
00-INDEX:整个目录的索引及介绍文档。api
api.txt:KVM用户空间API,所谓的API主要是经过ioctl来实现的。架构
cpuid.txt:KVM的cpuid相关API。electron
devices/:各类平台相关设备API。ui
hypercalls.txt:KVM的hypercall介绍,介绍了X86和S390的支持的hypercall详细信息。spa
locking.txt:介绍了KVM用到的锁、互斥量。线程
mmu.txt:介绍了Guest X86 MMU功能。blog
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是一系列用来控制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)。
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一个线程。
每一个API,即ioctl都包含一些信息,好比能力、所属架构、类型(System、VM、vcpu)、参数和返回值。这些ioctl很是多,而且庞杂,根据类型分为3类。有一些特殊架构专有的ioctl活在Architecture中列出,若是通用则是all。
下面重点分析Architecture为all或者x86,Capability为basic类型的ioctl。
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大小。 |
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来扩展。 |
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); |
这里重点关注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) { |
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,但反之不成立。