Share via


仮想保護モード

仮想セキュア モード (VSM) は、ホスト パーティションとゲスト パーティションに提供される一連のハイパーバイザー機能と対応機能であり、オペレーティング システム ソフトウェア内の新しいセキュリティ境界の作成と管理を可能にします。 VSM は、Device Guard、Credential Guard、仮想 TPM、シールドされた VM などのWindowsセキュリティ機能が基になっているハイパーバイザー機能です。 これらのセキュリティ機能は、Windows 10とWindows Server 2016で導入されました。

VSM を使用すると、ルート パーティションとゲスト パーティション内のオペレーティング システム ソフトウェアで、システム セキュリティ資産のストレージと処理用に分離されたメモリ領域を作成できます。 これらの分離リージョンへのアクセスは、ハイパーバイザーを通じてのみ制御および付与されます。ハイパーバイザーは、システムの信頼されたコンピューティング ベース (TCB) の高い特権と信頼性の高い部分です。 ハイパーバイザーはオペレーティング システム ソフトウェアよりも高い特権レベルで実行され、システム初期化の早い段階で CPU MMU や IOMMU のメモリ アクセス許可制御などの主要なシステム ハードウェア リソースを排他的に制御できるため、ハイパーバイザーは、監視モード アクセス (つまり CPL0 など) を使用するオペレーティング システム ソフトウェア (OS カーネルやデバイス ドライバーなど) からでも、これらの分離されたリージョンを不正アクセスから保護できます。 または "リング 0")。

このアーキテクチャでは、スーパーバイザ モードで実行されている通常のシステム レベルのソフトウェア (カーネル、ドライバーなど) が悪意のあるソフトウェアによって侵害された場合でも、ハイパーバイザーによって保護された分離リージョン内の資産はセキュリティで保護されたままになります。

仮想信頼レベル (VTL)

VSM は、仮想信頼レベル (VTL) を使用して分離を実現し、維持します。 VTL は、パーティションごとおよび仮想プロセッサごとに有効にされ、管理されます。

仮想信頼レベルは階層構造であり、より高いレベルの方が低いレベルよりも特権が高くなります。 VTL0 は最小の特権レベルであり、VTL1 は VTL0 よりも特権が高く、VTL2 は VTL1 よりも特権が高くなります。

アーキテクチャ上、最大 16 レベルの VTL がサポートされています。ただし、ハイパーバイザーは 16 VTL 未満の実装を選択できます。 現在、実装されている VTL は 2 つだけです。

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 Protections

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 アドレス レジスタ名 説明
0x000D0006 HV_X64_REGISTER_VSM_CAPABILITIES VSM 機能に関するレポート。

レジスタ VSM 機能 MSR の形式は次のとおりです。

Bits 説明 属性
63 Dr6Shared Read
62:47 MbecVtlMask Read
46 DenyLowerVtlStartup Read
45:0 RsvdZ Read

Dr6Shared は、Dr6 が VTL 間の共有レジスタであるかどうかをゲストに示します。

MvecVtlMask は、Mbec を有効にできる VTL をゲストに示します。

DenyLowerVtlStartup は、Vtl が低い VTL による VP リセットを拒否できるかどうかをゲストに示します。

VSM Status Register

パーティション特権フラグに加えて、2 つの仮想レジスタを使用して、VSM の状態 HvRegisterVsmPartitionStatusHvRegisterVsmVpStatusに関する追加情報を学習できます。

HvRegisterVsmPartitionStatus

HvRegisterVsmPartitionStatus は、すべての VTL で共有されるパーティションごとの読み取り専用レジスタです。 このレジスタは、パーティションに対して有効になっている 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、アクティブな 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 Enablement

VTL の使用を開始するには、低い VTL で次を開始する必要があります。

  1. パーティションのターゲット VTL を有効にします。 これにより、VTL がパーティションで一般提供されます。
  2. 1 つ以上の仮想プロセッサでターゲット VTL を有効にします。 これにより、VTL が VP で使用できるようになり、その初期コンテキストが設定されます。 すべての VP で同じ VTL が有効にすることをお勧めします。 一部の VP (すべてではない) で VTL を有効にすると、予期しない動作が発生する可能性があります。
  3. パーティションと VP に対して VTL が有効になると、EnableVtlProtection フラグが設定されると、アクセス保護の設定を開始できます。

VTL は連続する必要はありません。

パーティションのターゲット VTL の有効化

HvCallEnablePartitionVtl ハイパーコールは、特定のパーティションに対して VTL を有効にするために使用されます。 ソフトウェアを特定の VTL で実際に実行する前に、パーティション内の仮想プロセッサで VTL を有効にする必要があることに注意してください。

仮想プロセッサのターゲット VTL の有効化

パーティションに対して VTL を有効にすると、そのパーティションの仮想プロセッサで VTL を有効にすることができます。 HvCallEnableVpVtl ハイパーコールを使用して、初期コンテキストを設定する仮想プロセッサの VTL を有効にすることができます。

仮想プロセッサには、VTL ごとに 1 つの "コンテキスト" があります。 VTL が切り替わると、VTL の プライベート状態 も切り替わります。

VTL 構成

VTL が有効になると、その構成は、同等以上の VTL で実行されている VP によって変更できます。

パーティション構成

パーティション全体の属性は、HvRegisterVsmPartitionConfig レジスタを使用して構成できます。 このレジスタのインスタンスは、各パーティションの VTL (0 より大きい) ごとに 1 つ存在します。

すべての 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 フラグを設定する必要があります。 このフラグは write-once です。つまり、設定後は変更できません。

既定の保護マスク

既定では、システムは現在マップされているすべてのページと今後の "ホット追加" ページに RWX 保護を適用します。 ホットに追加されたページは、サイズ変更操作中にパーティションに追加されるすべてのメモリを参照します。

より高い VTL では、HV_REGISTER_VSM_PARTITION_CONFIGで DefaultVtlProtectionMask を指定することで、別の既定のメモリ保護ポリシーを設定できます。 このマスクは、VTL が有効になっている時点で設定する必要があります。 一度設定した後は変更できず、パーティションのリセットによってのみクリアされます。

ビット 説明
0 Read
1 Write
2 カーネル モード実行 (KMX)
3 ユーザー モード実行 (UMX)

リセット時にメモリが 0 個

ZeroMemOnReset は、パーティションがリセットされる前にメモリがゼロかどうかを制御するビットです。 この構成は既定でオンになっています。 ビットが設定されている場合、より高い VTL のメモリが低い VTL によって侵害されないように、リセット時にパーティションのメモリはゼロになります。 このビットがクリアされた場合、パーティションのメモリはリセット時にゼロになりません。

DenyLowerVtlStartup

DenyLowerVtlStartup フラグは、仮想プロセッサを下位の VTL で開始またはリセットできるかどうかを制御します。 これには、仮想プロセッサ (X64 の SIPI など) と HvCallStartVirtualProcessor ハイパーコールをリセットするアーキテクチャの方法が含まれます。

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 には、このレジスタの 2 つのインスタンスがあります。1 つは VTL1 用、もう 1 つは VTL0 の場合です。

このレジスタのフィールドを以下に示します。

MbecEnabled

このフィールドは、下位 VTL に対して MBEC を有効にするかどうかを構成します。

TlbLocked

このフィールドは、下位 VTL の TLB をロックします。 この機能を使用すると、低い VTL が TLB 無効化を引き起こして、より高い VTL に干渉する可能性があります。 このビットを設定すると、ロックが解除されるまで、下位 VTL からのすべてのアドレス空間フラッシュ要求がブロックされます。

TLB のロックを解除するには、高い VTL がこのビットをクリアできます。 また、VP が低い VTL に戻ると、その時点で保持されているすべての TLB ロックが解放されます。

VTL エントリ

VTL は、VP が低い VTL から上位の VTL に切り替わるときに "入力" されます。 これが発生する理由としては次のようなことが考えられます。

  1. VTL 呼び出し: これは、ソフトウェアが高い VTL でコードを明示的に呼び出す場合です。
  2. 安全な割り込み: より高い VTL に対して割り込みを受信した場合、VP は上位の VTL に入ります。
  3. セキュリティで保護されたインターセプト: 特定のアクションによって、セキュリティで保護された割り込みがトリガーされます (たとえば、特定の MSR にアクセスします)。

VTL が入力されたら、自発的に終了する必要があります。 より高い VTL を低い VTL で割り込むことはできません。

VTL エントリの理由の特定

エントリに適切に対応するには、より高い VTL が入力された理由を知る必要がある場合があります。 エントリの理由を識別するために、VTL エントリは HV_VP_VTL_CONTROL 構造に含まれています。

VTL 呼び出し

"VTL 呼び出し" は、下位の VTL が HvCallVtlCall ハイパーコールを介して上位 VTL へのエントリを開始する場合です (たとえば、より高い VTL でメモリの領域を保護するため)。

VTL 呼び出しでは、VTL スイッチ間で共有レジスタの状態が保持されます。 プライベート レジスタは VTL レベルごとに保持されます。 これらの制限の例外は、VTL 呼び出しシーケンスで必要なレジスタです。 VTL 呼び出しには、次のレジスタが必要です。

X64 x86 説明
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 Exit

下位 VTL への切り替えは、"戻り値" と呼ばれます。 VTL の処理が完了すると、VTL の戻り値を開始して、より低い VTL に切り替えることができます。 VTL リターンが発生する唯一の方法は、より高い VTL が自発的に VTL を開始する場合です。 VTL が低いほど、より高い VTL を割り込むことはありません。

VTL Return

"VTL 戻り値" は、より高い VTL が HvCallVtlReturn ハイパーコールを介してより低い 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 に設定されている場合、レジスタは復元されません (高速リターン)。

Hypercall ページ の支援

ハイパーバイザーは、VTL 呼び出しを支援し、 ハイパーコール ページを介して返すメカニズムを提供します。 このページでは、VTL の切り替えに必要な特定のコード シーケンスを抽象化します。

VTL 呼び出しを実行して返すコード シーケンスには、ハイパーコール ページで特定の命令を実行することでアクセスできます。 呼び出し/戻りチャンクは、HvRegisterVsmCodePageOffset 仮想レジスタによって決定されるハイパーコール ページ内のオフセットにあります。 これは読み取り専用でパーティション全体のレジスタであり、VTL ごとに個別のインスタンスがあります。

VTL は、CALL 命令を使用して VTL 呼び出し/戻り値を実行できます。 ハイパーコール ページ内の正しい場所への呼び出しによって、VTL 呼び出し/戻りが開始されます。

typedef union
{
    UINT64 AsUINT64;
    struct
    {
        UINT64 VtlCallOffset : 12;
        UINT64 VtlReturnOffset : 12;
        UINT64 ReservedZ : 40;
    };
} HV_REGISTER_VSM_CODE_PAGE_OFFSETS;

まとめると、ハイパーコール ページを使用してコード シーケンスを呼び出す手順は次のとおりです。

  1. ハイパーコール ページを VTL の GPA 空間にマップする
  2. コード シーケンスの正しいオフセットを決定します (VTL 呼び出しまたは戻り値)。
  3. CALL を使用してコード シーケンスを実行します。

メモリ アクセス保護

VSM によって提供される保護の 1 つは、メモリ アクセスを分離する機能です。

VTL が高いほど、下位の VTL で許容されるメモリ アクセスの種類を高度に制御できます。 特定の GPA ページの上位 VTL で指定できる保護には、読み取り、書き込み、eXecute の 3 種類があります。 これらは次の表で定義されています。

名前 説明
Read メモリ ページへの読み取りアクセスを許可するかどうかを制御します
Write メモリ ページへの書き込みアクセスを許可するかどうかを制御します
実行 メモリ ページに対して命令フェッチを許可するかどうかを制御します。

これら 3 つの組み合わせにより、次の種類のメモリ保護が行われます。

  1. アクセス権なし
  2. 読み取り専用、実行なし
  3. 読み取り専用、実行
  4. 読み取り/書き込み、実行なし
  5. 読み取り/書き込み、実行

"モード ベースの実行制御 (MBEC)" が有効になっている場合は、ユーザー モードとカーネル モードの実行保護を個別に設定できます。

上位の VTL では、 HvCallModifyVtlProtectionMask ハイパーコールを使用して GPA のメモリ保護を設定できます。

メモリ保護階層

メモリ アクセス許可は、特定の VTL の多数のソースによって設定できます。 各 VTL のアクセス許可は、他のいくつかの VTL とホスト パーティションによって制限される可能性があります。 保護を適用する順序は次のとおりです。

  1. ホストによって設定されたメモリ保護
  2. 上位の VTL によって設定されるメモリ保護

言い換えると、VTL 保護はホスト保護よりも優先されます。 上位レベルの VTL は、下位レベルの VTL よりも優先されます。 VTL では、それ自体のメモリ アクセス許可が設定されない場合があることに注意してください。

準拠インターフェイスは、RAM 以外の種類を RAM にオーバーレイしないことが想定されます。

メモリ アクセス違反

低い VTL で実行されている VP が、より高い VTL によって設定されたメモリ保護に違反しようとすると、インターセプトが生成されます。 このインターセプトは、保護を設定する上位の VTL によって受信されます。 これにより、上位の VTL がケース バイ ケースで違反に対処できるようになります。 たとえば、上位の VTL では、障害を返すか、アクセスをエミュレートするかを選択できます。

モード ベースの実行コントロール (MBEC)

VTL が下位 VTL にメモリ制限を設ける場合、"実行" 権限を付与するときに、ユーザーモードとカーネル モードを区別することが望ましい場合があります。 たとえば、コード整合性チェックが高い VTL で行われる場合、ユーザー モードとカーネル モードを区別する機能は、VTL がカーネル モード アプリケーションのみにコード整合性を適用できることを意味します。

MBEC では、従来の 3 つのメモリ保護 (読み取り、書き込み、実行) とは別に、実行保護のユーザー モードとカーネル モードの区別が導入されています。 したがって、MBEC が有効になっている場合、VTL は 4 種類のメモリ保護を設定できます。

名前 説明
Read メモリ ページへの読み取りアクセスを許可するかどうかを制御します
Write メモリ ページへの書き込みアクセスを許可するかどうかを制御します
ユーザー モード実行 (UMX) ユーザー モードで生成された命令フェッチをメモリ ページに対して許可するかどうかを制御します。 注: MBEC が無効になっている場合、この設定は無視されます。
カーネル モード実行 (UMX) カーネル モードで生成された命令フェッチをメモリ ページに対して許可するかどうかを制御します。 注: MBEC が無効になっている場合、この設定はユーザー モードとカーネル モードの両方の実行アクセスを制御します。

"ユーザー モード実行" 保護でマークされたメモリは、仮想プロセッサがユーザー モードで実行されている場合にのみ実行可能です。 同様に、"カーネル モード実行" メモリは、仮想プロセッサがカーネル モードで実行されている場合にのみ実行可能です。

KMX と UMX は、ユーザー モードとカーネル モードで実行アクセス許可が異なる方法で適用されるように、個別に設定できます。 KMX=1、UMX=0 を除き、UMX と KMX のすべての組み合わせがサポートされています。 この組み合わせの動作は未定義です。

MBEC は、すべての VTL と仮想プロセッサに対して既定で無効になっています。 MBEC を無効にすると、カーネル モード実行ビットによってメモリ アクセス制限が決定されます。 したがって、MBEC が無効になっている場合、KMX=1 コードはカーネル モードとユーザー モードの両方で実行可能です。

記述子テーブル

記述子テーブルにアクセスするユーザー モード コードは、KMX=UMX=1 としてマークされた GPA ページに存在する必要があります。 KMX=0 とマークされた GPA ページから記述子テーブルにアクセスするユーザー モード ソフトウェアはサポートされていないため、一般的な保護エラーが発生します。

MBEC 構成

モード ベースの実行制御を使用するには、次の 2 つのレベルで有効にする必要があります。

  1. パーティションに対して VTL が有効になっている場合は、HvCallEnablePartitionVtl を使用して MBEC を有効にする必要があります
  2. MBEC は、HvRegisterVsmVpSecureVtlConfig を使用して、VP ごとおよび VTL 単位で構成する必要があります。

スーパーバイザ モード実行防止 (SMEP) との MBEC 対話

Supervisor-Mode実行防止 (SMEP) は、一部のプラットフォームでサポートされているプロセッサ機能です。 SMEP は、メモリ ページへのスーパーバイザー アクセスの制限により、MBEC の動作に影響を与える可能性があります。 ハイパーバイザーは、SMEP に関連する次のポリシーに準拠しています。

  • SMEP がゲスト OS で使用できない場合 (ハードウェア機能またはプロセッサ互換モードが原因であるかどうかに関係なく)、MBEC は影響を受けません。
  • SMEP が使用可能で、有効になっている場合、MBEC は影響を受けません。
  • SMEP が使用可能で無効になっている場合、すべての実行制限は KMX コントロールによって制御されます。 したがって、KMX=1 とマークされたコードのみが実行できます。

仮想プロセッサの状態の分離

仮想プロセッサは、アクティブな VTL ごとに個別の状態を維持します。 ただし、この状態の一部は特定の VTL に対してプライベートであり、残りの状態はすべての VTL 間で共有されます。

VTL ごとに保持される状態 (プライベート状態など) は、VTL 遷移間でハイパーバイザーによって保存されます。 VTL スイッチが開始された場合、ハイパーバイザーはアクティブ VTL の現在のプライベート状態を保存し、ターゲット VTL のプライベート状態に切り替えます。 共有状態は、VTL スイッチに関係なくアクティブなままです。

プライベート状態

一般に、各 VTL には、独自の制御レジスタ、RIP レジスタ、RSP レジスタ、および MDR があります。 各 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 スイッチが発生します。 その後、より高い VTL が割り込みを受け取ることができます。 TPR/CR8 値が原因で高い VTL が割り込みを受信できない場合、割り込みは "保留中" として保持され、VTL は切り替わりません。 保留中の割り込みがある VTL が複数ある場合は、最も高い VTL が優先されます (低い VTL には通知されません)。

割り込みが低い VTL を対象とする場合、次に仮想プロセッサがターゲット VTL に移行するまで割り込みは配信されません。 より低い VTL を対象とする INIT とスタートアップ IPI は、より高い VTL が有効になっている仮想プロセッサ上で削除されます。 INIT/SIPI がブロックされているため、 HvCallStartVirtualProcessor ハイパーコールを使用してプロセッサを起動する必要があります。

RFLAGS。もし

VTL を切り替える目的で、RFLAGS。IF は、セキュリティで保護された割り込みが VTL スイッチをトリガーするかどうかに影響しません。 RFLAGS の場合。割り込みをマスクするために IF がクリアされた場合でも、上位の VTL への割り込みにより、VTL スイッチが高い VTL に切り替わります。 直ちに割り込むかどうかを決定する際には、より高い VTL の TPR/CR8 値のみが考慮されます。

この動作は、VTL リターン時の保留中の割り込みにも影響します。 RFLAGS の場合。特定の 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 の独自のバージョンがあります。 VINA 機能は、下位 VTL の割り込みが即時配信の準備ができたときに、現在アクティブな上位 VTL に対してエッジによってトリガーされる割り込みを生成します。

この機能が有効になっているときに中断が大量に発生するのを防ぐために、VINA 機能にはいくつかの制限された状態が含まれています。 VINA 割り込みが生成されると、VINA 機能の状態が "Asserted" に変更されます。 VINA 機能に関連付けられている SINT に割り込みの終了を送信しても、"Asserted" 状態はクリアされません。 アサートされた状態は、次の 2 つの方法のいずれかでのみクリアできます。

  1. 状態は、 HV_VP_VTL_CONTROL 構造体の VinaAsserted フィールドに書き込むことで手動でクリアできます。
  2. HvRegisterVsmVina レジスタで "VTL エントリの自動リセット" オプションが有効になっている場合、VTL の次のエントリで状態が自動的にクリアされます。

これにより、セキュリティで保護された VTL で実行されているコードは、より低い VTL に対して受信された最初の割り込みについての通知のみを受け取ることができます。 セキュリティで保護された VTL が追加の割り込みを通知する場合は、VP アシスト ページの VinaAsserted フィールドをクリアすると、次の新しい割り込みが通知されます。

セキュリティで保護されたインターセプト

ハイパーバイザーを使用すると、より高い VTL で、より低い VTL のコンテキストで発生するイベントのインターセプトをインストールできます。 これにより、VTL が高いほど、低い VTL リソースに対する制御レベルが高くなります。 セキュリティで保護されたインターセプトを使用して、システムクリティカルなリソースを保護し、下位 VTL からの攻撃を防ぐことができます。

セキュリティで保護されたインターセプトは、上位の VTL にキューに登録され、その VTL は VP で実行可能になります。

セキュリティで保護されたインターセプトの種類

インターセプトの種類 インターセプトの適用対象
メモリ アクセス より高い VTL によって確立された GPA 保護にアクセスしようとしています。
レジスタ アクセスの制御 上位 VTL で指定された一連の制御レジスタにアクセスしようとしています。

入れ子になったインターセプト

複数の VTL は、低い VTL で同じイベントに対してセキュリティで保護されたインターセプトをインストールできます。 したがって、入れ子になったインターセプトが通知される場所を決定するために階層が確立されます。 次の一覧は、インターセプトが通知される順序です。

  1. 下位 VTL
  2. より高い VTL

セキュリティで保護されたインターセプトの処理

セキュリティで保護されたインターセプトが VTL に通知されたら、下位の VTL が続行できるようにアクションを実行する必要があります。 VTL が高いほど、例外の挿入、アクセスのエミュレート、アクセスへのプロキシの提供など、さまざまな方法でインターセプトを処理できます。 いずれの場合も、下位 VTL VP のプライベート状態を変更する必要がある場合は、 HvCallSetVpRegisters を使用する必要があります。

セキュアレジスタインターセプト

VTL が高いほど、特定の制御レジスタへのアクセスをインターセプトできます。 これは、 HvCallSetVpRegisters ハイパーコールを使用して 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 と同じ特権があります。