Protected KVM (pKVM)

備考: pKVM は現在、実験的で開発中の機能であり、新しい分離機能が 実装されるにつれて、互換性を壊す変更が入る可能性がある。 質問がある場合は、kvmarm@lists.linux.dev の開発者に連絡してほしい。

概要

ホストカーネルを 'kvm-arm.mode=protected' で起動すると、「Protected KVM」(pKVM) が有効になる。起動中に pKVM はホスト用のステージ 2 の アイデンティティマップページテーブルを設定し、それを使って EL2 で動く ハイパーバイザを、EL1/0 で動くホストの残りの部分から分離する。pKVM は GICv3 だけをサポートする。保護モードでは GICv2 はサポートされず、 GICv5 ベースのゲストもサポートされない。

pKVM では、KVM_CREATE_VM ioctl() に KVM_VM_TYPE_ARM_PROTECTED というマシンタイプ識別子を渡すことで、保護仮想マシン (pVM) を作成できる。 ハイパーバイザは、pVM がページにアクセスするたびにそのページをステージ 2 のアイデンティティマップからアンマップして、pVM をホストから分離する。 pVM が自分の IPA 空間の特定の領域をホストと共有して VMM と通信できるよう、 ハイパーコールが用意されている。Linux ゲストは、これらのハイパーコールを 発行するために CONFIG_ARM_PKVM_GUEST=y で設定されている必要がある。

詳細は hypercalls.rst を参照。

分離の仕組み

pKVM は、pVM をホストから分離するためにいくつかの仕組みに依存している。

CPU メモリの分離

状態: 匿名メモリとメタデータページの分離。

メタデータページ (ページテーブルページや 'struct kvm_vcpu' ページ など) は pVM の作成時にホストからハイパーバイザへ寄付され、その結果、 pVM が破棄されるまでステージ 2 のアイデンティティマップからアンマップ される。

通常の KVM と同様、ページはホストが処理するステージ 2 のページフォールト に応じて、遅延してゲストにマップされる。ただし pVM を実行するときは、 これらのページはまずピン留めされ、寄付の手順の一部としてステージ 2 の アイデンティティマップからアンマップされる。このため、保護されていない VM と比べていくつかユーザから見える違いが生じる。その主な原因は MMU ノーティファイアがないことである。

  • pVM の実行が始まると、メモリスロットは移動も削除もできない (-EPERM)。

  • 読み取り専用のメモリスロットとダーティログはサポートされない (-EPERM)。

  • スワップを除き、ファイルにバッキングされたページを pVM にマップする ことはできない。

  • 寄付されたページは RLIMIT_MLOCK に対して計上されるため、VMM は 十分なリソース制限を持つか、CAP_IPC_LOCK を付与されていなければ ならない。実行時にメモリを回収する仕組みがないため、pVM のために ロックされたメモリは pVM が破棄されるまでロックされたままになる。

  • VMM のアドレス空間への変更 (たとえばメモリスロットに対応するマッピング の上への MAP_FIXED mmap() ) はゲストに反映されず、一貫性が失われる おそれがある。

  • 共有し返されていない pVM のメモリにアクセスすると、SIGSEGV が配送 される。

  • システムコールが共有し返されていない pVM のメモリにアクセスすると、 -EFAULT を返すか、そのメモリページを強制的に回収する。回収された メモリはハイパーバイザによってゼロ埋めされ、その後 pVM でそのメモリに アクセスしようとすると KVM_RUN ioctl() から -EFAULT が返る。

CPU 状態の分離

状態: 保護 vCPU の CPU レジスタ状態は完全に EL2 で管理される。

pKVM は保護 vCPU のコンテキストスイッチをハイパーバイザ内で完全に行う。 保護 vCPU の初期状態を設定するのはホストではなくハイパーバイザであり、 各エグジットで必要となるものだけが同期し戻される。

ユーザから見える影響は 保護 VM の API の振る舞い で説明する。

IOMMU を使った DMA の分離

状態: 未実装。

Trustzone サービスのプロキシ

状態: ホストからの FF-A 呼び出しと PSCI 呼び出しは pKVM ハイパーバイザが 代理する。

FF-A プロキシにより、ホストは「confused deputy」攻撃の一環として pVM や ハイパーバイザのメモリを Trustzone と共有できない。PSCI プロキシにより、 CPU がホストで実行しているときは常にステージ 2 のアイデンティティマップ が設定されている。このプロキシは、保護ゲストに提供される PSCI の扱いとは 別であり、そちらは 保護 VM の API の振る舞い で説明する。

保護 VM ファームウェア (pvmfw)

状態: 未実装。

保護 VM の API の振る舞い

保護 vCPU の状態は EL2 が所有する (CPU 状態の分離 を参照)。VMM は 最初の KVM_RUN より前に vCPU を設定する。その後は状態はゲスト専用に なり、それにアクセスする ioctl は -EPERM を返す。vCPU の ioctl の エラーは一つの規則に従う。-EPERM はホストがゲストの所有する状態を 求めたことを意味し、-EINVAL はその要求が保護 VM に対して有効でない ことを意味する。また pKVM が保護 VM に対してサポートしない機能は、KVM が 実装されていない機能を拒否するのと同じように拒否される (たとえば KVM_ARM_VCPU_PVTIME_CTRL からの -ENXIO)。ioctl 自体については Documentation/virt/kvm/api.rst で説明する。

起動

保護 VM は単一の主 vCPU から起動する。最初の KVM_RUN より前に、 VMM は起動状態を用意する。

  • 主 vCPU に KVM_MP_STATE_RUNNABLE を、それ以外の vCPU に KVM_MP_STATE_STOPPED を設定する。EL2 は保護 VM ごとに RUNNABLE な 主 vCPU を一つだけ許す。二つ目の RUNNABLE な vCPU は最初の KVM_RUN で失敗する。

  • KVM_SET_ONE_REG で主 vCPU の起動状態を設定する。PC にカーネルの エントリポイントのアドレスを、x0 に DTB のポインタを入れる。

EL2 がホストから受け取るレジスタは PC と x0 だけである。実行前の その他の書き込みは受け付けられるが、ゲストはハイパーバイザがリセットした 状態から起動する。

副 vCPU はゲスト自身が PSCI CPU_ON で起動する (電源状態 を参照)。 CPU_ON がエントリポイントとコンテキスト ID を与える。VMM はそれらが どこで起動するかを選べない。

vCPU の状態

  • vCPU が一度実行されると、KVM_GET_ONE_REG と KVM_SET_ONE_REG は -EPERM を返す。それ以前は、EL2 がゲストの起動状態を組み立てる元に なるホスト側のコピーにアクセスする (起動 を参照)。

  • KVM_ARM_VCPU_INIT は保護ゲストがサポートする vCPU の機能だけを 受け付け、それ以外には -EINVAL を返す。KVM_ARM_VCPU_PSCI_0_2 は 必須である。EL2 がゲスト向けに PSCI 1.1 を実装しているためである (電源状態 を参照)。KVM_ARM_VCPU_EL1_32BIT はサポートされない。 保護ゲストは AArch64 だけで動き、ID_AA64PFR0_EL1 に AArch32 の サポートは見えない。vCPU が一度実行されると KVM_ARM_VCPU_INIT は -EPERM を返す。再初期化してもホスト側のコピーだけがリセットされる ためである。

  • KVM_SET_VCPU_EVENTS は外部アボートの注入 (ext_dabt_pending) に対して -EPERM を返し、KVM_CAP_ARM_INJECT_EXT_DABT は保護 VM には提供されない。SError の注入は影響を受けない。

  • KVM_SET_GUEST_DEBUG は -EPERM を返し、 KVM_CAP_SET_GUEST_DEBUG は保護 VM には提供されない (デバッグ を 参照)。

電源状態

EL2 は保護ゲスト向けに PSCI 1.1 を実装している。vCPU の電源状態を変える 呼び出し CPU_ON と CPU_OFF は EL2 で処理され、ホストはその結果を 変えられない。CPU_ON では、ホストは対象のスケジュールを行うだけであり、 その対象は EL2 によってすでにゲストが選んだエントリポイントにリセット されている。CPU_OFF では、ホストはスケジュールを止めるだけである。 AFFINITY_INFO はホストが処理し、問い合わせが対象とするすべての vCPU の スケジュールを止めると OFF を返す。これは保護されていない VM と同じで ある。vCPU は最初の KVM_RUN で有効な CPU_ON の対象になり、それ より前は EL2 が INVALID_PARAMETERS を返す。プラットフォーム呼び出し CPU_SUSPEND、SYSTEM_OFF、SYSTEM_RESET、SYSTEM_RESET2 は ホストへ転送され、保護されていない VM と同じように動作する。SYSTEM_* の呼び出しは KVM_EXIT_SYSTEM_EVENT として VMM へエグジットする。 SYSTEM_RESET2 のリセットタイプとクッキーはゲストのレジスタにあり、 vCPU が一度実行されると KVM_GET_ONE_REG はそれらを拒否する。その他の 関数は NOT_SUPPORTED を返し、PSCI_FEATURES も同じ集合を報告する。 転送された呼び出しはホストが処理するため、KVM_REG_ARM_PSCI_VERSION への KVM_SET_ONE_REG は 1.1 未満のバージョンに対して -EINVAL を 返す。

vCPU が一度実行されると、その電源状態は VMM の PSCI 呼び出しではなく、 ゲストの PSCI 呼び出しに従う。ゲストが CPU_OFF で電源を切った vCPU、 またはまだ CPU_ON でオンラインにしていない vCPU は KVM_MP_STATE_UNINITIALIZED として読め、ゲスト内の CPU_ON だけが その電源を入れられる。そのような vCPU に対して KVM_SET_MP_STATE は KVM_MP_STATE_RUNNABLE と KVM_MP_STATE_SUSPENDED には -EPERM を返し、KVM_MP_STATE_STOPPED は受け付けてそのままにし、 KVM_MP_STATE_UNINITIALIZED を VMM から受け取ることはない。 KVM_MP_STATE_STOPPED は EL2 が電源を入れた vCPU を停止させ、 KVM_MP_STATE_RUNNABLE はそれを再開させるので、VMM は vCPU を一時停止 できる。

その他のインターフェースの違い

  • KVM_CHECK_EXTENSION は pKVM が保護ゲストに対してサポートする arm64 のケイパビリティだけを報告し、KVM_ENABLE_CAP もそれらだけを 受け付ける。VM のファイルディスクリプタで問い合わせる。システムの ファイルディスクリプタには絞り込む対象の VM がない。汎用の KVM コード が自分で答えるケイパビリティ (たとえば KVM_CAP_SYNC_MMU や KVM_CAP_DEVICE_CTRL) はどの VM に対しても同じように報告され、その 機能が保護ゲストで動くことを意味しない。このフィルタはすべての インターフェースを対象にしているわけでもない。デバイス fd の設定 (たとえば KVM_CREATE_DEVICE の後の VGIC) と vCPU の属性は絞り込まれ ないため、フィルタが除外する機能はその属性自身によって拒否される。 たとえば KVM_ARM_VCPU_PVTIME_CTRL は、steal time が実装されていない 場所と同じように -ENXIO を返す。

  • 保護 VM の vGIC はホストが管理し続ける。デバイスの作成、設定、割り込みの 注入はいずれも保護されていない VM と同じように動作する。

  • KVM_ARM_SET_COUNTER_OFFSET と KVM_ARM_GET_REG_WRITABLE_MASKS は -EINVAL を返す。これらのケイパビリティは保護 VM には提供されず、 保護 VM のカウンタオフセットと ID レジスタは EL2 が設定する。保護ゲスト には物理タイムベースが見える。

  • 保護ゲストが各メモリページに最初にアクセスするとホストへエグジットする。 ページがマップされる前は、ハイパーバイザがメモリとデバイスを区別 できないためである。ストアの場合は、ホストはシンドロームが示す レジスタの値をアクセス幅に合わせて切り詰めたものだけを見て、 レジスタファイルの他のものは見ない。ロードの場合は、シンドロームが示す レジスタがホストの返した値を取る。これはエミュレートされたアクセスと 同じである。

  • 与えられていない機能を使う、または SMC を実行する保護ゲストは、 ハイパーバイザから未定義命令例外を受け取る。ホストは関与しない。

  • PSCI を除き、ハイパーバイザは保護ゲストの SMCCC 呼び出しを自分で処理 し、ホストを関与させない。実装していない関数は NOT_SUPPORTED を 返し、ホストの SMCCC フィルタは適用されない。VM デバイス属性の ioctl (KVM_ARM_VM_SMCCC_CTRL) は保護 VM に対して -EINVAL を返す。

  • ハイパーバイザはトラップされたゲストのアクセスを、CPU の命令シンドローム からのみデコードできる。このシンドロームは、単一の汎用レジスタの ロードまたはストアに対してのみ提供される。シンドロームのないアクセス (たとえばロード/ストアペアや SIMD/FP アクセス) はデコードできない。 保護されていない VM では KVM_EXIT_ARM_NISV として VMM へエグジット できる。保護 VM では、そのアクセスを VMM がエミュレートできないため、 代わりにゲストが同期外部アボートを受け取る。

デバッグ

保護ゲストはハードウェア支援デバッグを利用できない。MDSCR_EL1、 OSLAR_EL1、OSLSR_EL1、OSDLR_EL1、およびブレークポイント 0 と ウォッチポイント 0 は RAZ/WI である。その他のデバッグレジスタへの アクセスは未定義命令例外になる。

参考資料

Quentin Perret 氏による KVM Forum 2022 の講演「Protected KVM on arm64: A technical deep dive」は、その後いくつかの細部が変わっているものの、 pKVM についてさらに学ぶための良い資料であり続けている。

https://www.youtube.com/watch?v=9npebeVFbFw