了解 x2APIC 的虚拟 APIC 页面
Understanding the virtual APIC page for x2APIC
我正在编写一个 VMM,我正在尝试支持虚拟机访问 x2APIC 的寄存器 OS 运行 在 VMX 非根模式下。
我想从做一些简单的事情开始,例如从来宾 OS 中读取本地 APIC ID。我尝试在我的 VMM 中添加对此的支持,但我读取的值似乎不正确。
遗憾的是,我似乎无法在网上找到很多关于虚拟APIC 页面的信息。我已经通读了英特尔手册的第 29 章(APIC 虚拟化和虚拟中断),这就是我正在做的事情:
在基于辅助处理器的 VM 执行控件中,我将以下位设置为 1:(我在下面设置位 9,因为我最终想支持发布的 IPI)
SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE
(位 4)
SECONDARY_EXEC_APIC_REGISTER_VIRT
(位 8)
SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY
(位 9)
在 MSR 位图中,我禁用了对 0x802
的拦截,这是本地 APIC ID 寄存器。
在我的客OS里,我用rdmsr
读0x802
。
我在固定到不同内核的两个线程上执行步骤 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 地址 0x802
与 0xFF
进行“与”运算时将为 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 我的实现。
我正在编写一个 VMM,我正在尝试支持虚拟机访问 x2APIC 的寄存器 OS 运行 在 VMX 非根模式下。
我想从做一些简单的事情开始,例如从来宾 OS 中读取本地 APIC ID。我尝试在我的 VMM 中添加对此的支持,但我读取的值似乎不正确。
遗憾的是,我似乎无法在网上找到很多关于虚拟APIC 页面的信息。我已经通读了英特尔手册的第 29 章(APIC 虚拟化和虚拟中断),这就是我正在做的事情:
在基于辅助处理器的 VM 执行控件中,我将以下位设置为 1:(我在下面设置位 9,因为我最终想支持发布的 IPI)
SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE
(位 4)SECONDARY_EXEC_APIC_REGISTER_VIRT
(位 8)SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY
(位 9)
在 MSR 位图中,我禁用了对
0x802
的拦截,这是本地 APIC ID 寄存器。在我的客OS里,我用
rdmsr
读0x802
。
我在固定到不同内核的两个线程上执行步骤 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 地址 0x802
与 0xFF
进行“与”运算时将为 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 我的实现。