4. 中断虚拟化

1. 概述

1.1. 物理平台的中断架构

物理平台的中断架构:

4. 中断虚拟化

物理平台, 外部中断流程如图5-9.

  • 首先, I/O设备通过中断控制器(I/O APIC或PIC)发出中断请求,
  • 中断请求经由PCI总线发送到系统总线上,
  • 最后目标CPU的Local APIC部件接收中断, CPU开始处理中断.

1.2. 虚拟平台的中断架构

虚拟化环境中, VMM也需要为客户机OS展现一个与物理中断架构类似的虚拟中断架构.

虚拟机的中断架构:

4. 中断虚拟化

和物理平台一样, 每个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 APICVMM中也是一个虚拟设备!!!. 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给出中断注入逻辑过程

4. 中断虚拟化

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的中断是开启的.

4. 中断虚拟化

图5-12给出该例子的流程.

物理设备发生中断, 将中断发送给物理I/O APIC的管脚0x12.

物理I/O APIC收到后, 将管脚0x12转化为中断向量0x41发送到Local APIC.

Local APIC中断0x41注入到CPU, CPU跳转到IDT表0x41表项指定的处理函数. 同时, Local APICISR寄存器对应0x41的位被置1, 后面的等于或低于0x41的中断被屏蔽.

VMM相关中断处理函数被执行.

中断处理函数检查发现这个中断是属于客户机的设备产生的中断, 故调用虚拟中断控制器!!!的接口函数. 在将中断事件注入客户机以后, VMM通过置一物理IOAPIC第0x12个RTE的屏蔽位!!!, 屏蔽后续的物理中断. VMM向物理Local APIC写入EOI, 以清掉Local APIC的ISR寄存器0x41位, 从而其它中断也可以被接受.

VMM处理函数对物理APIC的操作到此结束, 下面是虚拟APIC工作的流程.

虚拟的IOAPIC中断控制器调用虚拟的Local APIC的接口函数, 并将虚拟IOAPIC相关矢量号传入. 虚拟Local APICISR相关bit位被置一.

虚拟的Local APIC通过中断注入逻辑模块中断注入到客户机. 中断注入逻辑模块, 见前面.

客户机执行相关中断处理函数

⓸ 客户机中断处理函数处理物理设备的中断

⓹ 客户机向虚拟Local APIC写入EOI, EOI操作被VMM截获!!!. 虚拟Local APIC的ISR位被清掉, 同时, 虚拟Local APIC通知VMM客户已经完成中断0x21的处理, VMM清除物理IOAPIC第0x12个RTE的屏蔽位.