虚拟安全模式(VSM)是一组虚拟机监控程序功能和启发式,可用于托管和来宾分区,从而在作系统软件中创建和管理新的安全边界。 VSM 是 Windows 安全功能(包括 Device Guard、Credential Guard、虚拟 TPM 和受防护 VM)所基于的虚拟机监控程序设施。 Windows 10 和 Windows Server 2016 中引入了这些安全功能。
VSM 使根分区和来宾分区中的作系统软件能够创建独立内存区域,以便存储和处理系统安全资产。 仅通过虚拟机监控程序(这是系统受信任的计算基础(TCB)中高度特权、高度信任的部分)来控制和授予对这些隔离区域的访问。 由于虚拟机监控程序在比作系统软件更高的特权级别运行,并且对 CPU MMU 和 IOMMU 中的内存访问权限控制(例如 CPU MMU 和 IOMMU 中的内存访问控制)具有独占控制,因此虚拟机监控程序可以在系统初始化初期保护这些隔离区域免受未经授权的访问,甚至不受作系统软件(例如 OS 内核和设备驱动程序)的监督模式访问(例如 CPL0) 或“Ring 0”
使用此体系结构时,即使以监督模式(例如内核、驱动程序等)运行的正常系统级软件受到恶意软件的攻击,受虚拟机监控程序保护的隔离区域中的资产仍可以保持安全。
虚拟信任级别 (VTL)
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 状态。 例如,每个 VTL 定义一组专用 VP 寄存器。 在较低 VTL 上运行的软件无法访问较高 VTL 的专用虚拟处理器的注册状态。
- 中断:除了单独的处理器状态,每个 VTL 也有其自己的中断子系统(本地 APIC)。 这允许更高的 VTL 处理中断,而不会受到较低 VTL 的干扰。
- 覆盖页:某些覆盖页按 VTL 维护,以便更高的 VTL 具有可靠的访问权限。 例如,每个 VTL 都有单独的超覆盖页。
VSM 检测和状态
VSM 功能通过 AccessVsm 分区特权标志播发到分区。 只有具有以下所有特权的分区才能利用 VSM:AccessVsm、AccessVpRegisters 和 AccessSynicRegs。
VSM 功能检测
来宾应使用以下特定于模型的寄存器来访问有关 VSM 功能的报表:
| MSR 地址 | 注册名称 | Description |
|---|---|---|
| 0x000D0006 | HV_X64_REGISTER_VSM_CAPABILITIES | 报告 VSM 功能。 |
注册 VSM 功能 MSR 的格式如下所示:
| 比特 | Description | 特性 |
|---|---|---|
| 63 | Dr6Shared | 读取 |
| 62:47 | MbecVtlMask | 读取 |
| 46 | DenyLowerVtlStartup | 读取 |
| 45:0 | RsvdZ | 读取 |
Dr6Shared 向来宾指示 Dr6 是否是 VTL 之间的共享寄存器。
MvecVtlMask 向来宾指示可以为其启用 Mbec 的 VCL。
DenyLowerVtlStartup 向来宾指示 Vtl 是否可以通过较低的 VTL 拒绝 VP 重置。
VSM 状态注册
除了分区特权标志外,还可以使用两个虚拟寄存器来了解有关 VSM 状态的其他信息: HvRegisterVsmPartitionStatus 和 HvRegisterVsmVpStatus。
HvRegisterVsmPartitionStatus
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
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 的位图。
分区 VTL 初始状态
分区启动或重置时,它将开始在 VTL0 中运行。 在分区创建时禁用所有其他 VTL。
VTL 启用
若要开始使用 VTL,较低 VTL 必须启动以下内容:
- 为分区启用目标 VTL。 这使得 VTL 正式可用于分区。
- 在一个或多个虚拟处理器上启用目标 VTL。 这使得 VTL 可用于 VP,并设置其初始上下文。 建议所有 VP 具有相同的已启用 VTL。 在某些 VPS(但并非所有)上启用 VTL 可能会导致意外行为。
- 为分区和 VP 启用 VTL 后,可以在设置 EnableVtlProtection 标志后开始设置访问保护。
请注意,VTL 不需要连续。
为分区启用目标 VTL
HvCallEnablePartitionVtl hypercall 用于为特定分区启用 VTL。 请注意,在软件实际可在特定 VTL 中执行之前,必须在分区中的虚拟处理器上启用 VTL。
为虚拟处理器启用目标 VTL
为分区启用 VTL 后,可以在分区的虚拟处理器上启用它。 HvCallEnableVpVtl hypercall 可用于为虚拟处理器启用 VTL,从而设置其初始上下文。
虚拟处理器每个 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 保护
启用 VTL 后,必须先设置 EnableVtlProtection 标志,然后才能开始应用内存保护。 此标志是写入一次,这意味着设置后,无法修改它。
默认保护掩码
默认情况下,系统将 RWX 保护应用于所有当前映射的页面,以及任何将来的“热添加”页面。 热添加的页面是指在调整大小作期间添加到分区的任何内存。
更高的 VTL 可以通过在 HV_REGISTER_VSM_PARTITION_CONFIG 中指定 DefaultVtlProtectionMask 来设置不同的默认内存保护策略。 必须在启用 VTL 时设置此掩码。 设置后,无法更改它,并且仅被分区重置清除。
| Bit | Description |
|---|---|
| 0 | 读取 |
| 1 | 写入 |
| 2 | 内核模式执行 (KMX) |
| 3 | 用户模式执行 (UMX) |
重置时为零内存
ZeroMemOnReset 是一个位,用于控制分区重置前内存是否为零。 此配置默认处于打开状态。 如果设置了位,则重置时将零分区的内存,以便较低的 VTL 无法破坏更高的 VTL 内存。 如果清除此位,则重置时分区的内存不会为零。
DenyLowerVtlStartup
DenyLowerVtlStartup 标志控制虚拟处理器是否可以通过较低的 VTL 启动或重置。 这包括重置虚拟处理器(例如 X64 上的 SPI)以及 HvCallStartVirtualProcessor hypercall 的体系结构方法。
InterceptVpStartup
如果设置了 InterceptVpStartup 标志,启动或重置虚拟处理器会生成对更高 VTL 的拦截。
配置较低的 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 都低于自身。 例如,VTL2 将具有此寄存器的两个实例-一个用于 VTL1,另一个实例用于 VTL0。
下面描述了此寄存器的字段。
MbecEnabled
此字段配置是否为较低的 VTL 启用 MBEC。
TlbLocked
此字段锁定下 VTL 的 TLB。 此功能可用于防止较低的 VTL 导致 TLB 失效,这可能会干扰更高的 VTL。 设置此位后,会阻止来自较低 VTL 的所有地址空间刷新请求,直到解除锁定为止。
若要解锁 TLB,更高的 VTL 可以清除此位。 此外,一旦 VP 返回到较低的 VTL,它将释放它当时持有的所有 TLB 锁。
VTL 条目
当 VP 从较低的 VTL 切换到更高的 VTL 时,会“输入”VTL。 这可能发生在以下原因:
- VTL 调用:这是当软件明确希望调用更高 VTL 中的代码时。
- 安全中断:如果为更高的 VTL 收到中断,VP 将进入更高的 VTL。
- 安全拦截:某些作将触发安全中断(例如访问某些 MSR)。
进入 VTL 后,它必须自愿退出。 更高的 VTL 不能被较低的 VTL 抢占。
标识 VTL 条目原因
为了对条目做出适当的反应,更高的 VTL 可能需要知道输入的原因。 为了区分条目原因,VTL 条目包含在 HV_VP_VTL_CONTROL 结构中。
VTL 调用
“VTL 调用”是当较低 VTL 通过 HvCallVtlCall hypercall 启动更高 VTL 的条目(例如,若要保护具有较高 VTL 的内存区域)。
VTL 调用会保留 VTL 交换机之间共享寄存器的状态。 专用寄存器保留在每个 VTL 级别。 这些限制的例外是 VTL 调用序列所需的寄存器。 VTL 调用需要以下寄存器:
| x64 | x86 | Description |
|---|---|---|
| RCX | EDX:EAX | 指定虚拟机监控程序的 VTL 调用控制输入 |
| RAX | ECX | 已预留 |
VTL 调用控制输入中的所有位当前都保留。
VTL 调用限制
VTL 调用只能从最特权的处理器模式启动。 例如,在 x64 系统上,VTL 调用只能来自 CPL0。 从处理器模式启动的 VTL 调用,该模式不是系统上最特权的调用,会导致虚拟机监控程序将 #UD 异常注入虚拟处理器。
VTL 调用只能切换到下一个最高 VTL。 换句话说,如果启用了多个 VTL,则调用无法“跳过”VTL。 以下作会导致 #UD 异常:
- 从处理器模式启动的 VTL 调用,该模式不是系统(特定于体系结构)的最特权调用。
- 来自真实模式的 VTL 调用 (x86/x64)
- 禁用目标 VTL 的虚拟处理器上的 VTL 调用(或尚未启用)。
- 具有无效控件输入值的 VTL 调用
VTL 退出
切换到较低 VTL 的开关称为“return”。 VTL 完成处理后,可以启动 VTL 返回,以便切换到较低的 VTL。 VTL 返回的唯一方法是,如果更高的 VTL 自愿启动一个。 较低的 VTL 永远无法抢占更高的 VTL。
VTL 返回
“VTL 返回”是在较高 VTL 通过 HvCallVtlReturn hypercall 启动进入较低 VTL 时。 与 VTL 调用类似,专用处理器状态已切换,共享状态保持不变。 如果较低的 VTL 显式调用到更高的 VTL,则虚拟机监控程序在返回完成后递增更高的 VTL 指令指针,以便在 VTL 调用之后继续。
VTL 返回代码序列需要使用以下寄存器:
| x64 | x86 | Description |
|---|---|---|
| RCX | EDX:EAX | 指定虚拟机监控程序的 VTL 返回控件输入 |
| RAX | ECX | 已预留 |
VTL 返回控件输入具有以下格式:
| 比特 | 领域 | Description |
|---|---|---|
| 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,则不会还原寄存器(快速返回)。
Hypercall 页面助手
虚拟机监控程序提供机制来帮助通过 Hypercall 页面进行 VTL 调用和返回。 本页抽象化切换 VTL 所需的特定代码序列。
可以通过在 hypercall 页面中执行特定指令来访问执行 VTL 调用和返回的代码序列。 调用/返回区块位于由 HvRegisterVsmCodePageOffsets 虚拟寄存器确定的 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 页面映射到 VTL 的 GPA 空间
- 确定代码序列的正确偏移量(VTL 调用或返回)。
- 使用 CALL 执行代码序列。
内存访问保护
VSM 提供的一项必要保护是能够隔离内存访问。
较高的 VTL 可以高度控制较低 VTL 允许的内存访问类型。 有三种基本类型的保护可由特定 GPA 页面的更高 VTL 指定:读取、写入和 eXecute。 下表中定义了这些值:
| Name | Description |
|---|---|
| 读取 | 控制是否允许读取访问内存页 |
| 写入 | 控制是否允许对内存页进行写入访问 |
| Execute | 控制是否允许内存页使用指令提取。 |
这三种组合用于以下类型的内存保护:
- 无访问权限
- 只读,不执行
- 只读,执行
- 读/写,不执行
- 读/写,执行
如果启用了“基于模式的执行控制(MBEC)”,则可以单独设置用户和内核模式执行保护。
更高的 VTL 可以通过 HvCallModifyVtlProtectionMask hypercall 为 GPA 设置内存保护。
内存保护层次结构
内存访问权限可由特定 VTL 的多个源设置。 每个 VTL 的权限可能会受到许多其他 VTL 以及主机分区的限制。 应用保护的顺序如下:
- 主机设置的内存保护
- 由较高 VTL 设置的内存保护
换句话说,VTL 保护取代主机保护。 较高级别的 VTL 取代了较低级别的 VTL。 请注意,VTL 本身可能无法设置内存访问权限。
一个符合性接口预计不会覆盖 RAM 上的任何非 RAM 类型。
内存访问冲突
如果在较低 VTL 处运行的 VP 尝试违反由较高 VTL 设置的内存保护,则会生成拦截。 此拦截由设置保护的更高 VTL 接收。 这允许更高的 VTL 逐个处理冲突。 例如,较高的 VTL 可以选择返回错误,或模拟访问。
基于模式的执行控件 (MBEC)
当 VTL 在较低的 VTL 上放置内存限制时,它可能希望在授予“执行”权限时区分用户和内核模式。 例如,如果代码完整性检查发生在更高的 VTL 中,则区分用户模式和内核模式的能力意味着 VTL 只能对内核模式应用程序强制实施代码完整性。
除了传统的三种内存保护(读取、写入、执行),MBEC 还引入了用户模式与内核模式之间的区别,用于执行保护。 因此,如果启用了 MBEC,则 VTL 有机会设置四种类型的内存保护:
| Name | Description |
|---|---|
| 读取 | 控制是否允许读取访问内存页 |
| 写入 | 控制是否允许对内存页进行写入访问 |
| 用户模式执行 (UMX) | 控制是否允许内存页在用户模式下生成的指令提取。 注意:如果禁用 MBEC,则忽略此设置。 |
| 内核模式执行 (KMX) | 控制是否允许内存页在内核模式下生成的指令提取。 注意:如果禁用 MBEC,此设置控制用户模式和内核模式执行访问权限。 |
仅当虚拟处理器在用户模式下运行时,才会使用“User-Mode 执行”保护标记的内存。 同样,仅当虚拟处理器在内核模式下运行时,“Kernel-Mode 执行”内存才可执行。
可以单独设置 KMX 和 UMX,以便以不同的方式在用户和内核模式之间强制实施执行权限。 支持 UMX 和 KMX 的所有组合,但 KMX=1、UMX=0 除外。 此组合的行为未定义。
所有 VTL 和虚拟处理器默认禁用 MBEC。 禁用 MBEC 后,内核模式执行位确定内存访问限制。 因此,如果禁用 MBEC,则 KMX=1 代码在内核和用户模式下都是可执行的。
描述符表
访问描述符表的任何用户模式代码都必须位于标记为 KMX=UMX=1 的 GPA 页面中。 不支持从标记为 KMX=0 的 GPA 页访问描述符表的用户模式软件,并导致常规保护错误。
MBEC 配置
若要使用基于模式的执行控件,必须在两个级别启用它:
- 为分区启用 VTL 后,必须使用 HvCallEnablePartitionVtl 启用 MBEC
- 必须使用 HvRegisterVsmVpSecureConfigVtlX 在每个 VP 和每个 VTL 的基础上配置 MBEC。
MBEC 与监督模式执行防护的交互 (SMEP)
Supervisor-Mode 执行防护(SMEP)是某些平台上支持的处理器功能。 SMEP 可能会影响 MBEC 的作,因为它限制了对内存页的监督访问。 虚拟机监控程序遵循以下与 SMEP 相关的策略:
- 如果 SMEP 不适用于来宾 OS(无论是硬件功能还是处理器兼容性模式),MBEC 将不受影响。
- 如果 SMEP 可用且已启用,则 MBEC 将不受影响。
- 如果 SMEP 可用且已禁用,则所有执行限制都由 KMX 控件控制。 因此,只允许执行标记为 KMX=1 的代码。
虚拟处理器状态隔离
虚拟处理器为每个活动 VTL 维护单独的状态。 但是,某些状态是特定 VTL 的专用状态,其余状态在所有 VTL 之间共享。
每个 VTL (a.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 – DR3
- X87 浮点状态
- XMM 状态
- AVX 状态
- XCR0 (XFEM)
- DR6 (*依赖于处理器类型)。读取 HvRegisterVsmCapabilities 虚拟寄存器以确定共享/专用状态)
真实模式
任何大于 0 的 VTL 都不支持实际模式。 大于 0 的 VCL 可以在 32 位或 64 位模式下运行。
VTL 中断管理
为了在虚拟信任级别之间实现高度隔离,虚拟安全模式为虚拟处理器上启用的每个 VTL 提供单独的中断子系统。 这可确保 VTL 能够发送和接收中断,而不会受到不太安全的 VTL 干扰。
每个 VTL 都有自己的中断控制器,仅当虚拟处理器在该特定 VTL 中运行时,该控制器才处于活动状态。 如果虚拟处理器切换 VTL 状态,也会切换处理器上的中断控制器。
以 VTL 为目标且高于活动 VTL 的中断将导致直接的 VTL 交换机。 然后,更高的 VTL 可以接收中断。 如果较高的 VTL 由于其 TPR/CR8 值而无法接收中断,则中断将保留为“挂起”,并且 VTL 不会切换。 如果有多个具有挂起中断的 VTL,则最高 VTL 优先(不通知较低的 VTL)。
当中断以较低的 VTL 为目标时,在虚拟处理器下次转换为目标 VTL 之前不会传递中断。 在启用了更高 VTL 的虚拟处理器上,将删除以较低 VTL 为目标的 INIT 和启动 IPIs。 由于 INIT/SPI 被阻止,因此应使用 HvCallStartVirtualProcessor hypercall 来启动处理器。
RFLAGS。如果
出于切换 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 设施的状态将更改为“Asserted”。 将中断结束发送到与 VINA 设施关联的 SINT 不会清除“断言”状态。 断言状态只能通过以下两种方式之一进行清除:
- 可以通过写入 HV_VP_VTL_CONTROL 结构的 VinaAsserted 字段手动清除状态。
- 如果在 HvRegisterVsmVina 寄存器中启用了“VTL 上的自动重置”选项,则会在 VTL 的下一个条目中自动清除状态。
这允许在安全 VTL 上运行的代码仅收到针对较低 VTL 收到的第一个中断的通知。 如果安全 VTL 希望收到其他中断的通知,它可以清除 VP 辅助页的 VinaAsserted 字段,并会收到下一个新中断的通知。
安全拦截
虚拟机监控程序允许更高的 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、HvX64RegisterCrInterceptCr4Mask 和 HvX64RegisterCrInterceptIa32MiscEnableMask。
DMA 和设备
设备实际上具有与 VTL0 相同的特权级别。 启用 VSM 后,所有设备分配的内存都标记为 VTL0。 任何 DMA 访问都具有与 VTL0 相同的权限。