计时器

虚拟机监控程序提供简单的计时服务。 它们基于常量速率参考时间源 (通常 x64 系统上的 ACPI 计时器) 。

提供以下计时器服务:

  • 每分区引用时间计数器。
  • 每个虚拟处理器四个合成计时器。 每个合成计时器都是单次或定期计时器,用于在消息过期时传递消息或断言中断。
  • 每个虚拟处理器一个虚拟 APIC 计时器。
  • 基于主机平台对固定时间戳计数器的支持 (iTSC) 的分区引用时间启发。

引用计数器

虚拟机监控程序维护每个分区引用时间计数器。 其特征是,对它的连续访问将返回严格单调增加 (时间) 值,如分区的任何和所有虚拟处理器所看到的那样。 此外,参考计数器是速率常量,不受处理器或总线速度转换或深度处理器电源节省状态影响。 创建分区时,分区的引用时间计数器初始化为零。 所有分区的引用计数器的计数速率相同,但在任何时候,它们的绝对值通常会有所不同,因为分区的创建时间不同。

只要至少有一个虚拟处理器未显式挂起,引用计数器将继续计数。

分区引用计数器 MSR

可以通过分区范围的 MSR 访问分区的引用计数器。

MSR 地址 注册名称 说明
0x40000020 HV_X64_MSR_TIME_REF_COUNT 分区范围) (时间引用计数

创建分区时,TIME_REF_COUNT MSR 的值将设置为 0x0000000000000000。 虚拟处理器无法修改此值。 任何写入到它的尝试都会导致#GP错误。

分区引用时间启发

分区引用时间启发将引用时间源呈现给不需要截获虚拟机监控程序的分区。 仅当基础平台支持固定处理器时间戳计数器 (TSC) 或 iTSC 时,此启发才可用。 在此类平台上,处理器 TSC 频率保持不变,而不管处理器时钟频率因使用电源管理状态(如 ACPI 处理器性能状态、处理器空闲睡眠状态 (ACPI C 状态) 等)而变化。

分区引用时间启发使用虚拟 TSC 值、偏移量和乘数,使来宾分区能够计算自分区创建以来的规范化引用时间(以 100nS 为单位)。 该机制还允许来宾分区以原子方式计算将来宾分区迁移到具有不同 TSC 速率的平台的引用时间,并提供回退机制来支持迁移到没有固定速率 TSC 功能的平台。

此设施不应用作时钟时间的源,因为使用此设施计算的引用时间似乎在来宾分区保存期间停止,直到后续还原。

分区引用时间戳计数器页

虚拟机监控程序提供一个分区范围的虚拟引用 TSC 页,该页覆盖在分区的 GPA 空间上。 通过引用 TSC MSR 访问分区的引用时间戳计数器页。

参考 TSC 页使用以下结构进行定义:

typedef struct
{
   volatile UINT32 TscSequence;
   UINT32 Reserved1;
   volatile UINT64 TscScale;
   volatile INT64 TscOffset;
   UINT64 Reserved2[509];
} HV_REFERENCE_TSC_PAGE;

参考时间戳计数器 (TSC) 页 MSR

希望访问其引用 TSC 页面的来宾必须使用以下特定于模型的寄存器 (MSR) 。 具有 AccessPartitionReferenceTsc 特权的分区可以访问 MSR。

MSR 地址 注册名称 说明
0x40000021 HV_X64_MSR_REFERENCE_TSC 参考 TSC 页
Bits 说明 属性
63:12 GPA 页码 读/写
11:1 应保留 RsvdP (值) 读/写
0 启用 读/写

在创建来宾分区时,引用 TSC MSR 的值0x0000000000000000。 因此,默认情况下禁用引用 TSC 页。 来宾必须通过设置位 0 来启用引用 TSC 页。 如果指定的基址超出了分区的 GPA 空间的末尾,则来宾将无法访问引用 TSC 页。 修改寄存器时,来宾应保留保留位的值 (1 到 11) ,以便将来兼容。

分区引用 TSC 机制

分区引用时间按以下公式计算:

ReferenceTime = ( (VirtualTsc * TscScale) >> 64) + TscOffset

乘法是 64 位乘法,这会导致 128 位数字,然后向右移动 64 次,以获取高 64 位。

TscScale 值用于跨迁移事件调整虚拟 TSC 值,以缓解从一个平台到另一个平台的 TSC 频率变化。

如果在保存/还原或实时迁移期间缩放和/或偏移字段发生更改,则 TscSequence 值用于同步对启发式引用时间的访问。 此字段充当序列号,每当修改刻度和/或偏移量字段时,该序列号将递增。 0x0 的特殊值用于指示此设施不再是引用时间的可靠源,VM 必须回退到其他源。

使用此启发计算分区引用时间的建议代码如下所示:

do
{
    StartSequence = ReferenceTscPage->TscSequence;
    if (StartSequence == 0)
    {
        // 0 means that the Reference TSC enlightenment is not available at
        // the moment, and the Reference Time can only be obtained from
        // reading the Reference Counter MSR.
        ReferenceTime = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
        return ReferenceTime;
    }

    Tsc = rdtsc();

    // Assigning Scale and Offset should neither happen before
    // setting StartSequence, nor after setting EndSequence.
    Scale = ReferenceTscPage->TscScale;
    Offset = ReferenceTscPage->TscOffset;

    EndSequence = ReferenceTscPage->TscSequence;
} while (EndSequence != StartSequence);

// The result of the multiplication is treated as a 128-bit value.
ReferenceTime = ((Tsc * Scale) >> 64) + Offset;
return ReferenceTime;

合成计时器

合成计时器提供一种机制,用于在将来的某个指定时间后生成中断。 支持单次计时器和定期计时器。 合成计时器将消息发送到指定的 SynIC SINTx, (合成中断源在过期时) ,或断言中断,具体取决于其配置方式。

虚拟机监控程序保证计时器过期信号永远不会在过期时间之前传递。 该信号可能会在过期时间后的任何时间到达。

定期计时器

虚拟机监控程序会尝试定期向计时器发出信号。 但是,如果用于发出过期信号的虚拟处理器不可用,则某些计时器过期时间可能会延迟。 虚拟处理器可能不可用,因为虚拟处理器 (暂停,例如,在截获处理) 期间,或者虚拟机监控程序的计划程序决定不应在逻辑处理器 (上计划虚拟处理器,例如,因为另一个虚拟处理器正在使用逻辑处理器,或者虚拟处理器已超出其配额) 。

如果虚拟处理器在足够长的时间内不可用,则可能会错过完整的计时器周期。 在这种情况下,虚拟机监控程序使用两种技术之一。

第一种技术涉及计时器周期调节,实际上缩短周期,直到计时器“赶上”。 如果错过了大量计时器信号,虚拟机监控程序可能无法使用周期调节进行补偿。 在这种情况下,可能会完全跳过某些计时器过期信号。

对于标记为延迟的计时器,虚拟机监控程序使用第二种方法来处理虚拟处理器长时间不可用的情况。 在这种情况下,计时器信号将延迟到此虚拟处理器可用。 如果它直到下一个计时器到期前不久才可用,则会完全跳过它。

计时器过期顺序

合成计时器和虚拟化计时器在指定过期时间或接近其指定的过期时间生成中断。 由于硬件和其他计划交互,中断可能会延迟。 在任意一组计时器之间都不能进行排序。

直接合成计时器

“直接”合成计时器在计时器过期时断言中断,而不是将消息发送到 SynIc 合成中断源。 通过设置合成计时器配置 MSR 的“DirectMode”字段,将合成计时器设置为“直接”模式。 “ApicVector”字段控制计时器过期时断言的中断向量。

合成计时器 MSR

合成计时器是通过使用模型特定的寄存器 (与每个虚拟处理器关联的 MSR) 配置的。 四个合成计时器中的每一个都有一对关联的 MSR。

MSR 地址 注册名称 说明
0x400000B0 HV_X64_MSR_STIMER0_CONFIG 合成计时器 0 的配置寄存器。
0x400000B1 HV_X64_MSR_STIMER0_COUNT 合成计时器 0 的过期时间或期限。
0x400000B2 HV_X64_MSR_STIMER1_CONFIG 合成计时器 1 的配置寄存器。
0x400000B3 HV_X64_MSR_STIMER1_COUNT 合成计时器 1 的过期时间或期限。
0x400000B4 HV_X64_MSR_STIMER2_CONFIG 合成计时器 2 的配置寄存器。
0x400000B5 HV_X64_MSR_STIMER2_COUNT 合成计时器 2 的过期时间或期限。
0x400000B6 HV_X64_MSR_STIMER3_CONFIG 合成计时器 3 的配置寄存器。
0x400000B7 HV_X64_MSR_STIMER4_COUNT 合成计时器 3 的过期时间或期限。

合成计时器配置寄存器

Bits 说明 属性
63:20 RsvdZ (值应设置为零) 读/写
19:16 SINTx - 合成中断源 读/写
15:13 RsvdZ (值应设置为零) 读/写
12 直接模式 - 计时器过期时断言和中断。 读/写
11:4 ApicVector - 在直接模式下控制断言中断向量 读/写
3 AutoEnable - 如果写入相应的计数器隐式导致启用计时器,则设置 读/写
2 延迟 - 如果计时器为惰性,则设置 读/写
1 定期 - 如果计时器是定期的,则设置 读/写
0 已启用 - 如果启用了计时器,则设置 读/写

创建并重置虚拟处理器时,所有HV_X64_MSR_STIMERx_CONFIG (综合计时器配置) 寄存器的值都设置为0x0000000000000000。 因此,默认情况下禁用所有合成计时器。

如果设置了 AutoEnable,则向相应的计数寄存器写入非零值将导致设置 Enable 并激活计数器。 否则,应在写入相应的计数寄存器后设置 Enable 以激活计数器。 有关 Count 寄存器的信息,请参阅以下部分。

当一次性计时器过期时,它会自动标记为禁用。 定期计时器保持启用状态,直到显式禁用。

如果启用了一次性操作,并且指定的计数在过去,它将立即过期。

对于未处于直接模式) 的已启用计时器 (,不允许将 SINTx 字段设置为零。 如果尝试,计时器将被标记为禁用 (即立即清除位 0) 。

写入已启用的计时器的配置寄存器可能会导致未定义的行为。 例如,仅将计时器从一次性更改为定期可能不会产生预期内容。 在更改任何其他属性之前,应始终禁用计时器。

合成计时器计数寄存器

Bits 说明 属性
63:0 计数 - 一次性计时器的过期时间、定期计时器的持续时间 读/写

编程到 Count 寄存器中的值是一个以 100 纳秒为单位的时间值。 将值零写入计数寄存器将停止计数器,从而禁用计时器,这与配置寄存器中的 AutoEnable 设置无关。

请注意,允许 Count 寄存器换行。 包装不会影响计时器的行为,而不管任何计时器属性。

对于一次性计时器,它表示绝对计时器过期时间。 当分区的引用计数器等于或大于指定的计数值时,计时器将过期。

对于定期计时器,计数表示计时器的时间段。 第一个时间段从启用合成计时器时开始。

合成计时器过期消息

计时器过期消息在计时器事件触发时发送。 有关消息有效负载的定义,请参阅 HV_TIMER_MESSAGE_PAYLOAD

合成Time-Unhalted计时器 MSR

如果分区具有 AccessSyntheticTimerRegs 特权,并且虚拟机监控程序功能标识 CPUID 叶0x40000003中设置了 EDX 位 23,则综合Time-Unhalted计时器 MSR 可用。 来宾软件可以编程合成的无休时计时器,以在指定时间量(以 100ns 为单位)执行后生成定期中断。 当中断触发时,VP 助手页中的 SyntheticTimeUnhaltedTimerExpired 字段将设置为 TRUE。 来宾软件可能会将此字段重置为 FALSE。 与体系结构性能计数器不同,合成计时器永远不会由虚拟机监控程序重置,并在中断之间连续运行。 向量 ==2 发送 NMI,其他向量发送固定中断。

与来宾停止时累积时间的常规合成计时器不同, () 处于空闲状态,合成Time-Unhalted计时器仅在来宾未停止时累积时间。

MSR 地址 注册名称 说明
0x40000114 HV_X64_MSR_STIME_UNHALTED_TIMER_CONFIG 合成Time-Unhalted计时器配置 MSR
0x40000115 HV_X64_MSR_STIME_UNHALTED_TIMER_COUNT 合成Time-Unhalted计时器计数 MSR

合成Time-Unhalted计时器配置 MSR

Bits 说明 属性
63:9 RsvdZ (值应设置为零) 读/写
8 已启用 读/写
7:0 向量 读/写

合成Time-Unhalted计时器计数 MSR

Bits 说明 属性
63:0 定期中断速率(以 100 ns 为单位) 读/写