EFIバイトコードとメモリ操作

ご存知のように、コードのプログラムによる解釈に基づいて動作する仮想マシンを使用すると、再コンパイルせずにさまざまなハードウェアプラットフォームで実行されるユニバーサルアプリケーションを作成できます。 EFI Byte Codeテクノロジーは、このアプローチの成功したアプリケーションの典型的な例です。 しかし、そのすべての利点には明らかな欠点があります-ソフトウェアで実装されたプロセッサは、ハードウェアのプロセッサよりもはるかに遅いです。 この記事では、メモリブロックに定数を入力し、メモリブロックの内容をコピーする操作の例によって、EBCプログラムのパフォーマンス低下を平準化できる方法を検討します。 さらに、中央処理装置のネイティブコードの「挿入」の使用については話していません。これは、 クロスプラットフォームの概念そのものを信用しないためです

問題の声明


したがって、アプリケーションがコピーブロックだけでなく、指定されたメモリ領域に特定の定数を書き込む必要があると想像してみましょう。 さらに、アレイは十分な大きさであり、この操作のパフォーマンスは、アプリケーション全体のパフォーマンスにとって重要です。 EBC命令を使用してブロックを処理するとパフォーマンスが低下し、ネイティブコードの「挿入」はクロスプラットフォームの損失を意味します。 になる方法

ソリューションが存在する


UEFI仕様の開発者は、このタイプの問題に対するエレガントなソリューションを提供しています。 EFIブートサービス機能セットは、メモリブロックにSetMem()定数を入力し、 CopyMem()メモリブロックをコピーする手順を提供します。 UEFI APIサービス手順は、 EFIブートサービスEFIランタイムサービスに分かれています 。 前者はOSのロード段階でのみ使用でき、後者はOSの動作時間全体で使用できます。

画像

図1UEFI仕様バージョン2.4 Errata AのSetMem()関数のパラメーターの説明
バッファ-ブロックのベースアドレス。
サイズ-ブロックサイズ。
値-ブロックを満たすデータ。

この関数は、値Bufferと等しいアドレスでメモリブロックを満たします。バイト単位のサイズは、値Sizeと等しくなります。

画像

図2UEFI仕様バージョン2.4エラッタAのCopyMem()関数パラメータの説明
宛先-受信ブロックのベースアドレス。
ソース-ソースブロックのベースアドレス。
長さ-転送操作の長さ(バイト単位)。

この関数は、ソースアドレスにある長さサイズのソースブロックを、宛先アドレスにある宛先ブロックにコピーします。

ブロックを定数で埋める例

図3は、32バイトブロックを11hの定数で埋めるEBCプログラムのリストを示しています。 その実装を検討してください。 R7のレジスタアドレスはブロックのベースアドレス、R6はブロックの長さ、R5は書き込み用のデータです。 その後、呼び出されたルーチンにパラメーターを渡すために使用されるスタックフレームが作成されます。 次に、 EFIシステムテーブルから、子EFIブートサービス テーブルのアドレスが読み取られます。このテーブルには、42の番号が付けられ、 SetMem()関数を呼び出すためのポインターが含まれています。 CALL32EXA命令は、EBCプログラムと呼び出されたUEFIファームウェアプロシージャをロックするために使用されます。 サブルーチンが完了すると、スタックフレームは削除されます。

この例および以下の例で、 _Primary_Memory_Baseおよび_EFI_Tableは、プログラムが使用するメモリブロックのベースアドレスと、起動時にアプリケーションに渡されるEFIシステムテーブルルートシステムテーブルのベースアドレスを格納する変数のアドレス指定に使用されるオフセットです。

クロスプラットフォームを確保するために不可欠なUEFIシステムテーブルを構築する1つの機能を思い出してみましょう。 32ビットUEFI実装ではサイズが4バイトのポインターを使用し、64ビット実装ではサイズが8バイトを使用します。 テーブルヘッダーフィールドのサイズは常に24バイトです。 したがって、UEFIシステムテーブルをアドレス指定する命令は、アドレスを計算するときに、エントリ番号とヘッダーサイズの2つの用語で動作します。 これにより、要素のサイズを決定するネイティブプロセッサの容量に関係なく、EBC仮想マシンは必要な要素のアドレスを正しく計算できます。

画像

図3SetMem()関数を使用してブロックを定数で埋める手順の例。 EBCアセンブラー命令が使用されます。

画像

図4SetMem()関数を使用してブロックを定数で埋める手順の結果。 表示するには、Intel EBCデバッガーを使用します。 この例では、レンダリングされたブロックのベースアドレスは6C40000h、長さ80h = 128バイトです。 11バイトの定数は、32バイトブロックで埋められます。

ブロックコピーの例

図5は、32バイトブロックをコピーするEBCプログラムのリストを示しています。 その実装を検討してください。 ソースブロックのベースアドレスはレジスタR7に書き込まれ、R6は宛先ブロックのベースアドレス、R5はブロックの長さです。 その後、呼び出されたルーチンにパラメーターを渡すために使用されるスタックフレームが作成されます。 次に、EFIシステムテーブルから、子EFIブートサービステーブルのアドレスが読み取られます。このアドレスは、CopyMem()関数を呼び出すために41番にあります。 CALL32EXA命令は、EBCプログラムと呼び出されたUEFIファームウェアプロシージャをロックするために使用されます。 サブルーチンが完了すると、スタックフレームは削除されます。

画像

図5CopyMem()関数を使用してブロックをコピーする手順の例。 EBCアセンブラー命令が使用されます。

画像

図6CopyMem()関数を使用したコピーブロックプロシージャの結果。 表示するには、Intel EBCデバッガーを使用します。 この例では、レンダリングされたブロックのベースアドレスは6C40000h、長さ80h = 128バイトです。 アドレス6C40000h-6C4001Fhにあるソースブロックは、アドレス6C40030h-6C4004Fhの宛先ブロックにコピーされます。

まとめ


UEFIファームウェアの一部として、大規模アレイのプリミティブ処理に関連する機能を実装すると、理論的には、特定のプラットフォーム用にこれらの操作の実行を最適化できます。 x86セントラルプロセッサの128または256ビットSSE命令、さまざまなDMAコプロセッサ、メモリコントローラを使用したハードウェア実装を使用すると、パフォーマンスを大幅に向上させることができます。 プラットフォーム開発者がこの可能性をどれだけ効率的に使用しているかは、さらなる調査によって示されますが、UEFIファームウェアプロシージャを使用した従来の32ビットx86命令を使用する場合でも、クロスプラットフォームパフォーマンスを失わずにソフトウェアプロセッサで実行されるEBCアプリケーションは、自由にハードウェアプロセッサパフォーマンスを取得します

Source: https://habr.com/ru/post/J214119/


All Articles