KVM 特定的 MSR¶
- 作者:
Glauber Costa <glommer@redhat.com>, 红帽公司, 2010
KVM 使用一些自定义 MSR 来处理一些请求。
自定义 MSR 有一个保留的范围,从 0x4b564d00 到 0x4b564dff。这个区域之外也有 MSR,但它们已被弃用,不鼓励使用。
自定义 MSR 列表¶
当前支持的自定义 MSR 列表是
- MSR_KVM_WALL_CLOCK_NEW
0x4b564d00
- 数据
必须在客户机 RAM 中的内存区域的 4 字节对齐物理地址。此内存应保存以下结构的副本
struct pvclock_wall_clock { u32 version; u32 sec; u32 nsec; } __attribute__((__packed__));
其数据将由 hypervisor 填充。hypervisor 仅保证在 MSR 写入时更新此数据。希望可靠地多次查询此信息的用户必须多次写入此 MSR。字段具有以下含义
- 版本
客户机必须在获取时间信息之前和之后检查版本,并检查它们是否都相等且为偶数。奇数版本表示正在进行的更新。
- 秒
启动时墙钟的秒数。
- 纳秒
启动时墙钟的纳秒数。
为了获得当前的墙钟时间,需要添加 MSR_KVM_SYSTEM_TIME_NEW 中的 system_time。
请注意,虽然 MSR 是每个 CPU 的实体,但此特定 MSR 的影响是全局的。
必须在使用前通过 0x4000001 cpuid 叶中的位 3 来检查此 MSR 的可用性。
- MSR_KVM_SYSTEM_TIME_NEW
0x4b564d01
- 数据
必须在客户机 RAM 中的内存区域的 4 字节对齐物理地址,以及位 0 中的使能位。此内存应保存以下结构的副本
struct pvclock_vcpu_time_info { u32 version; u32 pad0; u64 tsc_timestamp; u64 system_time; u32 tsc_to_system_mul; s8 tsc_shift; u8 flags; u8 pad[2]; } __attribute__((__packed__)); /* 32 bytes */
其数据将由 hypervisor 定期填充。每个 VCPU 只需要一次写入或注册。此结构的更新间隔是任意的,并且取决于实现。hypervisor 可以随时更新此结构,直到写入任何 bit0 == 0 的值为止。
字段具有以下含义
- 版本
客户机必须在获取时间信息之前和之后检查版本,并检查它们是否都相等且为偶数。奇数版本表示正在进行的更新。
- tsc_timestamp
在更新此结构时,当前 VCPU 的 tsc 值。客户机可以从此结构中减去此值,以得出自结构更新以来经过的时间的概念。
- system_time
主机单调时间的表示,包括上次更新此结构时的睡眠时间。单位为纳秒。
- tsc_to_system_mul
将 tsc 相关量转换为纳秒时要使用的乘数
- tsc_shift
将 tsc 相关量转换为纳秒时要使用的移位。此移位将确保与 tsc_to_system_mul 的乘法不会溢出。正值表示左移,负值表示右移。
从 tsc 到纳秒的转换涉及额外的右移 32 位。有了此信息,客户机可以通过执行以下操作来得出每个 CPU 的时间
time = (current_tsc - tsc_timestamp) if (tsc_shift >= 0) time <<= tsc_shift; else time >>= -tsc_shift; time = (time * tsc_to_system_mul) >> 32 time = time + system_time
- 标志
此字段中的位表示客户机和 hypervisor 之间协调的扩展功能。必须在 0x40000001 cpuid 叶中检查特定标志的可用性。当前标志是
标志位
cpuid 位
含义
0
24
保证跨多个 cpu 进行的时间测量是单调的
1
N/A
客户机 vcpu 已被主机暂停。请参阅 api.txt 中的 4.70
必须在使用前通过 0x4000001 cpuid 叶中的位 3 来检查此 MSR 的可用性。
- MSR_KVM_WALL_CLOCK
0x11
- 数据和功能
与 MSR_KVM_WALL_CLOCK_NEW 相同。请改用它。
此 MSR 不在 KVM 保留范围内,将来可能会被删除。它的使用已被弃用。
必须在使用前通过 0x4000001 cpuid 叶中的位 0 来检查此 MSR 的可用性。
- MSR_KVM_SYSTEM_TIME
0x12
- 数据和功能
与 MSR_KVM_SYSTEM_TIME_NEW 相同。请改用它。
此 MSR 不在 KVM 保留范围内,将来可能会被删除。它的使用已被弃用。
必须在使用前通过 0x4000001 cpuid 叶中的位 0 来检查此 MSR 的可用性。
然后,检测 kvmclock 存在的建议算法是
if (!kvm_para_available()) /* refer to cpuid.txt */ return NON_PRESENT; flags = cpuid_eax(0x40000001); if (flags & 3) { msr_kvm_system_time = MSR_KVM_SYSTEM_TIME_NEW; msr_kvm_wall_clock = MSR_KVM_WALL_CLOCK_NEW; return PRESENT; } else if (flags & 0) { msr_kvm_system_time = MSR_KVM_SYSTEM_TIME; msr_kvm_wall_clock = MSR_KVM_WALL_CLOCK; return PRESENT; } else return NON_PRESENT;
- MSR_KVM_ASYNC_PF_EN
0x4b564d02
- 数据
异步页面错误 (APF) 控制 MSR。
位 63-6 保存必须在客户机 RAM 中的 64 字节内存区域的 64 字节对齐物理地址。此内存应保存以下结构
struct kvm_vcpu_pv_apf_data { /* Used for 'page not present' events delivered via #PF */ __u32 flags; /* Used for 'page ready' events delivered via interrupt notification */ __u32 token; __u8 pad[56]; };
MSR 的位 5-4 是保留的,应为零。当 vcpu 上启用异步页面错误时,位 0 设置为 1,禁用时设置为 0。如果当 vcpu 处于 cpl == 0 时可以注入异步页面错误,则位 1 为 1。如果异步页面错误作为 #PF vmexit 传递到 L1,则位 2 为 1。只有当 CPUID 中存在 KVM_FEATURE_ASYNC_PF_VMEXIT 时,才能设置位 2。如果 CPUID 中存在 KVM_FEATURE_ASYNC_PF_INT,则位 3 启用基于中断的“页面就绪”事件传递。只有当 CPUID 中存在 KVM_FEATURE_ASYNC_PF_INT 时才能设置位 3。
“页面不存在”事件当前始终作为合成 #PF 异常传递。在传递这些事件期间,APF CR2 寄存器包含一个令牌,该令牌将在丢失的页面可用时用于通知客户机。此外,为了能够区分真实 #PF 和 APF,hypervisor 将在注入时将 64 字节内存位置(“标志”)的前 4 个字节写入。当前仅支持“标志”的第一个位,当设置时,它表示客户机正在处理异步“页面不存在”事件。如果在页面错误期间 APF “标志”为“0”,则表示这是一个常规页面错误。客户机应该在处理 #PF 异常后清除“标志”,以便可以传递下一个事件。
请注意,由于 APF “页面不存在”事件使用与常规页面错误相同的异常向量,因此客户机必须在执行可能生成正常页面错误的操作之前将“标志”重置为“0”。
hypervisor 将在 APF “页面就绪”事件注入时将 64 字节内存位置(“令牌”)的字节 4-7 写入。这些字节的内容是在 CR2 中作为“页面不存在”事件先前传递的令牌。该事件指示页面现在可用。客户机应在处理“页面就绪”事件后将“0”写入“令牌”,并在清除该位置后将“1”写入 MSR_KVM_ASYNC_PF_ACK;写入 MSR 会强制 KVM 重新扫描其队列并传递下一个挂起的通知。
请注意,在 MSR_KVM_ASYNC_PF_EN 中启用 APF 机制之前,需要写入指定“页面就绪”APF 传递的中断向量的 MSR_KVM_ASYNC_PF_INT MSR,否则可能会注入中断 #0。如果 CPUID 中存在 KVM_FEATURE_ASYNC_PF_INT,则 MSR 可用。
请注意,以前,“页面就绪”事件通过与“页面不存在”事件相同的 #PF 异常传递,但这现在已被弃用。如果未设置位 3(基于中断的传递),则不会传递 APF 事件。
如果在存在未完成的 APF 时禁用 APF,则不会传递它们。
目前,“页面就绪”APF 事件将始终在与“页面不存在”事件相同的 vcpu 上传递,但客户机不应依赖这一点。
- MSR_KVM_STEAL_TIME
0x4b564d03
- 数据
必须在客户机 RAM 中的内存区域的 64 字节对齐物理地址,以及位 0 中的使能位。此内存应保存以下结构的副本
struct kvm_steal_time { __u64 steal; __u32 version; __u32 flags; __u8 preempted; __u8 u8_pad[3]; __u32 pad[11]; }
其数据将由 hypervisor 定期填充。每个 VCPU 只需要一次写入或注册。此结构的更新间隔是任意的,并且取决于实现。hypervisor 可以随时更新此结构,直到写入任何 bit0 == 0 的值为止。客户机需要确保此结构初始化为零。
字段具有以下含义
- 版本
一个序列计数器。换句话说,客户机必须在获取时间信息之前和之后检查此字段,并确保它们都相等且为偶数。奇数版本表示正在进行的更新。
- 标志
此时,始终为零。将来可能会用于指示此结构中的更改。
- 偷取
此 vCPU 未运行的时间量,单位为纳秒。vcpu 空闲期间的时间不会报告为偷取时间。
- 抢占
指示拥有此结构的 vCPU 是否正在运行。非零值表示 vCPU 已被抢占。零表示 vCPU 未被抢占。请注意,如果 hypervisor 不支持此字段,则始终为零。
- MSR_KVM_EOI_EN
0x4b564d04
- 数据
当在 vcpu 上启用 PV 中断结束时,位 0 为 1;禁用时为 0。位 1 是保留的,必须为零。当启用 PV 中断结束时(位 0 设置),位 63-2 保存必须在客户机 RAM 中且必须为零的 4 字节内存区域的 4 字节对齐物理地址。
超管理器通常在中断注入时,会将 4 字节内存位置的最低有效位写入。值为 1 表示客户机可以跳过向 APIC 写入 EOI(使用 MSR 或 MMIO 写入);相反,通过清除客户机内存中的该位来发出 EOI 信号就足够了 - 此位置稍后将由超管理器轮询。值为 0 表示需要进行 EOI 写入。
客户机始终可以安全地忽略优化并执行 APIC EOI 写入。
超管理器保证仅在当前 VCPU 上下文中修改此最低有效位,这意味着客户机无需使用锁前缀或内存排序原语与超管理器同步。
但是,超管理器可以随时设置和清除此内存位:因此,为了确保超管理器不会中断客户机,并且不会在客户机测试该位以检测是否可以跳过 EOI apic 写入以及客户机清除该位以向超管理器发出 EOI 信号之间的窗口中清除内存区域中的最低有效位,客户机必须使用单个 CPU 指令(例如测试并清除,或比较并交换)读取内存区域中的最低有效位并清除它。
- MSR_KVM_POLL_CONTROL
0x4b564d05
控制主机端轮询。
- 数据
位 0 启用 (1) 或禁用 (0) 主机端 HLT 轮询逻辑。
例如,如果 KVM 客户机正在自行执行轮询,则它们可以请求主机不轮询 HLT。
- MSR_KVM_ASYNC_PF_INT
0x4b564d06
- 数据
第二个异步页错误 (APF) 控制 MSR。
位 0-7:用于传递“页面就绪”APF 事件的 APIC 向量。位 8-63:保留
用于异步“页面就绪”通知传递的中断向量。在 MSR_KVM_ASYNC_PF_EN 中启用异步页错误机制之前,必须设置向量。仅当 CPUID 中存在 KVM_FEATURE_ASYNC_PF_INT 时,MSR 才可用。
- MSR_KVM_ASYNC_PF_ACK
0x4b564d07
- 数据
异步页错误 (APF) 确认。
当客户机完成处理“页面就绪”APF 事件,并且“struct kvm_vcpu_pv_apf_data”中的“token”字段被清除后,它应该向 MSR 的位 0 写入“1”,这会导致主机重新扫描其队列并检查是否有更多通知待处理。如果 CPUID 中存在 KVM_FEATURE_ASYNC_PF_INT,则 MSR 可用。
- MSR_KVM_MIGRATION_CONTROL
0x4b564d08
- 数据
如果 CPUID 中存在 KVM_FEATURE_MIGRATION_CONTROL,则此 MSR 可用。位 0 表示是否允许客户机的实时迁移。
当启动客户机时,如果客户机具有加密内存,则位 0 将为 0;如果客户机没有加密内存,则位 0 将为 1。如果客户机使用
KVM_HC_MAP_GPA_RANGE
超调用将页面加密状态通信给主机,它可以设置此 MSR 中的位 0 以允许客户机的实时迁移。