虚拟 Local APIC

本节描述了虚拟 Local APIC 的实现。

全虚拟化

寄存器虚拟化:

Local APIC 的寄存器通过内存映射(MMIO)访问。虚拟机对 APIC 寄存器的读写会触发 VM-exit,由虚拟化层(如 VMM/Hypervisor)模拟这些操作,维护每个虚拟 CPU(vCPU)的虚拟寄存器状态。

中断注入:

当物理中断需要传递给虚拟机时,虚拟化层将其转换为虚拟中断(如虚拟 IRQ),并通过修改虚拟 APIC 的状态(如 IRR/ISR 寄存器)或直接注入中断(如 Intel 的 vmcs VM_ENTRY_INTR_INFO)通知虚拟机。

定时器虚拟化:

虚拟 APIC 定时器需根据虚拟机的配置(如周期和计数)模拟中断。Hypervisor 可能使用物理定时器(如 host 的 hrtimer)或时间偏移技术来触发虚拟中断。

硬件辅助虚拟化

现代 CPU(如 Intel VT-x 和 AMD-V)提供了硬件加速特性,显著优化性能:

APICv(Intel) / AVIC(AMD):

硬件直接支持虚拟 APIC 状态维护,减少 VM-exit。例如:

  • Virtual APIC Page:在 VMCS 中维护虚拟 APIC 的寄存器,允许虚拟机直接访问,无需陷入。

  • 中断投递优化:硬件自动将中断路由到目标 vCPU 的虚拟 APIC。

  • 自动处理 EOI:某些中断的确认(EOI)由硬件处理,避免 VM-exit。

Posted Interrupts(Intel):

  • 物理中断可直接“投递”到虚拟机的虚拟 APIC,绕过 Hypervisor 干预,极大降低延迟。

具体实现

代码位于 x86-vlapic

EmulatedLocalApic 实现了虚拟中断的基本方法,通过 handle_read handle_write 实现读写虚拟中断寄存器的功能。

VirtualApicRegs 包含了 APIC 所有寄存器,保存客户机虚拟中断的寄存器状态

Local APIC 寄存器

本地APIC寄存器被内存映射到MP/MADT表中可找到的地址。若使用分页,请确保将这些寄存器映射到虚拟内存。每个寄存器均为32位长,并期望以32位整数形式进行读写。尽管每个寄存器占用4个字节,但它们都按16字节边界对齐。 本地APIC寄存器列表(待办事项:为所有寄存器添加描述):

#![allow(unused)]
fn main() {
register_structs! {
    #[allow(non_snake_case)]
    pub LocalAPICRegs {
        (0x00 => _reserved0),
        /// Local APIC ID register (VID): the 32-bit field located at offset 000H on the virtual-APIC page.
        (0x20 => pub ID: ReadWrite<u32>),
        (0x24 => _reserved1),
        /// Local APIC Version register (VVER): the 32-bit field located at offset 030H on the virtual-APIC page.
        (0x30 => pub VERSION: ReadOnly<u32>),
        (0x34 => _reserved2),
        /// Virtual task-priority register (VTPR): the 32-bit field located at offset 080H on the virtual-APIC page.
        (0x80 => pub TPR: ReadWrite<u32>),
        (0x84 => _reserved3),
        /// Virtual APIC-priority register (VAPR): the 32-bit field located at offset 090H on the virtual-APIC page.
        (0x90 => pub APR: ReadOnly<u32>),
        (0x94 => _reserved4),
        /// Virtual processor-priority register (VPPR): the 32-bit field located at offset 0A0H on the virtual-APIC page.
        (0xA0 => pub PPR: ReadOnly<u32>),
        (0xA4 => _reserved5),
        /// Virtual end-of-interrupt register (VEOI): the 32-bit field located at offset 0B0H on the virtual-APIC page.
        (0xB0 => pub EOI: WriteOnly<u32>),
        (0xB4 => _reserved6),
        /// Virtual Remote Read Register (RRD): the 32-bit field located at offset 0C0H on the virtual-APIC page.
        (0xC0 => pub RRD: ReadOnly<u32>),
        (0xC4 => _reserved7),
        /// Virtual Logical Destination Register (LDR): the 32-bit field located at offset 0D0H on the virtual-APIC page.
        (0xD0 => pub LDR: ReadWrite<u32>),
        (0xD4 => _reserved8),
        /// Virtual Destination Format Register (DFR): the 32-bit field located at offset 0E0H on the virtual-APIC page.
        (0xE0 => pub DFR: ReadWrite<u32>),
        (0xE4 => _reserved9),
        /// Virtual Spurious Interrupt Vector Register (SVR): the 32-bit field located at offset 0F0H on the virtual-APIC page.
        (0xF0 => pub SVR: SpuriousInterruptVectorRegisterMmio),
        (0xF4 => _reserved10),
        /// Virtual interrupt-service register (VISR):
        /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets
        /// 100H, 110H, 120H, 130H, 140H, 150H, 160H, and 170H on the virtual-APIC page.
        (0x100 => pub ISR: [ReadOnly<u128>; 8]),
        /// Virtual trigger-mode register (VTMR):
        /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets
        /// 180H, 190H, 1A0H, 1B0H, 1C0H, 1D0H, 1E0H, and 1F0H on the virtual-APIC page.
        (0x180 => pub TMR: [ReadOnly<u128>; 8]),
        /// Virtual interrupt-request register (VIRR):
        /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets
        /// 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H on the virtual-APIC page.
        /// Bit x of the VIRR is at bit position (x & 1FH) at offset (200H | ((x & E0H) » 1)).
        /// The processor uses only the low 4 bytes of each of the 16-Byte fields at offsets 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H.
        (0x200 => pub IRR: [ReadOnly<u128>; 8]),
        /// Virtual error-status register (VESR): the 32-bit field located at offset 280H on the virtual-APIC page.
        (0x280 => pub ESR: ReadWrite<u32>),
        (0x284 => _reserved11),
        /// Virtual LVT Corrected Machine Check Interrupt (CMCI) Register
        (0x2F0 => pub LVT_CMCI: LvtCmciRegisterMmio),
        (0x2F4 => _reserved12),
        /// Virtual Interrupt Command Register (ICR): the 64-bit field located at offset 300H on the virtual-APIC page.
        (0x300 => pub ICR_LO: ReadWrite<u32>),
        (0x304 => _reserved13),
        (0x310 => pub ICR_HI: ReadWrite<u32>),
        (0x314 => _reserved14),
        /// Virtual LVT Timer Register: the 32-bit field located at offset 320H on the virtual-APIC page.
        (0x320 => pub LVT_TIMER: LvtTimerRegisterMmio),
        (0x324 => _reserved15),
        /// Virtual LVT Thermal Sensor register: the 32-bit field located at offset 330H on the virtual-APIC page.
        (0x330 => pub LVT_THERMAL: LvtThermalMonitorRegisterMmio),
        (0x334 => _reserved16),
        /// Virtual LVT Performance Monitoring Counters register: the 32-bit field located at offset 340H on the virtual-APIC page.
        (0x340 => pub LVT_PMI: LvtPerformanceCounterRegisterMmio),
        (0x344 => _reserved17),
        /// Virtual LVT LINT0 register: the 32-bit field located at offset 350H on the virtual-APIC page.
        (0x350 => pub LVT_LINT0: LvtLint0RegisterMmio),
        (0x354 => _reserved18),
        /// Virtual LVT LINT1 register: the 32-bit field located at offset 360H on the virtual-APIC page.
        (0x360 => pub LVT_LINT1: LvtLint1RegisterMmio),
        (0x364 => _reserved19),
        /// Virtual LVT Error register: the 32-bit field located at offset 370H on the virtual-APIC page.
        (0x370 => pub LVT_ERROR: LvtErrorRegisterMmio),
        (0x374 => _reserved20),
        /// Virtual Initial Count Register (for Timer): the 32-bit field located at offset 380H on the virtual-APIC page.
        (0x380 => pub ICR_TIMER: ReadWrite<u32>),
        (0x384 => _reserved21),
        /// Virtual Current Count Register (for Timer): the 32-bit field located at offset 390H on the virtual-APIC page.
        (0x390 => pub CCR_TIMER: ReadOnly<u32>),
        (0x394 => _reserved22),
        /// Virtual Divide Configuration Register (for Timer): the 32-bit field located at offset 3E0H on the virtual-APIC page.
        (0x3E0 => pub DCR_TIMER: ReadWrite<u32>),
        (0x3E4 => _reserved23),
        /// Virtual SELF IPI Register: the 32-bit field located at offset 3F0H on the virtual-APIC page.
        (0x3F0 => pub SELF_IPI: WriteOnly<u32>),
        (0x3F4 => _reserved24),
        (0x1000 => @END),
    }
}
}

EOI 寄存器

使用值0向偏移量为0xB0的寄存器写入,以信号通知中断结束。使用非零值可能会导致通用保护故障。

Local Vector Table 寄存器

处理器和LAPIC自身可以生成一些特殊的中断。虽然外部中断是在I/O APIC中配置的,但这些中断必须使用LAPIC中的寄存器进行配置。最有趣的寄存器包括:0x320 = LAPIC定时器,0x350 = LINT0,0x360 = LINT1。更多详情请参见 Intel SDM vol 3

寄存器格式:

位范围描述
0-7向量编号
8-10(定时器保留)如果是NMI则为100b
11保留
12如果中断挂起则设置
13(定时器保留)极性,设置为低电平触发
14(定时器保留)远程IRR
15(定时器保留)触发模式,设置为电平触发
16设置以屏蔽
17-31保留

Spurious Interrupt Vector 寄存器

偏移量是0xF0。低字节包含伪中断的编号。如上所述,您应该将此设置为0xFF。要启用APIC,请设置此寄存器的第8位(或0x100)。如果设置了第12位,则EOI消息不会被广播。其余的所有位目前都是保留的。

Interrupt Command 寄存器 (ICR)

中断命令寄存器由两个32位寄存器组成;一个位于0x300,另一个位于0x310。它用于向不同的处理器发送中断。中断是在0x300被写入时发出的,而不是在0x310被写入时发出的。因此,要发送中断命令,应该首先写入0x310,然后写入0x300。在0x310处有一个位于位24-27的字段,它是目标处理器的本地APIC ID(针对物理目的地模式)。这里是0x300的结构:

位范围描述
0-7向量编号,或SIPI的起始页号
8-10交付模式。0表示正常,1表示最低优先级,2表示SMI,4表示NMI,5可以是INIT或INIT级别解除,6表示SIPI
11目的地模式。清除表示物理目的地,或者设置表示逻辑目的地。如果该位被清除,则0x310中的目的地字段被视为正常处理
12交付状态。当中断被目标接受时清除。通常应在发送中断后等待此位清除
13保留
14清除表示INIT级别解除,否则设置
15设置表示INIT级别解除,否则清除
18-19目的地类型。如果>0,则忽略0x310中的目的地字段。1总是发送中断给自己,2发送给所有处理器,3发送给除当前处理器外的所有处理器。最好避免使用模式1、2和3,并坚持使用0
20-31保留

Copyright © 2025 • Created by ArceOS Team