4. 中断虚拟化
1. 概述
1.1. 物理平台的中断架构
物理平台的中断架构:
物理平台, 外部中断流程如图5-9.
- 首先, I/O设备通过中断控制器(I/O APIC或PIC)发出中断请求,
- 中断请求经由PCI总线发送到系统总线上,
- 最后目标CPU的Local APIC部件接收中断, CPU开始处理中断.
1.2. 虚拟平台的中断架构
虚拟化环境中, VMM也需要为客户机OS展现一个与物理中断架构类似的虚拟中断架构.
虚拟机的中断架构:
和物理平台一样, 每个VCPU都对应一个虚拟Local APIC用于接收中断. 虚拟平台也包含虚拟I/O APIC或虚拟PIC用于发送中断.
和VCPU一样, 虚拟Local APIC、虚拟I/O APIC和虚拟PIC都是VMM维护的软件实体。
-
当虚拟设备需要发送中断时, 虚拟设备会调用虚拟I/O APIC的接口发送中断.
-
虚拟I/O APIC根据中断请求, 挑选出相应的虚拟Local APIC, 调用其接口发送中断请求.
-
虚拟Local APIC进一步利用VT-x的事件注入机制将中断注入到相应的VCPU.
可见, 中断虚拟化的主要任务是实现图5-10描述的虚拟中断架构, 具体包括虚拟PIC、虚拟I/O APIC和虚拟Local APIC, 并实现中断的生成、采集和注入的整个过程.
1.3. MSI
此外, PCI/PCIe设备还支持另一种中断方式MSI, MSI可以允许设备直接发送中断到Local APIC, 不需要经过中断控制器(I/O APIC).
MSI虚拟化原理和本节描述的原理类似. 感兴趣, 可以进一步参考PCI/PCIe规范中关于MSI描述, 以及IA32手册9.11节MESSAGE SIGNALLED INTERRUPTS的相关内容.
2. 虚拟PIC
PIC本质是芯片组的一个设备. 因此, 虚拟PIC的实现, 根据PIC硬件规范, 在软件上模拟出虚拟PIC, 为虚拟机提供和物理PIC一样的接口.
虚拟PIC首先要虚拟出和物理PIC一样的软件接口. PIC为软件提供了如下接口用于操作PIC.
- 4个初始化命令字(Initialization Command Words): ICW1~4, 用于初始化操作;
- 3个操作命令字(Operation Command Words): OCW1~3, 用于操作PIC.
在IA32平台上, PIC的ICW1~4和OCW1~3都是通过I/O端口访问的. 因此, VT-x下, VMM很容易就可以实现这些接口的虚拟化. 具体而言, 这些接口是通过I/O端口0x20/0x21以及0xA0/0xA1来访问, 因此, VMM可以设置VMCS的I/O bitmap中的相应位, 使得客户机为访问这些端口时发生VM-Exit, 便于VMM截获.
VMM在截获这些接口访问后, 下一步就是按PIC硬件规范对这些接口的定义, 实现相应的逻辑. 举例, 接口OCW1的功能用于操作IMR寄存器, 控制指定中断是否被屏蔽. 因此, VMM会分析客户机的OCW1命令, 判断是对哪个中断进行屏蔽或解除屏蔽, VMM继而在内部逻辑中记录指定中断是否被屏蔽. 若被屏蔽, 相应的虚拟中断就不会被提交.
此外, 虚拟PIC还要为虚拟设备提供接口用于发送中断请求. 这在物理上表现为I/O设备和PIC之间的电气连线, 在虚拟环境中由于设备和PIC都是虚拟的, 因而两者的交互表现为直接的函数调用.
虚拟PIC最终会向虚拟Local APIC提交中断, 这在物理上表现为PIC和CPU之间的电气连线. 同样, 在虚拟环境中由于设备和PIC都是虚拟的, 因而两者交互表现为直接的函数调用.
虚拟PIC接口的完整实现很复杂, VMM通常会为虚拟PIC维护一个内部的状态机来驱动虚拟PIC的行为. 虚拟PIC具体实现可参照KVM/Xen实现.
3. 虚拟I/O APIC
PIC值适用于单CPU系统, 对于多CPU, 必须通过I/O APIC来发送中断. 因此, 对于多CPU虚拟平台, 必须实现虚拟I/O APIC.
和虚拟PIC的实现类似, 虚拟I/O APIC在VMM中也是一个虚拟设备!!!. VMM根据其硬件规范来实现虚拟设备. Intel ICH系列Datasheet定义了I/O APIC的软件接口.
和PIC类似, 虚拟I/O APIC也会根据硬件规范实现相应的接口内部逻辑, 也会为虚拟设备提供接口用来发送中断请求. 虚拟I/O APIC最后也是通过虚拟Local APIC的接口来提交中断.
OS通过MMIO方式访问I/O APIC, 因此, VMM的实现和虚拟PIC有所不同. VMM会将虚拟IO APIC的MMIO地址对应的页表项置为"该页不存在", 因此, 当客户机访问相应的MMIO寄存器时, 就会发生原因为Page Fault的VM-Exit. 这样, VMM就能截获客户机对虚拟I/O APIC的访问, 进而正确地虚拟化.
虚拟I/O APIC的具体实现可参考KVM或XEN的实现.
4. 虚拟Local APIC
Local APIC是CPU上一个内部部件, 负责外部中断. 此外, 还提供了产生中断的功能, 例如, Local APIC Timer Interrupt和处理器间中断IPI. Intel手册的第9章ADVANCED PROGRAMMABLE INTERRUPT CONTROLLOR(APIC)详细定义了Local APIC的规范.
和虚拟PIC、虚拟I/O APIC一样, 虚拟Local APIC在VMM中被实现为一个模拟设备. 和I/O APIC一样, Local APIC提供给软件的接口是MMIO寄存器, 因此, VMM也通过Page Fault截获并模拟.
虚拟Local APIC的最主要功能是向VCPU注入中断. 在VT-x下, 虚拟Local APIC可借助VM-Entry事件注入机制简单实现这个功能!!!. 当然, 具体实现比较复杂.
5. 中断采集
上面介绍了虚拟机中断架构的各个组件, 下面介绍这些组件如何一起工作为客户机产生虚拟中断!!!. 这个过程包括两部分: 中断采集和中断注入.
中断采集指如何将虚拟机的设备中断请求送到虚拟中断控制器。在虚拟环境里, 客户机中断有两种可能来源.
⓵ 来自软件模拟的虚拟设备, 例如一个模拟出来的串口, 可以产生一个虚拟中断.
⓶ 来自直接分配给客户机的物理设备的中断, 例如一块物理网卡, 可产生一个真正的物理中断.
对于虚拟设备, 是软件模块. 当虚拟设备要发出中断请求时, 可通过虚拟中断控制器提供的接口函数发出中断请求, 例如使用虚拟PIC或虚拟I/O APIC提供的接口.
直接分配要复杂. 一个物理设备被直接分配给一个虚拟机, 意味当设备发生中断, 该物理中断的处理函数位于客户OS!!!中. 而在虚拟化环境中, 物理中断控制器由VMM控制(!!!这是关键, 直接分配设备也是通过物理中断控制器发中断!!!因为物理连接就是物理中断控制器!!!), 且中断发生时CPU的IDT表通常不是客户机的IDT表!!!, 因此, 物理中断需要首先由VMM的中断处理函数接收, 再注入给客户机.
下面通过例子概要介绍物理中断采集过程.
⓵ 物理设备发生中断, 假定设备IRQ号为14, 对应的中断向量号为0x41.
⓶ CPU收到中断, 执行标准中断处理流程, 例如应答PIC、中断门自动屏蔽等。最后, CPU跳转到物理机IDT表中0x41表项所指定的处理函数. 注意, 该处理函数是VMM提供的, 目的是将物理中断注入给客户机.
⓷ VMM的中断处理函数!!!对中断检查, 发现该中断是分配给客户机的设备产生的, VMM!!!调用虚拟中断控制器的接口函数!!!, 将中断发送给虚拟Local APIC. 之后, 虚拟Local APIC!!!就会在适当时候将该中断注入给客户机, 由客户OS处理函数处理.
⓸ 在将中断事件通知客户机后, VMM会进行后续处理, 例如开中断等.
上述过程中, 有两点信息是需要创建客户机时候提供.
⓵ 设备的分配信息. 第⓷步中, VMM必须了解, 中断0x41对应的设备是否分配给了虚拟机以及哪个虚拟机. 通常, 这是创建虚拟机时候由用户决定, 用户通过管理工具通知VMM相关的绑定信息.
⓶ 设备在客户机平台上的管脚信息. 第⓷步中, VMM在调用虚拟中断控制器的接口函数时, 需要提供IRQ号, 即管脚号. 需要注意的是, 虚拟中断控制器的输入管脚!!! 与 物理中断控制器的输入管脚!!! 不一定相同. 虚拟中断控制器的输入管脚是由VMM所提供的虚拟平台决定, 通常创建客户机时候确定. VMM负责两者之间的转换.
6. 中断注入
负责将虚拟中断控制器采集到的中断请求按照其优先级, 逐一注入客户机虚拟处理器. 这里有两个问题:
- 首先是如何取得需要注入的最高优先级中断的相关信息,
- 其次是如何才能将一个中断注入客户机VCPU.
第一个, 虚拟中断控制器会负责将中断按优先级排序, VMM只需要调用虚拟中断控制器提供的接口函数, 就可以获得当前最高优先级中断信息.
第二个, 虚拟Local APIC提供了将中断注入客户机VCPU最基本的功能. VMM可调用虚拟Local APIC接口实现中断注入. 这里, VMM的虚拟中断注入逻辑需要考虑下面几个问题.
⓵ 目标VCPU正在物理CPU上运行, 如何注入中断? 前面说过, 只能在VM-Entry时候将中断注入客户机!!!, 因此为保证中断的及时注入, 需要强迫VCPU发生VM-Exit, 这时就可以在VM-Entry返回客户机时候注入中断. 常用的使客户机发生VM-Exit的方法是向VCPU所在物理CPU发送IPI中断!!!.
⓶ 如果目标VCPU目前无法中断, 例如VCPU目前正处于关中断状态(客户机EFLAGS.IF为0), 如何注入中断? Intel VT-x提供了一个解决机制, 即使用中断窗口(Interrupt Windows). 该机制通过设置VMCS的一个特定字段, 告诉物理CPU, 其当前运行的客户机VCPU有一个中断需要注入. 一旦客户机VCPU开始可以接受中断, 例如进入开中断状态, 物理CPU会主动触发VM-Exit, 从客户机陷入到VMM中, 虚拟中断注入模块就可以注入等待的中断了.
⓷ 什么时候触发中断注入? 通常方法是, 当中断采集逻辑调用虚拟中断控制器接口请求发出中断后, 虚拟中断控制器会根据内部状态, 例如虚拟IMR/ISR寄存器的值, 来决定是否需要注入中断给客户机. 其判断过程和物理中断控制器判断是否提交中断给CPU一样.
图5-11给出中断注入逻辑过程
7. 案例分析
举两个例子论述虚拟中断从产生到注入的全过程
7.1. 简单例子
物理平台上可编程时钟PIT会定期产生时钟中断. VMM模拟的虚拟PIT也一样, 可以定时为客户机产生时钟中断.
⓵ 虚拟PIT产生中断请求时, 调用虚拟中断控制器提供的接口通知虚拟PIC/IOAPIC, 自己有一个中断.
⓶ 虚拟PIC/IOAPIC记录这个中断请求, 并检查内部寄存器, 如IMR、IRR和ISR等, 以决定是否需要将中断注入到客户机. 如果不是, 将这个中断请求保存在虚拟PIC/IOAPIC的内部逻辑中. 当虚拟PIC内部状态改变后(通常是客户机写PIC/IOAPIC的寄存器时), 虚拟PIC/IOAPIC会检查内部是否有等待处理的中断请求并重复这个过程.
⓷ 如果此时需要注入中断, 调用中断注入逻辑
⓸ VMM检查客户机VCPU是否正在运行, 如果是, 则发一个IPI强制其进入VMM上下文!!!.
⓹ 在客户机VCPU再运行前, VMM会检查!!!发现该VCPU有中断需要注入, 接着VMM会检查当前客户机VCPU是否能够被注入中断. 如果能, 使用虚拟中断控制器提供的接口, 获取最高优先级中断的信息, 设置好VMCS中的相应字段, 使当VCPU投入运行时自动去执行相应矢量号的中断处理函数. 否则, 设置中断窗口, 等待下次VM-Exit之后再注入.
7.2. 复杂例子
被直接分配给虚拟机的物理设备发生中断时, VMM需要将该中断注入给对应的客户机.
假定物理设备产生中断, 设备中断管脚连接到IOAPIC的管脚0x12, 对应中断重定向的矢量号为0x41, 并假定系统使用物理APIC, 客户机使用虚拟APIC. 同时, 物理设备中断发生时, 物理CPU的中断是开启的.
图5-12给出该例子的流程.
⓵ 物理设备发生中断, 将中断发送给物理I/O APIC的管脚0x12.
⓶ 物理I/O APIC收到后, 将管脚0x12转化为中断向量0x41发送到Local APIC.
⓷ Local APIC将中断0x41注入到CPU, CPU跳转到IDT表中0x41表项指定的处理函数. 同时, Local APIC的ISR寄存器对应0x41的位被置1, 后面的等于或低于0x41的中断被屏蔽.
⓸ VMM相关中断处理函数被执行.
⓹ 中断处理函数检查发现这个中断是属于客户机的设备产生的中断, 故调用虚拟中断控制器!!!的接口函数. 在将中断事件注入客户机以后, VMM通过置一物理IOAPIC第0x12个RTE的屏蔽位!!!, 屏蔽后续的物理中断. VMM向物理Local APIC写入EOI, 以清掉Local APIC的ISR寄存器0x41位, 从而其它中断也可以被接受.
VMM处理函数对物理APIC的操作到此结束, 下面是虚拟APIC工作的流程.
⓵ 虚拟的IOAPIC中断控制器调用虚拟的Local APIC的接口函数, 并将虚拟IOAPIC中相关矢量号传入. 虚拟Local APIC的ISR相关bit位被置一.
⓶ 虚拟的Local APIC通过中断注入逻辑模块将中断注入到客户机. 中断注入逻辑模块, 见前面.
⓷ 客户机执行相关中断处理函数
⓸ 客户机中断处理函数处理物理设备的中断
⓹ 客户机向虚拟Local APIC写入EOI, EOI操作被VMM截获!!!. 虚拟Local APIC的ISR位被清掉, 同时, 虚拟Local APIC通知VMM客户已经完成中断0x21的处理, VMM清除物理IOAPIC第0x12个RTE的屏蔽位.