前言

从Windows 7开始,微软为了使Windows操作系统能够在自研的Hyper-V平台得到更好性能,在Windows操作系统内嵌了许多半虚拟化接口,这些半虚拟化接口通过TLFS规范对外公开。假设其他虚拟化平台(KVM、Xen、VMware)实现了和Hyper-V一样的接口暴露给Guest,那么Windows内部的半虚拟化特性也会被激活,此时这些虚拟化平台被称为Hyper-V兼容平台。KVM就是一个Hyper-V兼容平台,KVM提供了部分关键的兼容接口,本文将详细描述这些接口。

CPUID

在使用Hyper-V半虚拟化接口之前,GuestOS应该先探测:

运行在物理机上还是运行在虚拟机上?
运行在微软的Hyper-V还是开源的QEMU/KVM还是VMware的vSphere?
当前的Hypervisor有提供哪个功能和特性?
Guest通过CPUID指令实现这个探测的过程,基本过程如下:

执行指令#cpuid 0x00000001,指令返回后,通用寄存器EAX、EBX、EXC、EDX存放返回值,如果EAX的bit31位为0 表示运行在物理机平台,如果EAX的bit31位为1 表示运行在虚拟化平台。对于虚拟化平台,这意味着GuestOS能继续使用CPUID指令获取虚拟化特性,X86规定0x40000000~0x400000FF共256个LEAF(参数)专用于虚拟化,所有虚拟化平台必须实现0x40000000和0x40000001两个LEAF,其他254个可选择性提供,当CPU执行#cpuid 0x40000000~0x400000FF时,物理CPU不认识这个参数引发#GP异常,陷入Hyper-visor处理。
如果是虚拟化平台,继续执行指令#cpuid 0x40000000,返回EAX = 0x40000005,返回EBX|ECX|EDX = "Microsoft Hv"。其中EAX值表示GuestOS能传递给cpuid指令的最大LEAF值,如果GuestOS发起#cpuid 0x40000006将引发#GP异常,EBX|ECX|EDX三个寄存器用于存放Hypervisor签名。规范要求这个签名仅用于展示作用, 但是很多程序仍然使用它来做控制判断。
如果存在0x40000001,执行指令#cpuid 0x40000001,返回EAX = "Hv#1"。其中EAX值为接口ID,目前固定为"Hv#1",是它决定了如何解释0x40000002~0x400000FF。各个位而不是签名。目前接口ID只有一种"Hv#1"。
如果接口ID为"Hv#1",执行指令#cpuid 0x40000003。指令返回后EAX和EBX存放了当前运行的Hyper-V兼容平台支持的特性,通过位表示。

* 如下EAX暴露的特性均通过MSRs实现(这里先不关心何为MSRs以及各位语义)

+ EAX-bit-00: AccessVpRunTimeReg
+ EAX-bit-01: AccessPartitionReferenceCounter
+ EAX-bit-02: AccessSynicRegs
+ EAX-bit-03: AccessSyntheticTimerRegs
+ EAX-bit-04: AccessIntrCtrlRegs
+ EAX-bit-05: AccessHypercallMsrs
+ EAX-bit-06: AccessVpIndex
+ EAX-bit-07: AccessResetReg
+ EAX-bit-08: AccessStatsReg
+ EAX-bit-09: AccessPartitionReferenceTsc
+ EAX-bit-10: AccessFrequencyRegs
+ EAX-bit-11: AccessDebugRegs
+ EAX-bit-12: AccessReenlightenmentControls
+ EAX-other-bits(13~31): Reserved

* 如下EBX暴露的特性均通过Hypercall实现(这里先不关心何为Hypercall以及各位语义)

+ EBX-bit-00: CreatePartitions
+ EBX-bit-01: AccessPartitionId
+ EBX-bit-02: AccessMemoryPool
+ EBX-bit-04: PostMessages
+ EBX-bit-05: SignalEvents
+ EBX-bit-06: CreatePort
+ EBX-bit-07: ConnectPort
+ EBX-bit-08: AccessStats
+ EBX-bit-11: Debugging
+ EBX-bit-12: CpuManagement
+ EBX-bit-16: AccessVSM
+ EBX-bit-17: AccessVpRegisters
+ EBX-bit-20: EnableExtendedHypercalls
+ EBX-bit-21: StartVirtualProcessor
+ EBX-other-bits(3,9-10,13-15,18-19,22~31): Reserved

最后,如果存在0x40000004,执行指令#cpuid 0x40000004,指令#cpuid 0x40000003能获取到Hypervisor支持的所有半虚拟化特性,Guest使用这些特性是否能提升性能,这取决于Hypervisor实现,不意味着半虚拟化性能一定很好。为此,需要GuestOS执行指令#cpuid 0x40000004 返回的EAX、EBX、ECX、EDX值各个位是Hypervisor推荐(或不推荐)GuestOS使用哪些特性。

Hypercall

在开始hypercall之前,需要介绍一下syscall(系统调用)实现,例

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

如上是系统调用read的声明,当用户态程序尝试调用read时,大概会执行如下指令:

配置寄存器EAX=3,3为read的编号,每个syscall都有一个唯一的编号,如下:

#cat /usr/include/asm/unistd_32.h
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
...

配置寄存器EBX=fd      参数1
配置寄存器ECX=buf     参数2
配置寄存器EDX=count   参数3
执行陷阱指令#int 0x80,触发中断,CPU特权级别改变,陷入内核态的中断回调函数处理,内核通过寄存器EAX值获取请求号, 然后调用相应的内核处理函数。
指令返回后,系统调用函数返回值存放在寄存器EAX。
用户态发起syscall请求,调用陷阱指令(i386为int指令)陷入内核态执行syscall,CPU特权级别变更,每个系统调用函数都有一个唯一的ID,内核态通过这个ID区别不通的系统调用请求。linux提供了大约300个系统调用。

hypercall和syscall原理上没有什么两样,hypercall也可以视为函数,pv-guest发起hypercall请求时把hypercall编号和参数写到通用寄存器,然后触调用陷阱指令(x86为vmcall指令)陷入ROOT模式,处于ROOT模式的hypervior处理相应的hypercall请求。hypercall能大大降低vmexit次数,例:SMP处理器某个CPU有时需要给一组CPU发送IPI中断。引入hypercall前,guest通过I/O端口配置LAPIC,期间会读写几十次I/O port,每次读写都会触发vmexit。引入hypercall后,hypervisor类似声明了如下函数供guest使用:

int hc_send_ipi((unsigned long)cpu_mask);

pv-guest调用此hypercall只触发一次vmexit(vmcall引发的),每个Hypervisor提供的hypercall均不相同,各个Hypervisor提供的hypercall如下:

标准Hyper-V(约提供200个hypercall调用):
编号                   Hypercall
0x0001              HvSwitchVirtualAddressSpace()
0x0002              HvFlushVirtualAddressSpace()
0x0003              HvFlushVirtualAddressList()
...(具体参数和返回值见TLFS v6.0规范Appendix A: Hypercall Code Reference)KVM(只提供8个hypercall调用):
编号                   Hypercall
0x0001              KVM_HC_VAPIC_POLL_IRQ()
0x0002              KVM_HC_MMU_OP()
0x0003              KVM_HC_FEATURES()
0x0004              KVM_HC_PPC_MAP_MAGIC_PAGE()
0x0005              KVM_HC_KICK_CPU()
0x0006              KVM_HC_CLOCK_PAIRING()
0x0007              KVM_HC_SEND_IPI()
0x0008              KVM_HC_SCHED_YIELD()
(具体参数和返回值参考# cat Documentation/virtual/kvm/hypercalls.txt)

需要注意的是,GuestOS可能运行在不同类型的Hypervisor上,不能假定所有Hypervisor都有提供如上Hypercall接口,因此新增的Hypercall接口需要通过cpuid特性暴露给Guest使用。

MSRs

操作系统可以通过rdmsr/wrmsr两个处理器指令读或写目标MSR寄存器的值。MSR寄存器可以分为三类:

- Architectural MSRs(处理器架构定义,例如X86)MSR-Number       MSR-Name 0x010                   X64_MSR_TIME_STAMP_COUNTER0x01B                   X64_MSR_APIC_BASE0x0FE                   X64_MSR_MTRRCAP...(详细参考X86 CPU手册)
- Vendor-Specific MSRs(处理器厂商自定义,例如AMD或INTEL)MSR-Number       MSR-Name 0xC0010010       AMD_MSR_SYSCFG0xC001001F       AMD_MSR_NB_CFG...(以上AMD CPU提供, 详细参考AMD X86 CPU手册)0x006                   INTEL_MSR_MONITOR_FILTER_SIZE0x017                   INTEL_MSR_PLATFORM_ID...(以上INTEL CPU提供, 详细参考INTEL X86 CPU手册)
- Hypervisor Synthetic MSRs(Hypervisor定义)MSR-Number       MSR-Name 0x40000000       HV_X64_MSR_GUEST_OS_ID0x40000001       HV_X64_MSR_HYPERCALL0x40000002       HV_X64_MSR_VP_INDEX0x40000003       HV_X64_MSR_RESET0x40000010       HV_X64_MSR_VP_RUNTIME0x40000020       HV_X64_MSR_TIME_REF_COUNT...(以上Hyper-V提供, 数量较多详细参考HYPER-V TLFS v4.0手册)

guest执行指令#rdmsr HV_X64_MSR_RESET,如果目标MSR属于CPU内部提供,那么将返回物理MSR寄存器的值,如果目标MSR不属于CPU内部提供,将导致#GP异常,陷入ROOT模式由Hypervisor处理后返回。需要注意的是,GuestOS可能运行在不同类型的Hypervisor上,不能假定所有Hypervisor都有提供目标MSR,因此新增的MSR需要通过cpuid特性暴露给Guest使用。

注意:MSRs和CPUID区别:CPUID是单向的,只适用于从底层获取信息,无法用CPUID把信息传递给Hypervisor,而MSR是有提供rdmsr和wrmsr指令,Guest可以把少量信息写入MSR,传递给Hypervisor。

QEMU/KVM实现

hv-relaxed

用例: #qemu -cpu host,hv-relaxed ...

是否配置hv-relaxed决定Guest执行#cpuid 0x40000004返回的寄存器EAX中的bit5。#cpuid 0x40000004返回值各个位是Hypervisor推荐GuestOS使用哪些半虚拟化功能bit5表示,Hypervisor建议GuestOS关闭中断和看门狗定时器超时机制,在虚拟化环境,因vCPU可能被抢占,因此很有可能出现超时,导致windows蓝屏,并非硬件异常了。配置hv-relaxed后,vCPU被长时间抢占不会导致WindowsOS蓝屏,建议所有Windows虚机都打开。

hv-vapic

用例: #qemu -cpu host,hv-vapic ...

KVM提供了如下三个MSR(半虚拟化的APIC),用于pv-guest一次性提交中断请求或中断应答:

HV_X64_MSR_EOI
HV_X64_MSR_ICR
HV_X64_MSR_TPR

是否配置hv-vapic决定Guest执行#cpuid 0x40000003返回的寄存器EAX中的bit4,同时也决定Guest执行#cpuid 0x40000004返回的寄存器EAX的bit3。#cpuid 0x40000003返回值各个位表示Hypervisor具备哪些特性。#cpuid 0x40000004返回值各个位表示Hypervisor推荐Guest使用哪些特性。EAX.bit4(HV_SYNTIMERS_AVAILABLE)为1时指示Guest当前Hypervisor有提供如上三个MSRs。guest可以通过rdmsr/wrmsr指令直接提交中断请求或中断应答,相比全模拟的APCI,HV-VAPIC大量减少vmexit。倘若EAX.bit4(HV_SYNTIMERS_AVAILABLE)为0,Guest执行#wrmsr HV_X64_MSR_EOI将引发异常。

hv-spinlocks

用例:    #qemu -cpu host,hv-spinlocks=0x10000 ...

GuestOS执行spinlock期间,其实是可以转让CPU给其他vCPU调度的。短时间的spinlock可以节省vCPU调度开销,长时间的spinlock会浪费CPU资源。为此,参数用于让guest重试"hv-spinlocks=number"次无果后通告hypervisor,主动转让CPU。

hv-spinlocks=0  表示不尝试(一旦guest调用spinlock,立刻退出到hypervisor转让CPU)
hv-spinlocks=0xFFFFFFFF(x86虚机缺省值)任其guest一直执行spinlock。
配置hv-spinlocks决定#cpuid 0x40000004返回后的整个EBX寄存器值。

hv-vpindex

用例: #qemu -cpu host,hv-vpindex ...

Virtual-Processor的编号其实就是LAPIC的编号,编号ID在一台机器内唯一。完全虚拟化环境,程序要获取当前运行CPU的编号,需配置LAPIC寄存器,引发多个vmexit。为此,KVM实现了如下MSR

HV_X64_MSR_VP_INDEX    
pv-guest只需要调用#rdmsr HV_X64_MSR_VP_INDEX,一次vmexit就能获取到VP_INDEX。配置hv-vpindex决定Guest执行#cpuid 0x40000003返回的寄存器EAX中的bit6,bit6(HV_VP_INDEX_AVAILABLE)表示运行环境的Hypervisor有提供HV_X64_MSR_VP_INDEX供Guest用。

hv-runtime 

用例: #qemu -cpu host,hv-runtime ...

KVM实现了如下MSR,存放vCPU实际运行时间(单位100ns),这可以让GuestOS知道被'stolen'(被抢占时间,#top可以查看)的时间。

HV_X64_MSR_VP_RUNTIME
配置hv-vapic决定Guest执行#cpuid 0x40000003返回的寄存器EAX中的bit0,bit0(HV_VP_RUNTIME_AVAILABLE)表示运行环境的Hypervisor有提供HV_X64_MSR_VP_RUNTIME供Guest用。

hv-crash

用例: #qemu -cpu host,hv-crash ...

KVM实现了如下MSR,当Guest发生CRASH时,会将crash信息写入到上面MSR寄存器,QEMU日志会记录这些信息。

注:写入CRASH信息后会触发Windows关机,此时windows不会再有crashdump生成。配置hv-crash决定Guest执行#cpuid 0x40000003返回的寄存器EAX中的bit10,bit10表示运行环境的Hypervisor有提供如上MSRs供Guest用。

- HV_X64_MSR_CRASH_P0- HV_X64_MSR_CRASH_P1- HV_X64_MSR_CRASH_P2- HV_X64_MSR_CRASH_P3- HV_X64_MSR_CRASH_P4- HV_X64_MSR_CRASH_P5- HV_X64_MSR_CRASH_CTL

hv-synic 

用例: #qemu -cpu host,hv-synic ...

KVM实现了如下MSR,Synthetic-interrupt-controller(SynIC,是LAPIC的功能扩展)SynIC是一个半虚拟化中断控制器提供向Guest发送中断机制(VMBus Message),guest通过如下MSR接口控制,VMBus-devices和Hyper-V-synthetic-timers依赖此特性,QEMU目前尚未有VMBus-devices设备。

- HV_X64_MSR_SCONTROL..HV_X64_MSR_EOM (0x40000080..0x40000084)
    - HV_X64_MSR_SINT0..HV_X64_MSR_SINT15 (0x40000090..0x4000009F)
配置hv-time决定Guest执行#cpuid 0x40000003返回的寄存器EAX中的bit2,bit2表示运行环境的Hypervisor有提供如上MSRs供Guest用。

hv-stimer

用例: #qemu -cpu host,hv-stimer ...

KVM实现了如下MSR为每个vCPU提供四路独立的定时器,通过MSR提供给Guest:不使用此半虚拟化时钟的windows CPU在空闲时也消耗大量的CPU资源,因为其他非半虚拟化的时钟都会产生周期性的中断,进而导致频繁的vm-exit。

HV_X64_MSR_STIMER0_CONFIG..HV_X64_MSR_STIMER3_COUNT(0x400000B0..0x400000B7)
配置hv-stimer决定Guest执行#cpuid 0x40000003返回的寄存器EAX中的bit3,bit3(HV_SYNTIMERS_AVAILABLE)表示运行环境的Hypervisor有提供如上MSRs供Guest用。

hv-tlbflush

用例: #qemu -cpu host,hv-tlbflush ...

TLB(translation lookaside buffer)缓存:

完整的虚拟地址和物理地址映射信息存放在页表(内存)上,TLB缓存了最频繁用的部分信息。当一个CPU变更了一条virtual--physical-mapping时,需要通过IPI(x86)中断让其他CPU执行TLBflush(清空)这个过程被称为“TLB shoot-down”。对于虚机,目标vCPU可能都还没被调度等,因此hypervisor可以为其实现“TLB shoot-down”优化配置此参数后,会推荐Guest使用Hypercall的方式实现TLBFlush而是不是IPI中断。配置hv-tlbflush决定Guest执行#cpuid 0x40000004返回的寄存器EAX中的bit2,bit2(HV_REMOTE_TLB_FLUSH_RECOMMENDED)推荐Guest使用hyper_call_tlbflush()半虚拟化接口。

hv-ipi

用例: #qemu -cpu host,hv-ipi ...

IPI表示由一个处理器发通告给一组处理器(可能包含自身)。

完全虚拟化IPI通过写APIC寄存器(虽然是KVM模拟的,但是对guest是完全虚拟化)pv-guest可以很轻易的把IPI描述到MSR上,然后通知hypervisor处理。激活该配置后,推荐Guest使用半虚拟化hypercall来实现IPI。配置hv-tlbflush决定Guest执行#cpuid 0x40000004返回的寄存器EAX中的bit10,bit10(HV_CLUSTER_IPI_RECOMMENDED)推荐Guest使用hyper_call_send_ipi()半虚拟化接口。

hv-vendor-id

用例:    #qemu -cpu host,hv-vendor-id="KVMKVMKVM" ...

guest执行 #cpuid 0x40000000,后可能返回:

- EAX|EBX|ECX|EDX = "Microsoft Hv"
    - EAX|EBX|ECX|EDX = "KVMKVMKVM"
QEMU如果有配置任何hv特性,那么返回"Microsoft Hv",此配置用于配置Hypervisor签名。

hv-reset

用例: #qemu -cpu host,hv-reset ...

KVM实现了如下MSR,Guest可通过写HV_X64_MSR_RESET来执行RESET操作。

- HV_X64_MSR_RESET
    不推荐使用!!!,即使在#cpuid 0x40000004中推荐Guest用,windows也可能不使用。

hv-frequencies

用例: #qemu -cpu host,hv-frequencies ...

KVM-Hyper-V提供了如下MSR,Guest无需测量计算可直接从MSR中读取TSC/APCI频率:

- HV_X64_MSR_TSC_FREQUENCY 用于提供TSC频率
    - HV_X64_MSR_APIC_FREQUENCY 用于APIC频率
对于物理机,在使用TSC时钟前需要通过rdtsc指令获取TSC中的当前值,通过两次计算算出具体频率。激活后,TSC频率将由KVM显式告诉Guest TSC和APIC时钟频率,ACPI频率计算也是类似的。

​​​​​​​

KVM中的Hyper-V接口相关推荐

  1. w7虚拟机服务器管理器,Hyper - V (五) 在Win7中安装Hyper - V 管理工具远程操作虚拟机...

    在Win7中安装Hyper - V 管理工具远程操作虚拟机 由于在Hyper - V 中安装的虚拟机运行时鼠标会出现延迟现象,所以我们可以在客户机Win 7 上安装虚拟机. 首先从microsoft ...

  2. WIN10安装Hyper V

    WIN10安装Hyper V 正常情况: Hyper-V是微软提出的一种系统管理程序虚拟化技术,能够实现桌面虚拟化. 正常情况下直接在控制面版->程序->程序和功能->启用和关闭Wi ...

  3. hpgen8服务器修改电源模式,用HP GEN8+WIN2012+Hyper V+黑群晖5.2组建家庭NAS中心 篇二:HP GEN8硬件改造...

    用HP GEN8+WIN2012+Hyper V+黑群晖5.2组建家庭NAS中心 篇二:HP GEN8硬件改造 2017-11-19 15:55:35 127点赞 945收藏 205评论 追加修改(2 ...

  4. hyper v虚拟机启动黑屏怎么办?

    最近有用户打开VMware虚拟机却出现了开机一直黑屏的情况,挂起时能够看到显示,但是开机就黑屏.不知道该如何解决,小编为你带来hyper v虚拟机启动黑屏的解决方法,希望对你有帮助. 具体解决方法: ...

  5. 理解java中的两种接口

    在java 中我们常常提高接口一词.在java 中有两中接口. 第一种接口:就是指系统对外提供的所有服务,在对象中表现为public类型的方法的声明.也就是我们常常在一个类中写的public的方法了. ...

  6. KVM中ioeventfd创建与触发的大致流程(十四)

    在使用virtio-blk的情况时,virtio notify使用的ioeventfd机制,原因是为了提高性能,能够较快速的回到guest中运行.具体是如何建立这个ioeventfd的呢?流程理出来了 ...

  7. 【Groovy】Groovy 方法调用 ( Java 中函数参数是接口类型 | 函数参数是接口类型 可以 直接传递闭包 )

    文章目录 一.Java 中函数参数是接口类型 二.函数参数是接口类型 可以 直接传递闭包 三.完整代码示例 一.Java 中函数参数是接口类型 在 Android 中经常使用如下形式的接口 : 定义一 ...

  8. java配置接口提供给vue,vue在js中配置全局API接口

    在src文件夹中新建util文件夹,然后在新建一个globalAPI.js文件. 在js中配置后端的接口数据 const http = 'http://127.0.0.1:8989' const gl ...

  9. 03-JDBC学习手册:JDBC中几个重要接口和异常处理

    一.JDBC中几个重要接口 1  Statement --- SQL  语句执行接口 Statement 接口代表了一个数据库的状态,在向数据库发送相应的 SQL 语句 时,都需要创建 Stateme ...

  10. 深入理解Java中的抽象类和接口

    对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的时候会以为它们可以随意互换使用, ...

最新文章

  1. 小学三年级上册计算机计划书,小学三年级班主任工作计划书
  2. kvm cpu的亲和性绑定配置
  3. git 创建分支,更改并提交
  4. python语言必背代码-Python入门必须知道的11个知识点
  5. hdu1395 2^x mod n = 1
  6. GetHashCode() 的研究
  7. 《iOS应用逆向工程(第2版)》高清电子书 PDF
  8. 异常处理、socke基于TCP协议编程
  9. Objective-C 日记③ 字符串
  10. matplotlib-快速学习折线图-柱状图2个-饼图-0225
  11. Dw序号列表如何通过html语言加,使用DW软件实现html编码转换的详细步骤
  12. mysql中清空数据库,并重置主键为1
  13. 8.1 Ext JS应用测试概览
  14. hadoop作业引用第三方jar文件原理解析
  15. (转)Rust: Rust的 Deref 运算符
  16. 如何查询硬盘序列号,百度的答案全是错的
  17. 利用C#实现的外挂式甲骨文拼音输入法
  18. 猫眼电影TOP100爬虫
  19. 五个最适合做博客的开源系统 开源免费大量精美模板使用!
  20. ruoyi框架分页总条数total返回错误解决方案

热门文章

  1. Virtualbox+Vagrant搭建linux虚拟机并搭建easySwoole框架
  2. 刚在港交所介绍上市,蔚来汽车又要在新交所第二上市
  3. getapp.php,getApp.php
  4. 35、公众号(订阅号)消息列表
  5. 优化之Aggregator组件
  6. 2020新版CAD都有些啥功能?
  7. Three.js 使用UV贴图制作地面
  8. H264VideoToolBox硬件解码
  9. 保安镇举办“文明叶县、欢乐中原”的农民文化活动
  10. Could not load file or assembly ‘Microsoft.windows.design.extensibility,version=4.3.1.0,Culture=neut