KVM API 简介
以下笔记来自阅读:https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt
1. 基本描述
KVM API
本质上是为了控制虚拟机的方方面面而下发的一组ioctl,这些ioctl可以划分为四类:
- system ioctl:
对影响整个KVM系统的属性进行查询或设置。创建虚拟机就是其中的一个系统ioctl。
- vm ioctl:
对影响整个虚拟机的属性进行查询或设置,比如虚拟机内存布局。vm
ioctl创建了虚拟机的虚拟机CPU(vcpu)和其他设备。
vm ioctl必须由创建虚拟机的进程下发。
- vcpu ioctl:
对控制单个虚拟CPU(vcpu)的属性进行查询或设置。
除了在文档中特别标注的异步vcpu ioctl,vcpu
ioctl应该由创建虚拟CPU的线程下发。否则,在切换线程后的第一次ioctl调用将对性能产生影响。
- device ioctl:
对控制单个设备的属性进行查询或设置。
设备ioctl必须由创建虚拟机的进程下发。
2. 文件描述符
文件描述符是整个KVM API的核心。打开"/dev/kvm"设备后会得到一个文件描述符,该文件描述符可以用来下发system ioctl。通过这个文件描述符下发KVM_CREATE_VM ioctl将创建一个虚拟机的文件描述符。这个虚拟机的文件描述符可以用来下发vm ioctl。通过虚拟机文件描述符下发KVM_CREATE_VCPU 和 KVM_CREATE_DEVICE ioctl将创建对应的虚拟机CPU和设备,并返回对应CPU和设备的文件描述符。最后,通过虚拟CPU或者设备文件描述符下发的ioctl可以用来控制对应虚拟机CPU或者设备。对于虚拟CPU而言,这包含了其重要的工作:运行Guest代码。
一般文件描述符可以通过fork()或者Unix Domain Socket的SCM_RIGHTS特性在进程间迁移,但是KVM并不显式支持这些方式。对操作系统而言,这些行为是无害的,但是KVM API的行为将无法保证。KVM 支持的 ioctl 使用模式参考“1. 基本描述"部分。
需要重点注意的是,尽管vm ioctl只能由创建虚拟机的进程下发,VM的生命周期是和其文件描述符关联的,而不是创建这个虚拟机的进程。换句话说,虚拟机和它使用的资源,包括关联的地址空间,在虚拟机对应的文件描述符的所有引用释放之前都不会回收。例如,在ioctl(KVM_CREATE_VM)之后如果调用了fork(),那么虚拟机在父进程和它的子进程都释放虚拟机文件描述符之后才能回收资源。
因为虚拟机资源在其文件描述符的引用都释放之前不能回收,在没有考虑清楚之前,非常不推荐使用 fork(), dup()等方式增加新的描述符引用。这些方式可能带来意料之外的副作用,比如在虚拟机关机之后,虚拟机使用的内存资源并没有回收。
3. 扩展
在Linux内核版本2.6.22之后,KVM的ABI已经稳定,破坏向后兼容的改动也不被允许。但是,KVM支持对使用的API添加向后兼容的扩展。
扩展机制并不基于Linux版本号,KVM定义了扩展标识,并提供功能查询特定扩展标识是否存在。如果存在,应用程序就可以使用该扩展对应的一组ioctl。
4. API 描述
本节描述了能够控制KVM虚拟机的ioctl。对于每个ioctl,提供如下的信息:
- 能力: 该ioctl对应的KVM扩展。'basic'表明任何支持API
版本12(参考4.1)的内核都可以提供该ioctl;
'KVM_CAP_xyz'意味着需要使用KVM_CHECK_EXTENSION检查该扩展是否可用;
'none'意味着不是所有的内核都支持该ioctl,并且也没有对应的检查方式,如果不支持,ioctl会返回错误-ENOTTY。
- 架构:
包含该ioctl的指令集架构。x86即包含i386也包含x86_64。
- 类型: system、vm、vcpu或者device ioctl。
- 参数: ioctl的调用参数。
- 返回值:
ioctl返回值。一般错误如EBADF、ENOMEM、EINVAL等没有详细错误信息,但是有特定含义的错误有详细信息。
System Ioctl
序号 | 名称 | 能力 | 架构 | 参数 | 返回值 | 说明 |
---|---|---|---|---|---|---|
4.1 | KVM_GET_API_VERSION | basic | all | none | KVM_API_VERSION(12)常量 | 表明当前使用的是稳定的KVM API版本。如果返回值不是12应用程序应拒绝运行。 |
4.2 | KVM_CREATE_VM | basic | all | 机器类型标识(KVM_VM_*) | 可以控制虚拟机的vm fd | 新创建的虚拟机没有vcpu也没有内存。 |
4.3 | KVM_GET_MSR_INDEX_LIST | basic | x86 | struct kvm_msr_list (in/out) | 成功返回0,错误返回-1 | 获取guest支持的msr列表 |
KVM_GET_MSR_FEATURE_INDEX_LIST | KVM_CAP_GET_MSR_FEATURES | x86 | struct kvm_msr_list (in/out) | 成功返回0,错误返回-1 | 列出可以传给KVM_GET_MSRS的msr列表 | |
4.4 | KVM_CHECK_EXTENSION | basic | all | 扩展标识(KVM_CAP_*) | 不支持返回0,1或其他正值表示支持 | 查询KVM API扩展 |
4.5 | KVM_GET_VCPU_MMAP_SIZE | basic | all | none | vcpu mmap区域的大小 | KVM_RUN ioctl 使用一个共享内存区域与用户态通信,KVM_GET_VCPU_MMAP_SIZE 返回该区域大小 |
4.18 | KVM_GET_MSRS | KVM_CAP_GET_MSR_FEATURES | x86 | struct kvm_msrs (in/out) | 成功返回msr 数量, 失败返回-1 | 读取虚拟机可使用的MSR 特性的值 |
4.46 | KVM_GET_SUPPORTED_CPUID | KVM_CAP_EXT_CPUID | x86 | struct kvm_cpuid2 (in/out) | 成功返回0, 失败返回-1 | 返回x86平台和kvm默认支持的cpuid信息 |
4.88 | KVM_GET_EMULATED_CPUID | KVM_CAP_EXT_EMUL_CPUID | x86 | struct kvm_cpuid2 (in/out) | 成功返回0, 失败返回-1 | 返回由KVM模拟的CPUID feature |
4.104 | KVM_X86_GET_MCE_CAP_SUPPORTED | KVM_CAP_MCE | x86 | u64 mce_cap (out) | 成功返回0, 失败返回-1 | 返回支持的MCE capability |
4.110 | KVM_MEMORY_ENCRYPT_OP | basic | x86 | 平台相关输入(in/out) | 成功返回0, 失败返回-1 | 下发内存加密命令,目前用于下发SEV命令(AMD SEV) |
4.111 | KVM_MEMORY_ENCRYPT_REG_REGION | basic | x86 | struct kvm_enc_region (in) | 成功返回0, 失败返回-1 | 注册guest加密内存区域(AMD SEV) |
4.112 | KVM_MEMORY_ENCRYPT_UNREG_REGION | basic | x86 | struct kvm_enc_region (in) | 成功返回0, 失败返回-1 | 取消注册guest加密内存区域(AMD SEV) |
VM Ioctl
序号 | 名称 | 能力 | 架构 | 参数 | 返回值 | 说明 |
---|---|---|---|---|---|---|
4.4 | KVM_CHECK_EXTENSION | KVM_CAP_CHECK_EXTENSION_VM | all | 扩展标识(KVM_CAP_*) | 不支持返回0,1或其他正值表示支持 | 查询VM支持的KVM API扩展 |
4.6 | KVM_SET_MEMORY_REGION | basic | all | struct kvm_memory_region (in) | 成功返回0, 失败返回-1 | 该API已废弃 |
4.7 | KVM_CREATE_VCPU | basic | all | vcpu id (apic id on x86) | 成功返回vcpu fd,失败返回-1 | 创建vcpu |
4.8 | KVM_GET_DIRTY_LOG | basic | all | struct kvm_dirty_log (in/out) | 成功返回0, 失败返回-1 | 返回从上一次调用该ioctl以来,memory slot页面的dirty bitmap |
4.9 | KVM_SET_MEMORY_ALIAS | basic | all | struct kvm_memory_alias (in) | 成功返回0, 失败返回-1 | 该API已废弃 |
4.24 | KVM_CREATE_IRQCHIP | KVM_CAP_IRQCHIP | x86, arm, arm64 | none | 成功返回0, 失败返回-1 | 在内核中创建虚拟机对应的中断控制器 |
4.25 | KVM_IRQ_LINE | KVM_CAP_IRQCHIP | x86, arm, arm64 | struct kvm_irq_level | 成功返回0, 失败返回-1 | 设置中断控制器指定中断的电位(1/0) |
4.26 | KVM_GET_IRQCHIP | KVM_CAP_IRQCHIP | x86 | struct kvm_irqchip (in/out) | 成功返回0, 失败返回-1 | 读取中断控制器状态 |
4.27 | KVM_SET_IRQCHIP | KVM_CAP_IRQCHIP | x86 | struct kvm_irqchip (in) | 成功返回0, 失败返回-1 | 设置中断控制器状态 |
4.28 | KVM_XEN_HVM_CONFIG | KVM_CAP_XEN_HVM | x86 | struct kvm_xen_hvm_config (in) | 成功返回0, 失败返回-1 | 设置Xen HVM Guest用来初始化hypercall所使用的msr寄存器状态 |
4.29 | KVM_GET_CLOCK | KVM_CAP_ADJUST_CLOCK | x86 | struct kvm_clock_data (out) | 成功返回0, 失败返回-1 | 获取当前guest看到的kvmclock时间戳 |
4.30 | KVM_SET_CLOCK | KVM_CAP_ADJUST_CLOCK | x86 | struct kvm_clock_data (in) | 成功返回0, 失败返回-1 | 设置当前guest的kvmclock为指定值 |
4.33 | KVM_GET_DEBUGREGS | KVM_CAP_DEBUGREGS | x86 | struct kvm_debugregs (out) | 成功返回0, 失败返回-1 | 读取vcpu debug寄存器 |
4.34 | KVM_SET_DEBUGREGS | KVM_CAP_DEBUGREGS | x86 | struct kvm_debugregs (in) | 成功返回0, 失败返回-1 | 设置vcpu debug寄存器 |
4.35 | KVM_SET_USER_MEMORY_REGION | KVM_CAP_USER_MEMORY | all | struct kvm_userspace_memory_region (in) | 成功返回0, 失败返回-1 | 该API允许创建、修改、删除guest虚拟机的物理内存槽(physical memory slot) |
4.36 | KVM_SET_TSS_ADDR | KVM_CAP_SET_TSS_ADDR | x86 | unsigned long tss_address (in) | 成功返回0, 失败返回-1 | 设置VM86模式TSS所使用的地址 |
4.40 | KVM_SET_IDENTITY_MAP_ADDR | KVM_CAP_SET_IDENTITY_MAP_ADDR | x86 | unsigned long identity (in) | 成功返回0, 失败返回-1 | 设置VM86模式下Guest页表的物理地址 |
4.41 | KVM_SET_BOOT_CPU_ID | KVM_SET_BOOT_CPU_ID | x86 | unsigned long vcpu_id | 成功返回0, 失败返回-1 | 定义哪个vcpu是BSP(默认是0号vcpu) |
4.52 | KVM_SET_GSI_ROUTING | KVM_CAP_IRQ_ROUTING | x86, s390, arm, arm64 | struct kvm_irq_routing (in) | 成功返回0, 失败返回-1 | 设置GSI路由项 |
4.59 | KVM_IOEVENTFD | KVM_CAP_IOEVENTFD | all | struct kvm_ioeventfd (in) | 成功返回0, 失败返回非0 | 挂载/卸载ioeventfd到虚拟机内的PIO和MMIO地址 |
4.71 | KVM_SIGNAL_MSI | KVM_CAP_SIGNAL_MSI | x86, arm, arm64 | struct kvm_msi (in) | 成功下发返回>0, 虚拟机已经屏蔽该MSI返回0, 错误返回-1 | 注入MSI中断信息 |
4.71 | KVM_CREATE_PIT2 | KVM_CAP_PIT2 | x86 | struct kvm_pit_config (in) | 成功返回0, 失败返回-1 | 创建i8254PIT(该API 序号在KVM文档中有误) |
4.72 | KVM_GET_PIT2 | KVM_CAP_PIT_STATE2 | x86 | struct kvm_pit_state2 (out) | 成功返回0, 失败返回-1 | 返回内核内PIT设备状态 |
4.73 | KVM_SET_PIT2 | KVM_CAP_PIT_STATE2 | x86 | struct kvm_pit_state2 (in) | 成功返回0, 失败返回-1 | 设置内核PIT设备状态 |
4.75 | KVM_IRQFD | KVM_CAP_IRQFD | x86, s390, arm, arm64 | struct kvm_irqfd (in) | 成功返回0, 失败返回-1 | 设置用于触发中断的eventfd |
4.99 | KVM_REINJECT_CONTROL | KVM_CAP_REINJECT_CONTROL | x86 | struct kvm_reinject_control (in) | 成功返回0, 失败返回负值(-EFAULT/-ENXIO) | 设置i8254 PIT的reinject或!reinject模式 |
4.113 | KVM_HYPERV_EVENTFD | KVM_CAP_HYPERV_EVENTFD | x86 | struct kvm_hyperv_eventfd (in) | 成功返回0, 失败返回负值(-EINVAL/-ENOENT/-EEXIST) | 注册监听Hyper-V hypercall的eventfd |
4.116 | KVM_REGISTER_COALESCED_MMIO | KVM_CAP_COALESCED_MMIO 或 KVM_CAP_COALESCED_PIO | all | struct kvm_coalesced_mmio_zone | 成功返回0, 失败返回负值 | 注册coalesed mmio/pio 内存区域 |
KVM_UNREGISTER_COALESCED_MMIO | KVM_CAP_COALESCED_MMIO 或 KVM_CAP_COALESCED_PIO | all | struct kvm_coalesced_mmio_zone | 成功返回0, 失败返回负值 | 取消注册coalesed mmio/pio 内存区域 | |
4.117 | KVM_CLEAR_DIRTY_LOG | KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 | x86, arm, arm64, mips | struct kvm_dirty_log (in) | 成功返回0, 失败返回-1 | 清除内存页的dirty标记 |
4.120 | KVM_SET_PMU_EVENT_FILTER | KVM_CAP_PMU_EVENT_FILTER | x86 | struct kvm_pmu_event_filter (in) | 成功返回0, 失败返回-1 | 限制guest可以设置的PMU事件 |
VCPU Ioctl
序号 | 名称 | 能力 | 架构 | 参数 | 返回值 | 说明 |
---|---|---|---|---|---|---|
4.10 | KVM_RUN | basic | all | none | 成功返回0, 失败返回-1 | 该API负责运行一个vcpu。 |
4.11 | KVM_GET_REGS | basic | 除了arm和arm64以外的架构 | struct kvm_regs (out) | 成功返回0, 失败返回-1 | 获取vcpu 通用寄存器(GPR)的值 |
4.12 | KVM_SET_REGS | basic | 除了arm和arm64以外的架构 | struct kvm_regs (in) | 成功返回0, 失败返回-1 | 设置vcpu的通用寄存器(GPR)的值 |
4.13 | KVM_GET_SREGS | basic | x86, ppc | struct kvm_sregs (out) | 成功返回0, 失败返回-1 | 获取vcpu的特殊寄存器的值 |
4.14 | KVM_SET_SREGS | basic | x86, ppc | struct kvm_sregs (in) | 成功返回0, 失败返回-1 | 设置vcpu的特殊寄存器的值 |
4.15 | KVM_TRANSLATE | basic | x86 | struct kvm_translation (in/out) | 成功返回0, 失败返回-1 | 根据当前vcpu的寻址模式,将虚拟地址转换为物理地址 |
4.16 | KVM_INTERRUPT | basic | x86, ppc, mips | struct kvm_interrupt (in) | 成功返回0, 失败返回负值 | 添加需要注入的中断,该中断会被保存在队列中 |
4.17 | KVM_DEBUG_GUEST | basic | none | none | 失败返回-1 | 该API已删除,使用KVM_SET_GUEST_DEBUG |
4.18 | KVM_GET_MSRS | basic | x86 | struct kvm_msrs (in/out) | 成功返回msr 数量, 失败返回-1 | 读取vcpu的msr寄存器,支持的msr 索引可以通过KVM_GET_MSR_INDEX_LIST 获取 |
4.19 | KVM_SET_MSRS | basic | x86 | struct kvm_msrs (in) | 成功返回0, 失败返回-1 | 设置msr寄存器的值 |
4.20 | KVM_SET_CPUID | basic | x86 | struct kvm_cpuid (in) | 成功返回0, 失败返回-1 | 定义vcpu cpuid指令的返回值,如果可行,使用KVM_SET_CPUID2而不是该API |
4.21 | KVM_SET_SIGNAL_MASK | basic | all | struct kvm_signal_mask (in) | 成功返回0, 失败返回-1 | 定义在KVM_RUN过程中需要屏蔽的中断 |
4.22 | KVM_GET_FPU | basic | x86 | struct kvm_fpu (out) | 成功返回0, 失败返回-1 | 读取vcpu 浮点运算状态 |
4.23 | KVM_SET_FPU | basic | x86 | struct kvm_fpu (in) | 成功返回0, 失败返回-1 | 设置vcpu 浮点运算状态 |
4.31 | KVM_GET_VCPU_EVENTS | KVM_CAP_VCPU_EVENTS | x86, arm , arm64 | struct kvm_vcpu_event (out) | 成功返回0, 失败返回-1 | 获取当前处于等待状态的异常、中断、nmi、smi及对应vcpu状态 |
4.32 | KVM_SET_VCPU_EVENTS | KVM_CAP_VCPU_EVENTS | x86, arm , arm64 | struct kvm_vcpu_event (in) | 成功返回0, 失败返回-1 | 设置当前处于等待状态的异常、中断、nmi、smi及对应vcpu状态 |
4.37 | KVM_ENABLE_CAP | KVM_CAP_ENABLE_CAP_VM | all | struct kvm_enable_cap (in) | 成功返回0, 失败返回-1 | 启用扩展 |
4.38 | KVM_GET_MP_STATE | KVM_CAP_MP_STATE | x86, s390, arm, arm64 | struct kvm_mp_state (out) | 成功返回0, 失败返回-1 | 获取vcpu的处理器状态 |
4.39 | KVM_SET_MP_STATE | KVM_CAP_MP_STATE | x86, s390, arm, arm64 | struct kvm_mp_state (in) | 成功返回0, 失败返回-1 | 设置vcpu的处理器状态 |
4.42 | KVM_GET_XSAVE | KVM_CAP_XSAVE | x86 | struct kvm_xsave (out) | 成功返回0, 失败返回-1 | 获取vcpu xsave状态 |
4.43 | KVM_SET_XSAVE | KVM_CAP_XSAVE | x86 | struct kvm_xsave (in) | 成功返回0, 失败返回-1 | 设置vcpu xsave状态 |
4.44 | KVM_GET_XCRS | KVM_CAP_XCRS | x86 | struct kvm_xcrs (out) | 成功返回0, 失败返回-1 | 获取vcpu xcr寄存器状态 |
4.45 | KVM_SET_XCRS | KVM_CAP_XCRS | x86 | struct kvm_xcrs (in) | 成功返回0, 失败返回-1 | 设置vcpu xcr寄存器状态 |
4.55 | KVM_SET_TSC_KHZ | KVM_CAP_TSC_CONTROL | x86 | virtual tsc_khz | 成功返回0, 失败返回-1 | 设置虚拟机的tsc频率 |
4.56 | KVM_GET_TSC_KHZ | KVM_CAP_GET_TSC_KHZ | x86 | none | 成功返回virtual tsc-khz, 失败返回负值 | 返回虚拟机的TSC频率 |
4.57 | KVM_GET_LAPIC | KVM_CAP_IRQCHIP | x86 | struct kvm_lapic_state (out) | 成功返回0, 失败返回-1 | 返回虚拟机LAPIC寄存器内容 |
4.58 | KVM_SET_LAPIC | KVM_CAP_IRQCHIP | x86 | struct kvm_lapic_state (in) | 成功返回0, 失败返回-1 | 设置虚拟机LAPIC寄存器内容 |
4.64 | KVM_NMI | KVM_CAP_USER_NMI | x86 | none | 成功返回0, 失败返回-1 | 将NMI中断加入vcpu队列 |
4.68 | KVM_SET_ONE_REG | KVM_CAP_ONE_REG | all | struct kvm_one_reg (in) | 成功返回0, 失败返回负值 | 设置vcpu某个寄存器的值 |
4.69 | KVM_GET_ONE_REG | KVM_CAP_ONE_REG | all | struct kvm_one_reg (in and out) | 成功返回0, 失败返回负值 | 获取虚拟机某个寄存器的值 |
4.70 | KVM_KVMCLOCK_CTRL | KVM_CAP_KVMCLOCK_CTRL | x86 | none | 成功返回0, 失败返回-1 | 通知内核guest已经暂停,内核会标记pvclock对应的标志位 |
4.80 | KVM_GET_DEVICE_ATTR | KVM_CAP_VCPU_ATTRIBUTES | x86 | struct kvm_device_attr | 成功返回0, 失败返回-1 | 获取设备状态(x86用于获取tsc offset) |
KVM_SET_DEVICE_ATTR | KVM_CAP_VCPU_ATTRIBUTES | x86 | struct kvm_device_attr | 成功返回0, 失败返回-1 | 设置设备状态(x86用于设置tsc offset) | |
4.81 | KVM_HAS_DEVICE_ATTR | KVM_CAP_VCPU_ATTRIBUTES | x86 | struct kvm_device_attr | 成功返回0, 失败返回-1 | 检查设备是否具有某特性(x86检查vcpu是否支持tsc offset) |
4.87 | KVM_SET_GUEST_DEBUG | KVM_CAP_SET_GUEST_DEBUG | x86, s390, ppc, arm64 | struct kvm_guest_debug (in) | 成功返回0, 失败返回-1 | 配置vcpu debug寄存器和vcpu对应的debug事件处理逻辑 |
4.96 | KVM_SMI | KVM_CAP_X86_SMM | x86 | none | 成功返回0, 失败返回-1 | 将SMI加入vcpu 队列 |
4.105 | KVM_X86_SETUP_MCE | KVM_CAP_MCE | x86 | u64 mcg_cap (in) | 成功返回0, 失败返回负值(-EFAULT/-EINVAL) | 初始化MCE功能 |
4.106 | KVM_X86_SET_MCE | KVM_CAP_MCE | x86 | struct kvm_x86_mce (in) | 成功返回0, 失败返回负值(-EFAULT/-EINVAL) | 为Guest注入MCE |
4.114 | KVM_GET_NESTED_STATE | KVM_CAP_NESTED_STATE | x86 | struct kvm_nested_state (in/out) | 成功返回0, 失败返回-1 | 获取vcpu嵌套虚拟化状态 |
4.115 | KVM_SET_NESTED_STATE | KVM_CAP_NESTED_STATE | x86 | vcpu iostl | struct kvm_nested_state (in) | 成功返回0, 失败返回-1 |
4.118 | KVM_GET_SUPPORTED_HV_CPUID | KVM_CAP_HYPERV_CPUID | x86 | struct kvm_cpuid2 (in/out) | 成功返回0, 失败返回-1 | 返回KVM模拟的与Hyper-V相关的CPUID信息 |
- 只展示了x86支持的KVM API