x86命令では、独自のエンコーディングとすべての引数が同時にメモリに存在する必要がありますか?


64

私は、RAMが単一の物理ページによってのみ裏付けられているLinux VMを実行できるかどうかを把握しようとしています。

これをシミュレートするために、KVMのネストされたページフォールトハンドラーを変更して、現在処理されているページフォールトに対応するものを除くすべてのネストされたページテーブル(NPT)エントリから現在のビットを削除しました。

Linuxゲストを起動しようとしたときに、次のようなメモリオペランドを使用するアセンブリ命令に気づきました。

add [rbp+0x820DDA], ebp

命令を含むページとオペランドで参照されるページ(この例では[rbp+0x820DDA])の現在のビットを復元するまで、ページフォールトループが発生します。

なぜそうなのかと思います。CPUはメモリページに順次アクセスするべきではありませんか。つまり、最初に命令を読み取ってから、メモリオペランドにアクセスしますか?または、x86では、命令ページとすべてのオペランドページに同時にアクセスできる必要がありますか?

AMD Zen 1でテストしています。


2
なぜこれをしたいのですか?
SSアン

11
技術的な関心から:)
savvybug

14
陽気なプロジェクトのアイデアに賛成票を投じます。
パイプ

10
これは、「ブラウザーのJavaScriptで実行されている486エミュレーターでLinuxをブートする」レベルでは異常です。大好きです。
クリリス

3
ええ、どうやら私はこの質問を、あなたがすでに考えていたのと同じ論理的な結論に導きました。前進を保証するための最小の作業セットについてです。あなたが質問に新しい最初の段落を追加する前に、私はすでにそのことを答えました。:PIはいくつかのリンク(たとえば、ページウォーカーがゲストのページディレクトリエントリを内部的にキャッシュすることを許可されている)にいくつかのリンクと詳細を追加しました。
Peter Cordes

回答:


56

はい、マシンコードとすべてのメモリオペランドが必要です。

CPUはメモリページに順次アクセスするべきではありませんか。つまり、最初に命令を読み取ってから、メモリオペランドにアクセスしますか?

はい、それは論理的に何が起こるかですが、ページフォールト例外が2ステップのプロセスを中断し、進行状況を破棄します。CPUには、ページフォールトが発生したときの途中の命令を記憶する方法がありません。

有効なページフォールトを処理した後にページフォールトハンドラーが戻ると、RIP =フォールトが発生した命令のアドレスであるため、CPUはその実行を再試行します。 は最初からます。

OSが障害のある命令のマシンコードを変更し、その後に別の命令を実行することを期待することは合法です iret障害し、ページフォールトハンドラー(またはその他の例外ハンドラーまたは割り込みハンドラー)から。つまり、あなたが話している場合に備えて、CPUがCS:RIPからのコードフェッチをやり直すことがアーキテクチャ上要求されています。(ハードページフォールトでディスクを待機している間に別のプロセスをスケジュールしたり、無効なページフォールトでSIGSEGVをシグナルハンドラーに配信したりする代わりに、障害のあるCS:RIPに戻ると仮定します。)

また、ハイパーバイザーの入り口/出口にもアーキテクチャ上必要となるでしょう。また、紙で明示的に禁止されていなくても、CPUの動作方法ではありません。

@torekは、一部の(CISC)マイクロプロセッサが部分的に命令をデコードし、ページフォールト時マイクロレジスタの状態をダンプするとコメントしていますが、x86はそうではありません。


いくつかの命令は割り込み可能rep movsで、(canのmemcpy)や他の文字列命令のように部分的に進行したり、ロード/スキャッターストアを収集したりできます。ただし、唯一のメカニズムは、文字列操作の場合はRCX / RSI / RDIなどのアーキテクチャレジスタ、または収集の場合は宛先およびマスクレジスタを更新することです(AVX2のマニュアルなど)。vpgatherdd)。opcode / decodeを保持しないと、一部の非表示の内部レジスタが発生し、ページフォールトハンドラーからiretした後に再起動します。これらは、複数の個別のデータアクセスを行う命令です。

また、x86(ほとんどのISAと同様)は、命令がアトミックなwrtであることを保証することにも留意してください。割り込み/例外:割り込みの前に完全に発生するか、まったく発生しないかのいずれかです。 動作中にアセンブリ命令に割り込む。したがって、たとえばadd [mem], reglockプリフィックスがなくてもストアパーツに障害が発生した場合は、ロードを破棄する必要があります。


前進するために存在するゲストユーザー空間ページの最悪の場合の数は6(さらに、それぞれに個別のゲストカーネルページテーブルサブツリー)になる可能性があります。

  • movsqまたはmovsw、ページ境界にまたがる2バイトの命令なので、デコードするには両方のページが必要です。
  • qwordソースオペランド[rsi]もページ分割
  • qword宛先オペランド[rdi]もページ分割

これらの6ページのいずれかに障害が発生した場合は、スクエアページに戻ります。

rep movsdも2バイトの命令であり、その1つのステップで進行する場合も同じ要件があります。同様の例は好きpush [mem]pop [mem]ずれスタックで構成することができます。

ギャザーロード/スキャッターストアを「中断可能」にする(マスクベクトルを進行状況に合わせて更新する)理由の1つ(または副次的な利点)は、この最小フットプリントを増やして単一の命令を実行しないようにすることです。また、1回の収集または分散中に複数の障害を処理する効率を改善します。


@Brandonは、ゲストがメモリ内のページテーブルを必要とすることをコメントで指摘しており、ユーザー空間のページ分割を1GiB分割にすることもできるため、2つのサイドがトップレベルのPML4の異なるサブツリーにあります。HWページウォークは、これらのゲストページテーブルのすべてのページに触れて、進行する必要があります。この病理学的な状況が偶然に起こることはまずありません。

TLB(およびページウォーカーの内部)は、ページテーブルデータの一部をキャッシュすることでき、OSがinvlpg新しいCR3トップレベルページディレクトリを設定または設定しない限り、ページウォークを最初から再起動する必要はありません。ページを存在しない状態から存在する状態に変更する場合、これらはどちらも必要ありません。紙の上のx86は、それが不要であることを保証します(したがって、存在しないPTEの「ネガティブキャッシング」は許可されません。少なくともソフトウェアからは見えません)。そのため、ゲスト物理ページテーブルページの一部が実際に存在しない場合でも、CPUはVMexitしない可能性があります。

PMUパフォーマンスカウンターを有効にして、命令がその命令のPEBSバッファーに書き込むためのパフォーマンスイベントも必要とするように構成できます。カーネルではなく、ユーザー空間の命令のみをカウントするようにカウンターのマスクを構成すると、ユーザー空間に戻るたびにカウンターがオーバーフローし、サンプルがバッファーに格納され続け、ページフォールトが発生する可能性があります。


15
単一の命令の最悪のケースは、 " push dword [foo"(または単にcall [foo])のようなもので、 " ページディレクトリポインターテーブルの境界"を横切ってすべてが整列しない(最大6ページ、6ページのテーブル、6ページのディレクトリ、6つのPDPTおよび1つのPML4を追加する)。CPUの「PEBSバッファーを使用した正確なイベントベースのサンプリング」機能を有効にして、pushパフォーマンス監視データがPEBSバッファーに追加されるように構成します。控えめな「ホストが提供する最低限のページは、ゲストが病理学的なケースで進歩できるようにするため」少なくとも16ページが必要です。
ブレンダン

4
この種のことはCISC-yアーキテクチャでは常に一般的であることに注意してください。一部のマイクロプロセッサは命令を部分的にデコードし、ページフォールトでマイクロレジスタの状態をダンプしますが、「loop-y」命令のアドレスオペランド(m68kのDBRA、VaxのMOVC3 / MOVC5など)が同様のレジスタにあることを要求しないあなたのREP MOVSの例に。
トレック

1
@Brendan:誰かがVAX命令の最悪のケースを約50ページと数えました。詳細は忘れましたが、ページ境界に命令自体を配置し、ページ境界にまたがるテーブルで変換テーブルルックアップなどを使用し、ページ境界で間接を使用して(rX)[rY]を使用し、など。最も毛深い命令は最大6オペランド(r0-r5にロード)を取り、6つすべてが二重間接になる可能性があると思います。
トレック

3
OSは命令を変更できますが、変更することもできますEIP。したがって、論理的なフォローアップの質問があります。インテリジェントな命令パッチスキームを想定して、必要な最小ページ数はいくつですか?たとえば、アラインされていない値をアラインされたスクラッチバッファーにコピーし、命令をエミュレートし、IRETを次の命令にコピーします。
MSalters

1
OSのiret命令を含むページもメモリ内にある必要があります。これは1バイトの命令なので、1ページ余分に。ページフォールトハンドラーの割り込みアドレスもメモリ内にある必要がありますが、上記と同じページにすることもできます。
Stig Hemmer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.