了解 x2APIC 的虚拟 APIC 页面

Understanding the virtual APIC page for x2APIC

我正在编写一个 VMM,我正在尝试支持虚拟机访问 x2APIC 的寄存器 OS 运行 在 VMX 非根模式下。

我想从做一些简单的事情开始,例如从来宾 OS 中读取本地 APIC ID。我尝试在我的 VMM 中添加对此的支持,但我读取的值似乎不正确。

遗憾的是,我似乎无法在网上找到很多关于虚拟APIC 页面的信息。我已经通读了英特尔手册的第 29 章(APIC 虚拟化和虚拟中断),这就是我正在做的事情:

  1. 在基于辅助处理器的 VM 执行控件中,我将以下位设置为 1:(我在下面设置位 9,因为我最终想支持发布的 IPI)

    1. SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE(位 4)
    2. SECONDARY_EXEC_APIC_REGISTER_VIRT(位 8)
    3. SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY(位 9)
  2. 在 MSR 位图中,我禁用了对 0x802 的拦截,这是本地 APIC ID 寄存器。

  3. 在我的客OS里,我用rdmsr0x802

我在固定到不同内核的两个线程上执行步骤 3。他们都从寄存器中读取值 2621447225。这似乎是不正确的,因为线程被固定到不同的内核,因此应该读取不同的本地 APIC ID(而且数字 2621447225 真的很大)。 我做错了什么?

这里有一些额外的信息供您参考:

在英特尔手册的第 29.5 节(虚拟化基于 MSR 的 APIC 访问)中,它说:

If “APIC-register virtualization” is 1 and ECX contains a value in the range 800H–8FFH, the instruction reads the 8 bytes from offset X on the virtual-APIC page into EDX:EAX, where X = (ECX & FFH) « 4. This occurs even if the local APIC is not in x2APIC mode (no general-protection fault occurs because the local APIC is not in x2APIC mode).

X 偏移量对我来说很有意义:MSR 地址 0x8020xFF 进行“与”运算时将为 0x2,而 0x2 将为左移 4 位时变为 0x20。如果您通过内存映射寄存器访问 xAPIC,0x20 是物理 APIC 页面内的偏移量。然后读取 8 个字节(即 64 位),因此低 32 位是 x2APIC 的本地 APIC ID。

我在@prl 的帮助下解决了这个问题。我必须自己为每个内核分配一个虚拟 APIC 页面,然后使用其关联内核的本地 APIC ID 单独初始化每个页面。

然后我将页面的物理地址添加到 VMCS(在 Linux 内核中定义了一个名为 VIRTUAL_APIC_PAGE_ADDR 的常量,它包含 VMCS 中的偏移量)。我没有意识到我必须初始化页面,因为它不是自动完成的。

编辑: 我在 Linux 上实现了一个支持 x2APIC 虚拟化和发布中断处理的工作虚拟化系统,并在 [=11= 中写了这两个主题].该文档应该相当简单,它包含一个 link 我的实现。