PPC KVM 半虚拟化接口¶
PowerPC 上 KVM 的基本执行原理是:所有内核空间代码都在 PR=1(用户空间)中运行。这样我们就能捕获所有特权指令并进行相应的仿真。
不幸的是,这同时也是它的缺点。有相当多的特权指令不必要地将我们返回到虚拟机管理程序,即使它们可以以不同的方式处理。
这正是 PPC PV 接口所能帮助的地方。它将特权指令在虚拟机管理程序的帮助下转换为非特权指令。根据我的一些基准测试,这能将虚拟化成本降低约 50%。
该接口的代码可以在 arch/powerpc/kernel/kvm* 中找到
查询是否存在¶
为了查明我们是否在 KVM 上运行,我们利用设备树。当 Linux 在 KVM 上运行时,存在一个 /hypervisor 节点。该节点包含一个兼容属性,其值为“linux,kvm”。
一旦您确定您正在支持 PV 的 KVM 下运行,您现在就可以使用下面描述的超级调用。
KVM 超级调用¶
在设备树的 /hypervisor 节点内,有一个名为“hypercall-instructions”的属性。该属性最多包含 4 个构成超级调用的操作码。要调用一个超级调用,只需调用这些指令即可。
参数如下
寄存器
输入
输出
r0
易失性
r3
第 1 个参数
返回代码
r4
第 2 个参数
第 1 个输出值
r5
第 3 个参数
第 2 个输出值
r6
第 4 个参数
第 3 个输出值
r7
第 5 个参数
第 4 个输出值
r8
第 6 个参数
第 5 个输出值
r9
第 7 个参数
第 6 个输出值
r10
第 8 个参数
第 7 个输出值
r11
超级调用号
第 8 个输出值
r12
易失性
超级调用定义在通用代码中共享,因此 x86 和 powerpc 采用相同的超级调用号,但每个 KVM 超级调用还需要与 KVM 供应商代码进行或运算,该代码为 (42 << 16)。
返回代码可能如下
代码
含义
0
成功
12
超级调用未实现
<0
错误
魔术页¶
为了实现虚拟机管理程序和客户机之间的通信,有一个新的共享页,其中包含部分对监管者可见的寄存器状态。客户机可以使用 KVM 超级调用 KVM_HC_PPC_MAP_MAGIC_PAGE 来映射此共享页。
发出此超级调用后,客户机总能将魔术页映射到所需位置。第一个参数表示 MMU 启用时的有效地址。第二个参数表示实际模式下的地址(如果适用于目标)。目前,我们总是将页映射到 -4096。这样我们就可以使用绝对加载和存储功能来访问它。以下指令读取魔术页的第一个字段
ld rX, -4096(0)
该接口设计为可扩展,以便日后需要向魔术页添加更多寄存器。如果您向魔术页添加字段,也请定义一个新的超级调用功能来指示宿主可以为您提供更多寄存器。只有当宿主支持这些额外功能时,才使用它们。
魔术页布局由 arch/powerpc/include/uapi/asm/kvm_para.h 中的 struct kvm_vcpu_arch_shared 描述。
魔术页特性¶
当使用 KVM 超级调用 KVM_HC_PPC_MAP_MAGIC_PAGE 映射魔术页时,会将第二个返回值传递给客户机。该第二个返回值包含魔术页中可用特性的位图。
目前魔术页提供以下增强功能
KVM_MAGIC_FEAT_SR
在魔术页中映射 SR 寄存器读/写
KVM_MAGIC_FEAT_MAS0_TO_SPRG7
映射 MASn、ESR、PIR 和高 SPRG
对于魔术页中的增强功能,请在使用前检查该功能是否存在!
魔术页标志¶
除了指示宿主是否支持特定功能的特性之外,我们还有一个通道供客户机告知宿主其是否支持某项功能。我们称之为“标志”。
标志通过有效地址的低 12 位传递给宿主。
客户机目前可以暴露以下标志
MAGIC_PAGE_FLAG_NOT_MAPPED_NX 客户机正确处理与魔术页相关的 NX 位
MSR 位¶
MSR 包含需要虚拟机管理程序干预的位以及不需要直接虚拟机管理程序干预的位,因为它们只在进入客户机时才被解释,或者对虚拟机管理程序的行为没有任何影响。
以下位可在客户机内安全设置
MSR_EE
MSR_RI
如果 MSR 中有任何其他位发生变化,请仍然使用 mtmsr(d)。
修补指令¶
在 32 位系统上,“ld”和“std”指令分别转换为“lwz”和“stw”指令,并增加 4 的偏移量以适应大端序。
以下是 Linux 内核作为客户机运行时执行的映射列表。实现这些映射中的任何一个都是可选的,因为指令陷阱也作用于共享页。因此,调用特权指令仍然像以前一样有效。
从 |
到 |
---|---|
mfmsr rX |
ld rX, magic_page->msr |
mfsprg rX, 0 |
ld rX, magic_page->sprg0 |
mfsprg rX, 1 |
ld rX, magic_page->sprg1 |
mfsprg rX, 2 |
ld rX, magic_page->sprg2 |
mfsprg rX, 3 |
ld rX, magic_page->sprg3 |
mfsrr0 rX |
ld rX, magic_page->srr0 |
mfsrr1 rX |
ld rX, magic_page->srr1 |
mfdar rX |
ld rX, magic_page->dar |
mfdsisr rX |
lwz rX, magic_page->dsisr |
mtmsr rX |
std rX, magic_page->msr |
mtsprg 0, rX |
std rX, magic_page->sprg0 |
mtsprg 1, rX |
std rX, magic_page->sprg1 |
mtsprg 2, rX |
std rX, magic_page->sprg2 |
mtsprg 3, rX |
std rX, magic_page->sprg3 |
mtsrr0 rX |
std rX, magic_page->srr0 |
mtsrr1 rX |
std rX, magic_page->srr1 |
mtdar rX |
std rX, magic_page->dar |
mtdsisr rX |
stw rX, magic_page->dsisr |
tlbsync |
nop |
mtmsrd rX, 0 |
b <特殊 mtmsr 部分> |
mtmsr rX |
b <特殊 mtmsr 部分> |
mtmsrd rX, 1 |
b <特殊 mtmsrd 部分> |
[仅 Book3S] |
|
mtsrin rX, rY |
b <特殊 mtsrin 部分> |
[仅 BookE] |
|
wrteei [0|1] |
b <特殊 wrteei 部分> |
有些指令需要比加载或存储指令更多的逻辑来确定发生了什么。为了能够修补这些指令,我们保留了一些 RAM,可以在其中实时翻译指令。发生的情况如下:
将仿真代码复制到内存
修补该代码以适应被仿真的指令
修补该代码以返回到原始 pc + 4
修补原始指令以分支到新代码
这样,我们可以注入任意数量的代码来替换单个指令。这允许我们在设置 EE=1 时检查挂起的中断,例如。
KVM on PowerPC 中的超级调用 ABI¶
KVM 超级调用 (ePAPR)
这些是符合 ePAPR 规范的超级调用实现(如上所述)。甚至通用超级调用也在此实现,例如 ePAPR 空闲超级调用。这些在所有目标上都可用。
PAPR 超级调用
PAPR 超级调用是运行服务器 PowerPC PAPR 客户机(QEMU 中的 -M pseries)所必需的。这些与 POWER 虚拟机管理程序 pHyp 实现的超级调用相同。其中一些在内核中处理,另一些在用户空间中处理。这仅在 book3s_64 上可用。
OSI 超级调用
Mac-on-Linux 是 KVM on PowerPC 的另一个用户,它有自己的超级调用(远在 KVM 之前)。这是为了保持兼容性而支持的。所有这些超级调用都被转发到用户空间。这仅在 book3s_32 上有用,但也可用于 book3s_64。