AMD-V/SVM における Nested Paging について、有効化方法や具体的な設定方法をメモ。なお、ここで記載する内容は AMD64 Architecture Programmer’s Manual, Volume 2 (以下、APM, Vol.2) の「15.25 Nested Paging」にすべて書いてあるので詳細はそちらを参照すること。
さっそく Nested Paging についての説明をドキュメントより引用。
The optional SVM nested paging feature provides for two levels of address translation, thus eliminating the need for the VMM to maintain shadow page tables.
CPU によるこういう支援機能があるよという話で、これを使わないと VMM 側で「ゲスト物理アドレス → ホスト物理アドレス」の変換をエミュレーションする必要性が出てくる。なお、Nested Paging は Intel VT-x でいうところの EPT (Extended Page Table) にあたるもの。
こちらが Nested Paging を有効化した際のアドレス変換の様子 (APM, Vol.2, 15.25.1 Traditional Paging versus Nested Paging より引用)。
つまり、ゲストの仮想アドレスは以下の2段階を経てホストの物理アドレスに変換される。そして、この変換結果は TLB にキャッシュされる。
- ゲストの仮想アドレス → (Guest page tables (gPT)) → ゲストの物理アドレス
- ゲストの物理アドレス → (Nested page tables (nPT)) → ホストの物理アドレス
Nested Paging を有効にした場合、ページングに関わる各種レジスタ (CR0, CR3, CR4, EFER and PAT) はホスト用とゲスト用でそれぞれ別々のものが用意される。それぞれについて、VMRUN/#VMEXIT 発生時の動きは以下の通り。
- ゲスト用レジスタ:
- VMRUN: VMCB の各フィールド (CR0, CR3, CR4, EFER and G_PAT) からゲスト用の各レジスタに値がロードされる。つまり、上記の図でいうところの gCR3 には VMCB の CR3 フィールドからロードされる。
- #VMEXIT: 各レジスタの値が VMCB の各フィールドに書き戻される。
- ホスト用レジスタ:
- VMRUN: もともと CR3 に格納されていた値が host state-save area に退避され、VMCB の N_CR3 フィールドから新たにロードされる。つまり、上記の図の nCR3 がここに該当する。CR3 以外のレジスタについては VMRUN 実行前に格納されていた値がそのまま残る。
- #VMEXIT: host state-save area から CR3 の値が reload される。#VMEXIT 発生時点の CR3 (nCR3) は VMCB に書き戻されない。
では、具体的な設定方法などについて。Nested Paging を有効化するには VMCB の NP_ENABLE フィールドを 1 にセットする必要がある。そのうえで、VMCB の N_CR3 フィールドに「ゲスト物理アドレス → ホスト物理アドレス」変換をするためのページテーブル nPT のベースアドレスをセットする。その際、このページテーブルは誰も用意してくれないので VMM 側で用意する必要がある。雑ーーな疑似コードを書くとこんなイメージ。
uint64_t *npt = create_nested_page_table();
vmcb->np_enalbe = 1;
vmcb->n_cr3 = (uintptr_t)npt
...
vmrun();