虚拟安全模式
虚拟安全模式 (VSM) 是一组虚拟机监控程序功能和启发式,可用于托管和来宾分区,从而在操作系统软件中创建和管理新的安全边界。 VSM 是虚拟机监控程序设施,Windows安全功能,包括 Device Guard、Credential Guard、虚拟 TPM 和受防护的 VM。 这些安全功能是在Windows 10和Windows Server 2016中引入的。
VSM 使根分区和来宾分区中的操作系统软件能够创建独立的内存区域,以便存储和处理系统安全资产。 仅通过虚拟机监控程序控制并授予对这些隔离区域的访问,这是系统受信任的计算基础 (TCB) 的特权、高度信任的一部分。 由于虚拟机监控程序在比操作系统软件更高的特权级别运行,并且对 CPU MMU 和 IOMMU 中的内存访问权限控制等关键系统硬件资源进行独占控制,因此虚拟机监控程序可以在系统初始化初期保护这些隔离区域免受未经授权的访问,甚至从操作系统软件 ((例如,OS 内核和设备驱动程序)) 具有监督模式访问 ((例如 CPL0) 或"环形 0") 。
使用此体系结构时,即使在监督模式下运行的正常系统级软件 ((例如内核、驱动程序等)) 受到恶意软件的攻击,受虚拟机监控程序保护的隔离区域中的资产仍可以保持安全。
VSM 通过虚拟信任级别 (VTL) 实现和维护隔离。 在每分区和每个虚拟处理器的基础上启用和管理 VTL。
虚拟信任级别是分层的,级别高于较低级别的特权。 VTL0 是最低特权级别,VTL1 的特权高于 VTL0、VTL2 特权高于 VTL1 等。
在体系结构上,最多支持 16 个级别的 VTL:但是,虚拟机监控程序可以选择实现少于 16 个 VTL。 目前,仅实现两个 VTL。
typedef UINT8 HV_VTL, *PHV_VTL;
#define HV_NUM_VTLS 2
#define HV_INVALID_VTL ((HV_VTL) -1)
#define HV_VTL_ALL 0xF
每个 VTL 都有自己的一组内存访问保护。 这些访问保护由分区的物理地址空间中的虚拟机监控程序管理,因此无法由分区中运行的系统级软件进行修改。
由于更特权的 VTL 可以强制实施自己的内存保护,因此更高的 VTL 可以有效地保护内存区域免受较低 VTL 的保护。 实际上,这允许较低的 VTL 通过使用更高的 VTL 来保护隔离的内存区域。 例如,VTL0 可以将机密存储在 VTL1 中,此时只有 VTL1 可以访问它。 即使 VTL0 遭到入侵,机密也是安全的。
可通过多个方面在 VTL 之间实现隔离:
- 内存访问保护:每个 VTL 维护一组来宾物理内存访问保护。 在特定 VTL 上运行的软件只能根据这些保护访问内存。
- 虚拟处理器状态:虚拟处理器维护单独的每个 VTL 状态。 例如,每个 VTL 定义一组专用 VP 寄存器。 在较低 VTL 上运行的软件无法访问更高 VTL 的专用虚拟处理器的注册状态。
- 中断:除了单独的处理器状态,每个 VTL 也有其自己的中断子系统, (本地 APIC) 。 这允许更高的 VTL 处理中断,而不会危及较低 VTL 的干扰。
- 覆盖页:每个 VTL 维护某些覆盖页,以便更高的 VTL 具有可靠的访问权限。 例如,每个 VTL 都有一个单独的 hypercall 覆盖页。
VSM 功能通过 AccessVsm 分区特权标志播发到分区。 只有具有以下所有特权的分区才能使用 VSM:AccessVsm、AccessVpRegisters 和 AccessSynicRegs。
来宾应使用以下特定于模型的寄存器来访问有关 VSM 功能的报表:
MSR 地址 | 注册名称 | 说明 |
---|---|---|
0x000D0006 | HV_X64_REGISTER_VSM_CAPABILITIES | 报告 VSM 功能。 |
注册 VSM 功能 MSR 的格式如下所示:
Bits | 说明 | 属性 |
---|---|---|
63 | Dr6Shared | 读取 |
62:47 | MbecVtlMask | 读取 |
46 | DenyLowerVtlStartup | 读取 |
45:0 | RsvdZ | 读取 |
Dr6Shared 向来宾指示 Dr6 是否是 VCL 之间的共享寄存器。
MvecVtlMask 向来宾指示可以为其启用 Mbec 的 VTL。
DenyLowerVtlStartup 向来宾指示 Vtl 是否可以通过较低的 VTL 拒绝 VP 重置。
除了分区特权标志外,还可以使用两个虚拟寄存器来了解有关 VSM 状态的其他信息: HvRegisterVsmPartitionStatus
和 HvRegisterVsmVpStatus
。
HvRegisterVsmPartitionStatus 是跨所有 VTL 共享的每分区只读寄存器。 此寄存器提供有关为分区启用了哪些 VTL、已启用基于模式的执行控件以及允许的最大 VTL 的信息。
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 EnabledVtlSet : 16;
UINT64 MaximumVtl : 4;
UINT64 MbecEnabledVtlSet: 16;
UINT64 ReservedZ : 28;
};
} HV_REGISTER_VSM_PARTITION_STATUS;
HvRegisterVsmVpStatus 是一个只读寄存器,在所有 VTL 之间共享。 它是每 VP 寄存器,这意味着每个虚拟处理器都维护自己的实例。 此寄存器提供有关已启用哪些 VTL、处于活动状态以及 VP 上处于活动状态的 MBEC 模式的信息。
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 ActiveVtl : 4;
UINT64 ActiveMbecEnabled : 1;
UINT64 ReservedZ0 : 11;
UINT64 EnabledVtlSet : 16;
UINT64 ReservedZ1 : 32;
};
} HV_REGISTER_VSM_VP_STATUS;
ActiveVtl 是虚拟处理器上当前处于活动状态的 VTL 上下文的 ID。
ActiveMbecEnabled 指定 MBEC 当前在虚拟处理器上处于活动状态。
EnabledVtlSet 是虚拟处理器上启用的 VTL 的位图。
当分区启动或重置时,它开始在 VTL0 中运行。 在创建分区时禁用所有其他 VTL。
若要开始使用 VTL,较低 VTL 必须启动以下内容:
- 为分区启用目标 VTL。 这使得 VTL 正式可用于分区。
- 在一个或多个虚拟处理器上启用目标 VTL。 这使得 VTL 可用于 VP,并设置其初始上下文。 建议所有 VP 具有相同的已启用 VTL。 在某些 VPS 上启用 VTL (,但并非所有) 都可能导致意外行为。
- 为分区和 VP 启用 VTL 后,它就可以在设置 EnableVtlProtection 标志后开始设置访问保护。
请注意,VTL 不需要连续。
HvCallEnablePartitionVtl hypercall 用于为特定分区启用 VTL。 请注意,在软件实际上可以在特定 VTL 中执行之前,必须在分区中的虚拟处理器上启用 VTL。
为分区启用 VTL 后,可以在分区的虚拟处理器上启用它。 HvCallEnableVpVtl hypercall 可用于为虚拟处理器启用 VTL,从而设置其初始上下文。
虚拟处理器每个 VTL 有一个"上下文"。 如果 VTL 已切换,则 VTL 的 专用状态 也会切换。
启用 VTL 后,其配置可由运行在等于或更高 VTL 的 VP 更改。
可以使用 HvRegisterVsmPartitionConfig 寄存器配置分区范围的属性。 每个 VTL (每个分区上都有一个大于 0) 的此寄存器实例。
每个 VTL 都可以修改其自己的HV_REGISTER_VSM_PARTITION_CONFIG实例,以及较低 VTL 的实例。 VTL 可能不会修改此寄存器以获取更高的 VTL。
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 EnableVtlProtection : 1;
UINT64 DefaultVtlProtectionMask : 4;
UINT64 ZeroMemoryOnReset : 1;
UINT64 DenyLowerVtlStartup : 1;
UINT64 ReservedZ : 2;
UINT64 InterceptVpStartup : 1;
UINT64 ReservedZ : 54; };
} HV_REGISTER_VSM_PARTITION_CONFIG;
下面介绍了此寄存器的字段。
启用 VTL 后,必须先设置 EnableVtlProtection 标志,然后才能开始应用内存保护。 此标志是写入一次,这意味着设置后,无法修改它。
默认情况下,系统将 RWX 保护应用于所有当前映射的页面,以及任何将来的"热添加"页面。 热添加的页面是指在调整大小操作期间添加到分区的任何内存。
更高的 VTL 可以通过在 HV_REGISTER_VSM_PARTITION_CONFIG 中指定 DefaultVtlProtectionMask 来设置不同的默认内存保护策略。 必须在启用 VTL 时设置此掩码。 设置后,无法更改它,并且仅被分区重置清除。
bit | 说明 |
---|---|
0 | 读取 |
1 | 写入 |
2 | 内核模式执行 (KMX) |
3 | 用户模式执行 (UMX) |
ZeroMemOnReset 是一个位,用于控制分区重置前内存是否为零。 默认情况下,此配置处于打开状态。 如果设置了位,则分区的内存在重置时为零,以便较低的 VTL 无法破坏更高的 VTL 内存。 如果清除此位,则重置时分区的内存不会为零。
DenyLowerVtlStartup 标志控制虚拟处理器是否可以通过较低的 VTL 启动或重置。 这包括重置虚拟处理器 (的体系结构方法,例如 X64) 上的 SPI 以及 HvCallStartVirtualProcessor hypercall。
如果设置了 InterceptVpStartup 标志,启动或重置虚拟处理器会生成对更高 VTL 的拦截。
更高的 VTL 可以使用以下寄存器来配置较低 VTL 的行为:
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 MbecEnabled : 1;
UINT64 TlbLocked : 1;
UINT64 ReservedZ : 62;
};
} HV_REGISTER_VSM_VP_SECURE_VTL_CONFIG;
每个 VTL (高于 0) 的每个 VTL 都有此寄存器的实例,每个 VTL 都低于自身。 例如,VTL2 将有两个此寄存器的实例 - 一个用于 VTL1,另一个用于 VTL0。
下面介绍了此寄存器的字段。
此字段配置是否为较低的 VTL 启用 MBEC。
此字段锁定下 VTL 的 TLB。 此功能可用于防止降低 VTL 导致 TLB 失效,这可能会干扰更高的 VTL。 设置此位后,会阻止来自较低 VTL 的所有地址空间刷新请求,直到解除锁定为止。
若要解锁 TLB,更高的 VTL 可以清除此位。 此外,一旦 VP 返回到较低的 VTL,它将释放它当时持有的所有 TLB 锁。
当 VP 从较低的 VTL 切换到更高的 VTL 时,VTL 将"输入"。 这可能是由以下原因引起的:
- VTL 调用:这是软件明确希望调用更高 VTL 中的代码时。
- 安全中断:如果为更高的 VTL 收到中断,VP 将输入更高的 VTL。
- 安全拦截:某些操作将触发安全中断, (访问某些 MSR,例如) 。
进入 VTL 后,它必须自愿退出。 更高的 VTL 不能被较低的 VTL 抢占。
为了对条目做出适当的反应,更高的 VTL 可能需要知道输入的原因。 为了区分条目原因,VTL 条目包含在 HV_VP_VTL_CONTROL 结构中。
例如,当较低 VTL 启动更高 VTL (的条目时,可通过 HvCallVtlCall hypercall 保护具有更高 VTL) 内存区域。
VTL 调用会保留 VTL 交换机之间共享寄存器的状态。 专用寄存器在每 VTL 级别保留。 这些限制的例外是 VTL 调用序列所需的寄存器。 VTL 调用需要以下寄存器:
X64 | x86 | 说明 |
---|---|---|
RCX | EDX:EAX | 指定虚拟机监控程序的 VTL 调用控制输入 |
RAX | ECX | 保留 |
VTL 调用控制输入中的所有位当前保留。
VTL 调用只能从最特权的处理器模式启动。 例如,在 x64 系统上,VTL 调用只能来自 CPL0。 从处理器模式发起的 VTL 调用,该模式不是系统上特权最高的调用,会导致虚拟机监控程序向虚拟处理器注入#UD异常。
VTL 调用只能切换到下一个最高 VTL。 换句话说,如果启用了多个 VTL,则调用无法"跳过"VTL。 以下操作会导致#UD异常:
- 从处理器模式启动的 VTL 调用,这是系统 (体系结构中特定于) 的最特权的调用。
- 从实际模式 (x86/x64) 的 VTL 调用
- (或尚未) 启用目标 VTL 的虚拟处理器上的 VTL 调用。
- 具有无效控件输入值的 VTL 调用
切换到较低的 VTL 称为"return"。 VTL 完成处理后,可以启动 VTL 返回,以便切换到较低的 VTL。 VTL 返回的唯一方法是,如果更高的 VTL 自愿启动一个。 较低的 VTL 永远无法抢占更高的 VTL。
"VTL 返回"是当更高的 VTL 通过 HvCallVtlReturn hypercall 启动切换到较低的 VTL 时。 与 VTL 调用类似,将关闭专用处理器状态,并且共享状态保持不变。 如果较低的 VTL 显式调用到更高的 VTL,则虚拟机监控程序在返回完成之前递增更高的 VTL 指令指针,以便它可以在 VTL 调用后继续。
VTL 返回代码序列需要使用以下寄存器:
X64 | x86 | 说明 |
---|---|---|
RCX | EDX:EAX | 指定虚拟机监控程序的 VTL 返回控件输入 |
RAX | ECX | 保留 |
VTL 返回控件输入具有以下格式:
Bits | 字段 | 说明 |
---|---|---|
63:1 | RsvdZ | |
0 | 快速返回 | 未还原寄存器 |
以下操作将生成#UD异常:
- 尝试在当前处于活动状态的最低 VTL 时返回 VTL
- 尝试使用无效控件输入值返回 VTL
- 尝试从处理器模式返回 VTL,该模式不是系统上特权最高的 (体系结构特定的)
在处理返回过程中,虚拟机监控程序可以从 HV_VP_VTL_CONTROL 结构还原较低 VTL 的注册状态。 例如,在处理安全中断后,更高的 VTL 可能需要返回,而不会中断较低的 VTL 状态。 因此,虚拟机监控程序提供了一种机制,用于将较低的 VTL 寄存器还原到存储在 VTL 控制结构中的预调用值。
如果不需要此行为,则更高的 VTL 可以使用"快速返回"。 快速返回是在虚拟机监控程序不从控制结构还原注册状态时。 应尽可能利用此功能,以避免不必要的处理。
可以使用 VTL 返回输入的位 0 设置此字段。 如果设置为 0,则从HV_VP_VTL_CONTROL结构还原寄存器。 如果此位设置为 1,则寄存器不会还原 (快速返回) 。
虚拟机监控程序提供机制来帮助 VTL 调用并通过 hypercall 页面返回。 本页抽象化切换 VTL 所需的特定代码序列。
执行 VTL 调用的代码序列,可以通过在 hypercall 页中执行特定指令来访问返回。 调用/返回区块位于由 HvRegisterVsmCodePageOffset 虚拟寄存器确定的 hypercall 页面中的偏移量。 这是一个只读和分区范围的寄存器,每个 VTL 有一个单独的实例。
VTL 可以使用 CALL 指令执行 VTL 调用/返回。 对 hypercall 页面中正确位置的调用将启动 VTL 调用/返回。
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 VtlCallOffset : 12;
UINT64 VtlReturnOffset : 12;
UINT64 ReservedZ : 40;
};
} HV_REGISTER_VSM_CODE_PAGE_OFFSETS;
总之,使用 hypercall 页面调用代码序列的步骤如下所示:
- 将 hypercall 页面映射到 VTL 的 GPA 空间
- 确定代码序列的正确偏移量, (VTL 调用或返回) 。
- 使用 CALL 执行代码序列。
VSM 提供的一项必要的保护是能够隔离内存访问。
较高的 VTL 对较低 VTL 允许的内存访问类型具有高度的控制。 对于特定 GPA 页面,可以使用更高 VTL 指定的三种基本保护类型:读取、写入和 eXecute。 下表中定义了这些项:
名称 | 说明 |
---|---|
读取 | 控制是否允许读取访问内存页 |
写入 | 控制是否允许对内存页进行写入访问 |
执行 | 控制是否允许内存页获取指令。 |
这三种组合用于以下类型的内存保护:
- 无访问权限
- 只读,无执行
- 只读,执行
- 读/写,无执行
- 读/写,执行
如果启用了"基于模式的执行控制 (MBEC) ",则可以单独设置用户和内核模式执行保护。
更高的 VTL 可以通过 HvCallModifyVtlProtectionMask hypercall 设置 GPA 的内存保护。
内存访问权限可由特定 VTL 的多个源设置。 每个 VTL 的权限可能会受到许多其他 VTL 以及主机分区的限制。 应用保护的顺序如下:
- 主机设置的内存保护
- 由较高 VTL 设置的内存保护
换句话说,VTL 保护取代主机保护。 较高级别的 VTL 取代了较低级别的 VTL。 请注意,VTL 可能无法为自身设置内存访问权限。
一个符合性接口预计不会覆盖 RAM 上的任何非 RAM 类型。
如果在较低 VTL 处运行的 VP 尝试违反由较高 VTL 设置的内存保护,则会生成拦截。 此拦截由设置保护的更高 VTL 接收。 这允许更高的 VTL 逐个案例处理违规行为。 例如,更高的 VTL 可以选择返回错误,或模拟访问。
当 VTL 在较低的 VTL 上放置内存限制时,它可能需要在授予"执行"权限时区分用户和内核模式。 例如,如果代码完整性检查发生在更高的 VTL 中,则区分用户模式和内核模式的功能意味着 VTL 只能对内核模式应用程序强制实施代码完整性。
除了传统的三种内存保护 (读取、写入、执行) 之外,MBEC 还引入了用户模式与内核模式之间的区别,用于执行保护。 因此,如果启用了 MBEC,则 VTL 有机会设置四种类型的内存保护:
名称 | 说明 |
---|---|
读取 | 控制是否允许读取访问内存页 |
写入 | 控制是否允许对内存页进行写入访问 |
用户模式执行 (UMX) | 控制是否允许内存页在用户模式下生成的指令提取。 注意:如果禁用 MBEC,则忽略此设置。 |
内核模式执行 (UMX) | 控制是否允许内存页在内核模式下生成的指令提取。 注意:如果禁用 MBEC,此设置控制用户模式和内核模式执行访问权限。 |
仅当虚拟处理器在用户模式下运行时,才会使用"用户模式执行"保护标记的内存。 同样,只有在虚拟处理器在内核模式下运行时,"内核模式执行"内存才可执行。
可以单独设置 KMX 和 UMX,以便以不同的方式在用户和内核模式之间强制实施执行权限。 支持 UMX 和 KMX 的所有组合,但 KMX=1、UMX=0 除外。 此组合的行为未定义。
默认情况下,所有 VTL 和虚拟处理器都禁用 MBEC。 禁用 MBEC 后,内核模式执行位确定内存访问限制。 因此,如果禁用 MBEC,KMX=1 代码在内核和用户模式下都是可执行的。
访问描述符表的任何用户模式代码都必须在标记为 KMX=UMX=1 的 GPA 页面中。 不支持从标记为 KMX=0 的 GPA 页访问描述符表的用户模式软件,并导致常规保护错误。
若要使用基于模式的执行控件,必须在两个级别启用它:
- 为分区启用 VTL 后,必须使用 HvCallEnablePartitionVtl 启用 MBEC
- 必须使用 HvRegisterVsmVpSecureVtlConfig,按 VP 和每个 VTL 配置 MBEC。
Supervisor-Mode执行防护 (SMEP) 是某些平台上支持的处理器功能。 SMEP 可能会影响 MBEC 的操作,因为它限制对内存页的监督访问。 虚拟机监控程序遵循与 SMEP 相关的以下策略:
- 如果 SMEP 不适用于来宾 OS (是因为硬件功能还是处理器兼容性模式) ,MBEC 将不受影响。
- 如果 SMEP 可用且已启用,则 MBEC 将不受影响。
- 如果 SMEP 可用且已禁用,则所有执行限制均由 KMX 控件控制。 因此,仅允许标记为 KMX=1 的代码执行。
虚拟处理器为每个活动 VTL 维护单独的状态。 但是,某些状态是特定 VTL 的专用状态,其余状态在所有 VTL 之间共享。
每个 VTL (a.k.a. 专用状态) 的状态由虚拟机监控程序跨 VTL 转换保存。 如果启动 VTL 交换机,虚拟机监控程序会保存活动 VTL 的当前专用状态,然后切换到目标 VTL 的专用状态。 无论 VTL 交换机如何,共享状态都保持活动状态。
通常,每个 VTL 都有自己的控制寄存器、RIP 寄存器、RSP 寄存器和 MSR。 下面是每个 VTL 专用的特定寄存器和 MSR 的列表。
专用 MSR:
- SYSENTER_CS、SYSENTER_ESP、SYSENTER_EIP、STAR、LSTAR、CSTAR、SFMASK、EFER、PAT、KERNEL_GSBASE、FS。BASE、GS。BASE、TSC_AUX
- HV_X64_MSR_HYPERCALL
- HV_X64_MSR_GUEST_OS_ID
- HV_X64_MSR_REFERENCE_TSC
- HV_X64_MSR_APIC_FREQUENCY
- HV_X64_MSR_EOI
- HV_X64_MSR_ICR
- HV_X64_MSR_TPR
- HV_X64_MSR_APIC_ASSIST_PAGE
- HV_X64_MSR_NPIEP_CONFIG
- HV_X64_MSR_SIRBP
- HV_X64_MSR_SCONTROL
- HV_X64_MSR_SVERSION
- HV_X64_MSR_SIEFP
- HV_X64_MSR_SIMP
- HV_X64_MSR_EOM
- HV_X64_MSR_SINT0 – HV_X64_MSR_SINT15
- HV_X64_MSR_STIMER0_CONFIG – HV_X64_MSR_STIMER3_CONFIG
- HV_X64_MSR_STIMER0_COUNT – HV_X64_MSR_STIMER3_COUNT
- 本地 APIC 注册 (,包括 CR8/TPR)
专用寄存器:
- RIP、RSP
- RFLAGS
- CR0、CR3、CR4
- DR7
- IDTR、GDTR
- CS、DS、ES、FS、GS、SS、TR、LDTR
- TSC
- DR6 (*依赖于处理器类型。读取 HvRegisterVsmCapabilities 虚拟寄存器以确定共享/专用状态)
VTL 共享状态,以减少切换上下文的开销。 共享状态还允许在 VTL 之间进行一些必要的通信。 大多数常规用途和浮点寄存器是共享的,大多数体系结构 MSR 都是共享的。 下面是所有 VTL 之间共享的特定 MSR 和寄存器的列表:
共享 MSR:
- HV_X64_MSR_TSC_FREQUENCY
- HV_X64_MSR_VP_INDEX
- HV_X64_MSR_VP_RUNTIME
- HV_X64_MSR_RESET
- HV_X64_MSR_TIME_REF_COUNT
- HV_X64_MSR_GUEST_IDLE
- HV_X64_MSR_DEBUG_DEVICE_OPTIONS
- MTRR
- MCG_CAP
- MCG_STATUS
共享寄存器:
- Rax、Rbx、Rcx、Rdx、Rsi、Rdi、Rbp
- CR2
- R8 – R15
- DR0 – DR5
- X87 浮点状态
- XMM 状态
- AVX 状态
- XCR0 (XFEM)
- DR6 (*依赖于处理器类型。读取 HvRegisterVsmCapabilities 虚拟寄存器以确定共享/专用状态)
任何大于 0 的 VTL 都不支持实际模式。 大于 0 的 VTL 可以在 32 位或 64 位模式下运行。
为了在虚拟信任级别之间实现高级别隔离,虚拟安全模式为虚拟处理器上启用的每个 VTL 提供单独的中断子系统。 这可确保 VTL 能够发送和接收中断,而不会干扰不太安全的 VTL。
每个 VTL 都有自己的中断控制器,仅在虚拟处理器在该特定 VTL 中运行时处于活动状态。 如果虚拟处理器切换 VTL 状态,则处理器上的中断控制器也会切换。
针对 VTL 的中断(高于活动 VTL)将导致直接 VTL 开关。 然后,更高的 VTL 可以接收中断。 如果由于 VPR/CR8 值而无法接收中断,则中断保留为"挂起",并且 VTL 不会切换。 如果有多个具有挂起中断的 VTL,则最高 VTL 优先于 (,而不会注意到较低的 VTL) 。
当中断针对较低 VTL 时,在下次虚拟处理器转换为目标 VTL 之前,不会传递中断。 在启用了更高 VTL 的虚拟处理器上,将删除面向较低 VTL 的 INIT 和启动 IP。 由于 INIT/SPI 被阻止,因此应使用 HvCallStartVirtualProcessor hypercall 来启动处理器。
为了切换 VTL,RFLAGS。IF 不会影响安全中断是否触发 VTL 开关。 如果 RFLAGS。如果清除以屏蔽中断,则中断到更高的 VTL 仍会导致 VTL 切换到更高的 VTL。 在决定是否立即中断时,仅考虑更高的 VTL TPR/CR8 值。
此行为还会影响 VTL 返回时挂起的中断。 如果 RFLAGS。如果清除位以屏蔽给定 VTL 中的中断,并且 VTL 将 (返回到较低的 VTL) ,虚拟机监控程序将重新评估任何挂起的中断。 这将导致立即回叫到更高的 VTL。
如果 VTL 正在阻止将中断立即传送到同一虚拟处理器的较低 VTL,则较高的 VTL 可能会注册以接收通知。 更高的 VTL 可以通过虚拟注册 HvRegisterVsmVina 启用虚拟中断通知协助 (VINA) :
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 Vector : 8;
UINT64 Enabled : 1;
UINT64 AutoReset : 1;
UINT64 AutoEoi : 1;
UINT64 ReservedP : 53;
};
} HV_REGISTER_VSM_VINA;
每个 VP 上的每个 VTL 都有自己的 VINA 实例,以及其自己的 HvRegisterVsmVina 版本。 当下 VTL 中断准备好立即交付时,VINA 设施将生成当前活动较高 VTL 的边缘触发中断。
为了防止启用此设施时发生中断的洪水,VINA 设施包含一些有限的状态。 生成 VINA 中断时,VINA 设施的状态将更改为"断言"。 将中断结束发送到与 VINA 设施关联的 SINT 不会清除"断言"状态。 断言状态只能通过以下两种方式之一清除:
- 可以通过写入 HV_VP_VTL_CONTROL 结构的 VinaAsserted 字段手动清除状态。
- 如果在 HvRegisterVsmVina 寄存器中启用了"在 VTL 条目上自动重置"选项,则会在 VTL 的下一个条目上自动清除状态。
这允许在安全 VTL 上运行的代码仅收到针对较低 VTL 收到的第一个中断的通知。 如果安全 VTL 希望收到其他中断的通知,则可以清除 VP 辅助页面的 VinaAsserted 字段,并会收到下一个新中断的通知。
虚拟机监控程序允许更高的 VTL 为较低 VTL 上下文中发生的事件安装拦截。 这为更高的 VTL 提供了对较低 VTL 资源的提升控制级别。 安全拦截可用于保护系统关键型资源,并防止攻击降低 VTL。
安全拦截排队到更高的 VTL,并且 VTL 可在 VP 上运行。
截获类型 | 截距应用于 |
---|---|
内存访问 | 尝试访问由更高 VTL 建立的 GPA 保护。 |
控制注册访问权限 | 尝试访问由较高 VTL 指定的一组控件寄存器。 |
多个 VTL 可以在较低的 VTL 中为同一事件安装安全拦截。 因此,建立层次结构以确定嵌套拦截的通知位置。 以下列表是通知截获位置的顺序:
- 较低 VTL
- 更高 VTL
VTL 收到安全拦截通知后,必须采取措施,使较低的 VTL 可以继续。 更高的 VTL 可以通过多种方式处理拦截,包括:注入异常、模拟访问或提供访问代理。 在任何情况下,如果需要修改较低 VTL VP 的专用状态,则应使用 HvCallSetVpRegisters 。
更高的 VTL 可以截获对特定控制寄存器的访问。 这可以通过使用 HvCallSetVpRegisters hypercall 设置 HvX64RegisterCrInterceptControl 来实现。 在 HvX64RegisterCrInterceptControl 中设置控件位将触发相应控件寄存器的每个访问的拦截。
typedef union
{
UINT64 AsUINT64;
struct
{
UINT64 Cr0Write : 1;
UINT64 Cr4Write : 1;
UINT64 XCr0Write : 1;
UINT64 IA32MiscEnableRead : 1;
UINT64 IA32MiscEnableWrite : 1;
UINT64 MsrLstarRead : 1;
UINT64 MsrLstarWrite : 1;
UINT64 MsrStarRead : 1;
UINT64 MsrStarWrite : 1;
UINT64 MsrCstarRead : 1;
UINT64 MsrCstarWrite : 1;
UINT64 ApicBaseMsrRead : 1;
UINT64 ApicBaseMsrWrite : 1;
UINT64 MsrEferRead : 1;
UINT64 MsrEferWrite : 1;
UINT64 GdtrWrite : 1;
UINT64 IdtrWrite : 1;
UINT64 LdtrWrite : 1;
UINT64 TrWrite : 1;
UINT64 MsrSysenterCsWrite : 1;
UINT64 MsrSysenterEipWrite : 1;
UINT64 MsrSysenterEspWrite : 1;
UINT64 MsrSfmaskWrite : 1;
UINT64 MsrTscAuxWrite : 1;
UINT64 MsrSgxLaunchControlWrite : 1;
UINT64 RsvdZ : 39;
};
} HV_REGISTER_CR_INTERCEPT_CONTROL;
为了允许更精细的控制,控件寄存器的子集也具有相应的掩码寄存器。 掩码寄存器可用于在相应控件寄存器的子集上安装拦截。 如果未定义掩码寄存器,则 HvX64RegisterCrInterceptControl) 定义的任何访问 (都将触发拦截。
虚拟机监控程序支持以下掩码寄存器:HvX64RegisterCrInterceptCr0Mask、HvX64RegisterCrInterceptCrInterceptCr4Mask 和 HvX64RegisterCrInterceptIa32MiscEnableMask。
设备实际上具有与 VTL0 相同的特权级别。 启用 VSM 后,所有设备分配的内存都标记为 VTL0。 任何 DMA 访问都具有与 VTL0 相同的权限。