计时器

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

提供以下计时器服务:

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

引用计数器

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

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

分区引用计数器 MSR

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

MSR 地址 注册名称 Description
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 地址 注册名称 Description
0x40000021 HV_X64_MSR_REFERENCE_TSC “引用 TSC”页
比特 Description 特性
63:12 GPA 页码 读/写
11:1 RsvdP (应保留值) 读/写
0 Enable 读/写

在来宾分区创建时,引用 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 地址 注册名称 Description
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_STIMER3_COUNT 合成计时器 3 的过期时间或期限。

合成计时器配置寄存器

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

创建和重置虚拟处理器时,所有合成计时器配置注册(HV_X64_MSR_STIMER0_CONFIG到HV_X64_MSR_STIMER3_CONFIG)的值将设置为0x0000000000000000。 因此,默认情况下禁用所有合成计时器。

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

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

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

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

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

合成计时器计数寄存器

比特 Description 特性
63:0 计数 - 一次性计时器的过期时间、定期计时器的持续时间 读/写

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

请注意,允许计数寄存器包装。 无论任何计时器属性如何,包装都不会影响计时器的行为。

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

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

合成计时器过期消息

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

合成 Time-Unhalted 计时器 MSR

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

与定期合成计时器不同,当来宾停止(即:空闲)时累积时间,合成 Time-Unhalted 计时器仅在来宾未停止时累积时间。

MSR 地址 注册名称 Description
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

比特 Description 特性
63:9 RsvdZ (值应设置为零) 读/写
8 已启用 读/写
7:0 Vector 读/写

综合 Time-Unhalted 计时器计数 MSR

比特 Description 特性
63:0 100 ns 单位的中断周期速率 读/写