PPC の KVM 準仮想化インターフェース
PowerPC 上の KVM が動作する基本原則は、カーネル空間のコードをすべて PR=1(ユーザ空間)で実行することである。こうすると特権命令をすべて トラップでき、それに応じてエミュレーションできる。
残念ながらこれが欠点でもある。本来なら別の方法で扱えるはずの特権命令が かなりあり、それらは不要にハイパーバイザへ戻ってしまう。
これを助けるのが PPC PV インターフェースである。特権命令を、ハイパーバイザの 助けを借りて非特権命令に変換する。これにより、私のベンチマークのいくつかでは 仮想化のコストが約 50% 減る。
このインターフェースのコードは arch/powerpc/kernel/kvm* にある。
存在の確認
KVM 上で動作しているかどうかを調べるために、デバイスツリーを利用する。 Linux が KVM 上で動作しているときは、ノード /hypervisor が存在する。 そのノードには値 "linux,kvm" を持つ compatible プロパティがある。
PV 対応の KVM 上で動作していると分かったら、以下に述べるハイパーコールを 使えるようになる。
KVM ハイパーコール
デバイスツリーの /hypervisor ノードの中に 'hypercall-instructions' という プロパティがある。このプロパティには、ハイパーコールを構成する最大 4 個の オペコードが入っている。ハイパーコールを呼ぶには、単にこれらの命令を 呼べばよい。
パラメータは次のとおりである:
レジスタ |
入力 |
出力 |
|---|---|---|
r0 |
volatile |
|
r3 |
第 1 パラメータ |
戻り値 |
r4 |
第 2 パラメータ |
1 番目の出力値 |
r5 |
第 3 パラメータ |
2 番目の出力値 |
r6 |
第 4 パラメータ |
3 番目の出力値 |
r7 |
第 5 パラメータ |
4 番目の出力値 |
r8 |
第 6 パラメータ |
5 番目の出力値 |
r9 |
第 7 パラメータ |
6 番目の出力値 |
r10 |
第 8 パラメータ |
7 番目の出力値 |
r11 |
ハイパーコール番号 |
8 番目の出力値 |
r12 |
volatile |
ハイパーコールの定義は汎用コードで共有される。そのため、KVM ハイパーコール ごとに KVM ベンダコード (42 << 16) との OR を取る必要がある点を除けば、 x86 と powerpc で同じハイパーコール番号が使える。
戻り値は次のとおりである:
コード |
意味 |
|---|---|
0 |
成功 |
12 |
ハイパーコールが実装されていない |
<0 |
エラー |
マジックページ
ハイパーバイザとゲストの間の通信を可能にするため、スーパバイザに見える レジスタ状態の一部を含む新しい共有ページがある。ゲストは KVM ハイパーコール KVM_HC_PPC_MAP_MAGIC_PAGE を使ってこの共有ページをマップできる。
このハイパーコールを発行すると、ゲストは常にマジックページを望みの位置に マップできる。第 1 パラメータは MMU が有効なときの実効アドレスを示す。 第 2 パラメータは、対象に当てはまるなら、リアルモードでのアドレスを示す。 今のところ、常にページを -4096 にマップする。こうすると絶対ロード命令と 絶対ストア命令でアクセスできる。次の命令はマジックページの先頭フィールドを 読む:
ld rX, -4096(0)
このインターフェースは、後でマジックページにレジスタを追加する必要が 生じたときに拡張できるように設計されている。マジックページにフィールドを 追加するなら、ホストがより多くのレジスタを渡せることを示す新しい ハイパーコール機能も定義すること。ホストがその追加機能をサポートする 場合にのみ、それを使うこと。
マジックページのレイアウトは、arch/powerpc/include/uapi/asm/kvm_para.h の struct kvm_vcpu_arch_shared で記述されている。
マジックページの機能
KVM ハイパーコール KVM_HC_PPC_MAP_MAGIC_PAGE でマジックページをマップすると、 2 番目の戻り値がゲストに渡される。この 2 番目の戻り値には、マジックページ内で 利用できる機能のビットマップが入っている。
現在、マジックページには次の拡張が利用できる:
KVM_MAGIC_FEAT_SR
マジックページ内で SR レジスタを読み書きできるようマップする
KVM_MAGIC_FEAT_MAS0_TO_SPRG7
MASn、ESR、PIR、高位の SPRG をマップする
マジックページの拡張機能を使う前に、その機能が存在するかを確認すること!
マジックページのフラグ
ホストが特定の機能に対応しているかを示す機能フラグに加えて、ゲストが 何かに対応しているかをホストに伝えるチャネルもある。これを「フラグ」と呼ぶ。
フラグは実効アドレスの下位 12 ビットでホストに渡される。
現在、ゲストが公開できるフラグは次のとおりである:
MAGIC_PAGE_FLAG_NOT_MAPPED_NX ゲストがマジックページに対して NX ビットを正しく扱う
MSR ビット
MSR には、ハイパーバイザの介入が必要なビットと、直接の介入が不要なビットが ある。後者は、ゲストへ入るときにだけ解釈されるか、ハイパーバイザの動作に 影響しないためである。
ゲスト内で安全に設定できるビットは次のとおりである:
MSR_EE
MSR_RI
MSR で他のビットが変わる場合は、やはり mtmsr(d) を使うこと。
パッチを当てる命令
32 ビットシステムでは、"ld" 命令と "std" 命令がそれぞれ "lwz" 命令と "stw" 命令に変換され、ビッグエンディアンに対応するためにオフセット 4 が加えられる。
以下は、Linux カーネルがゲストとして動作するときに行うマッピングの一覧である。 命令トラップも共有ページに作用するため、これらのマッピングの実装は任意である。 つまり、特権命令の呼び出しはこれまでどおり動作する。
変換前 |
変換後 |
|---|---|
mfmsr rX |
ld rX, magic_page->msr |
mfsprg rX, 0 |
ld rX, magic_page->sprg0 |
mfsprg rX, 1 |
ld rX, magic_page->sprg1 |
mfsprg rX, 2 |
ld rX, magic_page->sprg2 |
mfsprg rX, 3 |
ld rX, magic_page->sprg3 |
mfsrr0 rX |
ld rX, magic_page->srr0 |
mfsrr1 rX |
ld rX, magic_page->srr1 |
mfdar rX |
ld rX, magic_page->dar |
mfdsisr rX |
lwz rX, magic_page->dsisr |
mtmsr rX |
std rX, magic_page->msr |
mtsprg 0, rX |
std rX, magic_page->sprg0 |
mtsprg 1, rX |
std rX, magic_page->sprg1 |
mtsprg 2, rX |
std rX, magic_page->sprg2 |
mtsprg 3, rX |
std rX, magic_page->sprg3 |
mtsrr0 rX |
std rX, magic_page->srr0 |
mtsrr1 rX |
std rX, magic_page->srr1 |
mtdar rX |
std rX, magic_page->dar |
mtdsisr rX |
stw rX, magic_page->dsisr |
tlbsync |
nop |
mtmsrd rX, 0 |
b <special mtmsr section> |
mtmsr rX |
b <special mtmsr section> |
mtmsrd rX, 1 |
b <special mtmsrd section> |
[Book3S only] |
|
mtsrin rX, rY |
b <special mtsrin section> |
[BookE only] |
|
wrteei [0|1] |
b <special wrteei section> |
命令によっては、ロード命令やストア命令では足りないほど、何が起きているかを 判断するロジックが必要になる。それらにパッチを当てられるようにするため、 命令をその場で変換して書き込める RAM をいくらか確保しておく。起きることは 次のとおりである:
エミュレーションコードをメモリにコピーする
そのコードを、エミュレートする命令に合うようにパッチする
そのコードを、元の pc + 4 に戻るようにパッチする
元の命令を、新しいコードへ分岐するようにパッチする
こうすると、1 つの命令の代わりに任意の量のコードを注入できる。例えば EE=1 を 設定するときに、保留中の割り込みを確認できるようになる。
PowerPC 上の KVM におけるハイパーコール ABI
KVM ハイパーコール(ePAPR)
これらは ePAPR 準拠のハイパーコール実装である(前述)。ePAPR の idle hcall の ように、汎用のハイパーコールもここで実装されている。これらはすべての ターゲットで利用できる。
PAPR ハイパーコール
PAPR ハイパーコールは、サーバ PowerPC の PAPR ゲスト(QEMU の -M pseries)を 動作させるのに必要である。これらは POWER ハイパーバイザである pHyp が実装する ものと同じハイパーコールである。一部はカーネル内で処理され、一部はユーザ空間で 処理される。これは book3s_64 でのみ利用できる。
OSI ハイパーコール
Mac-on-Linux も PowerPC 上の KVM の利用者であり、独自のハイパーコールを 持っている(KVM よりずっと前から)。これは互換性を保つためにサポートされる。 これらのハイパーコールはすべてユーザ空間へ転送される。これは book3s_32 でのみ 役に立つが、book3s_64 でも使える。