EFIバイトコードからのx86プロシージャコールの整理

EFI Byte Codeテクノロジー(略してEBC)を使用すると、ファームウェアプラットフォームの一部として実装された仮想プロセッサで実行されるクロスプラットフォームアプリケーションとドライバーを作成できます。 このプロセッサのアーキテクチャは、Unified Extensible Firmware Interface仕様で定義されています。 理想的には、EBCプログラムは、仮想マシンリソース、UEFIシステムテーブル、およびプラットフォームのハードウェア実装から抽象化された他のオブジェクトと排他的に対話する必要があります。 実際には、この原則を厳守すると、ソフトウェア製品の機能が著しく制限されます。 中央処理装置のネイティブコードでサブルーチンを使用し、特定のハードウェアプラットフォームが検出された場合に条件付きで制御を受け取ると、クロスプラットフォームを失うことなく状況から抜け出すことができます。



問題の声明



EBCアプリケーションから指定されたMSR(モデル固有レジスタ)レジスタを読み取る例を考えてみましょう。 ご存じのとおり、x86命令システムは、ECXレジスタの32ビットMSRアドレスを入力パラメーターとして受け取り、EDX(上位32ビット)およびEAX(下位32ビット)レジスタに64ビットMSRの内容を返すRDMSR(MSR読み取り)命令を提供します) EBC仮想マシンコマンドシステムは同様の機能を提供しないため、ネイティブコードでのサブルーチン呼び出しが必要です。



UEFImark x64 Editionの情報および診断ユーティリティではRDMSR命令が直接使用され、 UEFImark EBC Editionでは EBCプログラムからネイティブルーチンを呼び出す必要があることに注意してください。



実験条件



問題の手順に制御を移す前に、EBCプログラムはx86プラットフォームを検出し、IA32またはx64アーキテクチャのいずれかがサポートされていると判断したと想定されます。 検出方法はこの記事の範囲外であり、今後の出版物で検討する予定です。



サンプルをブロードキャストするために、FASM 1.69.50が使用されます。 EBC命令はマクロを使用して実装され、x86コードは64ビットモード用にブロードキャストされます。32ビットモードとの互換性を確保する機能については、以下で説明します。



プロシージャEBC_Read_MSRの呼び出し


;--- Subroutine: EBC/x86 gate for Read MSR ----------------------------------; ; Caller must verify x86 support (IA32 or x64) before call this subroutine, ; ; but this subroutine differentiate IA32/x64 internally. ; ; ; ; INPUT: R1 = Global variables pool base address ; ; R6 = MSR index (same as ECX before RDMSR instruction) ; ; OUTPUT: R3 = MSR data after Read (same as EDX:EAX after RDMSR instruction) ; ; R4-R7 can be changed ; ;----------------------------------------------------------------------------; EBC_Read_MSR: XOR64 R7,R7 ; R7=0 PUSH64 R7 ; Storage for output MOVQ R7,R0 ; Address of storage = stack pointer PUSHN R7 ; Parameter#2 = Output address PUSHN R6 ; Parameter#1 = MSR address MOVINW R7,1,0 CMPI32WEQ R7,4 ; R7=4 for 32-bit, R7=8 for 64-bit MOVIQW R7,_IA32_Read_MSR ; This pointer for IA32 (native width=4) JMP8CS Native_Gate MOVIQW R7,_x64_Read_MSR ; This pointer for x64 (native width=8) Native_Gate: ADD64 R7,R1 ; Add base address = R1 CALL32EXA R7 POPN R6 ; Remove Parameter#1 POPN R7 ; Remove Parameter#2 POP64 R3 ; Read R3 = Output RET
      
      





1 x86 MSR読み取りプロシージャを呼び出すEBCプロシージャ



呼び出し元のEBCプロシージャによって実行される一連の操作を検討します。

  1. 呼び出されたプロシージャが指定されたMSRレジスタの内容を書き込む64ビット変数のスタック内の予約。
  2. 呼び出されたプロシージャの2番目の入力パラメータ-MSRの内容を保存するためのアドレスをスタックに書き込みます。 これは、ステップ1で作成された変数のアドレスです。
  3. 呼び出されたプロシージャの最初の入力パラメータであるスタックへの書き込み-レジスタR6のサブルーチンによって採用されたMSRレジスタのアドレス。
  4. EBC命令MOVINWを使用した自然容量の決定。 プラットフォームがx86互換であることが以前に確立されていた場合、自然ビット4の値はIA32(4バイト= 32ビット)を意味し、8はx64(8バイト= 64ビット)を意味します。
  5. 呼び出されたサブルーチンへのエントリポイントのアドレスを選択します(手順4の結果に従って、R7レジスタに配置します)。
  6. 手順5で取得したアドレスでサブルーチンを呼び出します。
  7. 以前に書き込まれたパラメーターをスタックから読み取り、削除すると、ステップ1で作成された変数の値がR3レジスターに読み込まれ、呼び出されたサブルーチンが結果を書き込みました-64ビットMSRコンテンツ。




PUSHN(Push Natural)命令によってスタックに書き込まれ、POPN(Pop Natural)命令によってスタックから読み取られるパラメーターのビット深度は、IA32 UEFIでは32ビット、x64 UEFIでは64ビットです。



呼び出されたプロシージャ:IA32_Read_MSR、x64_Read_MSR


 ;--- Read Model-Specific Register, selected by input index --------------; ; INPUT: Parm#1 = MSR address (ECX before RDMSR), natural width 32/64 ; ; Parm#2 = Address for write output data, natural width 32/64 ; ; OUTPUT: R7 = Reserved for UEFI status ; ; QWORD at Address [Parm#2] = MSR data (EDX:EAX after RDMSR) ; ;------------------------------------------------------------------------; IA32_Read_MSR: ; Entry point for IA32 push rbx rcx rdx mov ecx,[rsp+16] ; ECX = Parm#1 = MSR address, assembled same as [esp+16], can use for IA32 mov ebx,[rsp+20] ; EBX = Parm#2 = Output address, assembled same as [esp+20], can use for IA32 jmp Entry_R_MSR x64_Read_MSR: ; Entry point for x64 push rbx rcx rdx mov rbx,rdx ; RBX=Output address (p#2), RCX=MSR address (p#1) Entry_R_MSR: rdmsr ; RCX=Input, EDX:EAX=Output mov [rbx+00],eax mov [rbx+04],edx pop rdx rcx rbx ret
      
      





2X86がMSR読み取りプロシージャを呼び出しました



呼び出されたx86プロシージャによって実行される一連の操作を検討します。

  1. IA32のエントリポイント(ラベルIA32_Read_MSR)。 レジスタEBX、ECX、EDXのスタックに保存します。 入力パラメーターは、呼び出し側プロシージャーによって作成されたスタックフレームから読み取られます。MSRの内容を保存するためのMSRアドレスと変数アドレスです。 ステップ3に進みます。
  2. x64のエントリポイント(ラベルx64_Read_MSR)。 レジスタスタックRBX、RCX、RDXに保存します。 レジスタでは、RCXとRDXがそれぞれ最初と2番目の入力パラメーターです。
  3. ターゲット操作の実行— RDMSR命令を使用してMSRを読み取ります。
  4. 読み取ったMSRの内容を2番目の入力パラメーターで指定されたアドレスに保存します。
  5. レジスタEDX、ECX、EBX(IA32の場合)またはRDX、RCX、RBX(x64の場合)を復元し、呼び出し元のプロシージャに戻ります。




手順を見ると、矛盾が見つかります。64ビットモードの多くの命令は、32ビット分岐の実行に使用されます。たとえば、64ビットレジスタで動作するPUSHおよびPOP命令です。 どのように機能しますか? 実際、これらの命令の32ビットおよび64ビット形式は同じ方法でエンコードされ、それらの解釈はプロセッサの動作モードによって異なります。 したがって、32ビットモードのコード53hはPUSH EBX命令に対応し、64ビットモードのコードはPUSH RBX命令に対応します。



サブプログラムの入力および出力パラメータの送信メカニズムを検討してください。



IA32 EFIの場合、呼び出されたルーチンの入力パラメーターはスタックを介して渡されます。 この手順では、最初のパラメーターは[ESP + 16]にあります。 オフセット16は2つの用語で構成されます:4スタックバイトは、サブルーチンから戻るときに必要なEIP命令カウンター、PUSH命令によってスタックに書き込まれたEBX、ECX、EDXレジスタ用の12バイトを格納するために使用されます。 4 + 12 = 16。



x64 UEFIの場合、呼び出されたサブルーチンの最初の4つの入力パラメーターはレジスタRCX、RDX、R8、R9を通過し、次はスタックを通過します。 この例では、2つのパラメーターのみが使用され、RCXとRDXに渡されます。



IA32 UEFIの場合、x86サブルーチンから戻った後の32ビットx86 EAXレジスターの内容は、64ビットEBCレジスターR7の下位32ビットにあります。 R7の上位32ビットの内容は未定義です。 x64 UEFIの場合、x86サブルーチンから戻った後の64ビットx86 RAXレジスタの内容は、EBCレジスタR7にあります。 この機能は、ステータスコードを送信するのに便利です;考慮された例では使用されません。



説明されているテクノロジーは、アプリケーションに含まれるプロシージャを呼び出すためだけでなく、その処理がファームウェアに実装されているUEFI APIにアクセスするときにも使用されます。 たとえば、 CPU Architectural ProtocolおよびFile I / O Protocol機能を使用する場合



まとめ



説明された方法は、 EFI Byte Code内で達成できない機能を提供する必要がある場合にのみ使用する必要があります。 そのため、情報診断ユーティリティUEFImark EBC Editionでは、ネイティブコードのCPUID命令を使用して、プロセッサモデルとサポートされているテクノロジのリストを表示します。



ハードウェアリソースに直接アクセスすると、クロスプラットフォームのプロビジョニングが複雑になることに注意することが重要です。 特に、上記の例ではx86プラットフォームIA32とx64を区別できるという事実にもかかわらず、制御を移す前に、アプリケーションはx86プラットフォームで動作することを確認する必要があります。 ARMまたはItaniumで実行すると、CPU命令システムの違いにより、予期しない結果が生じます。



All Articles