この記事は、インテル® デベロッパー・ゾーンに掲載されている「Intel® System Studio 2014 – UEFI BIOS Debugging」(http://software.intel.com/en-us/articles/intel-system-studio-2014-uefi-bios-debugging) の日本語参考訳です。
以前の記事 (http://software.intel.com/node/488501) では、インテル® System Studio に含まれるインテル® JTAG デバッガー (XDB) とインテル® System Studio 2014 の新機能について説明しました。
この記事では、インテル® JTAG デバッガー (XDB) の EFI デバッグ機能と簡単な EFI デバッグセッションの例を紹介します。
EFI コマンド
デバッグ情報
モジュールの場所を特定する
EFI シェルとシステムテーブルからモジュールを見つける
独自のコードによりモジュールを見つける
Windows の subst に関するヒント
[Evaluation] ウィンドウを活用する
EFI コマンド
最初に、XDB で利用できる EFI コマンドについて説明します。XDB のコンソールで "efi" と入力すると EFI コマンドの一覧が表示されます。
EFI "LOADTHIS { |
最もよく使用するコマンドです。指定されたアドレスのデバッグ情報のロードを試みます。指定されたアドレスに近いメモリーをスキャンし、モジュールヘッダーを探します。アドレスを指定しない場合、現在の IP アドレスが使用されます。 |
EFI "SETSYSTAB |
EFI システムテーブルのベースアドレスを設定します。このコマンドにより、XDB に EFI システムテーブルの場所を知らせます。 |
EFI "SHOWSYSTAB" | システムテーブルをスキャンします。最初にシステムテーブルのアドレスを設定しておくことをお勧めします。そうでない場合、定義されたメモリー検索範囲がスキャンされるため、時間がかかります。 |
EFI "SHOWMODULES" | DXE フェーズでロードされているすべての EFI モジュールを表示します (EFI シェルを使用することもできます)。 |
EFI "LOAD |
指定されたモジュール ID またはモジュール名のデバッグ情報のロードを試みます。このコマンドは、デバッガーがシステムテーブルのアドレスを知っていることを前提としています。このコマンドではなく、LOADTHIS を使用することを推奨します。 |
デバッグ情報
XDB は、Microsoft* コンパイラーや GCC でコンパイルおよびリンクされた PE/COFF モジュールをデバッグできます。同じプロジェクトやデバッグセッションで、両方を混在させることもできます。例えば、Linux* 開発者が Microsoft* ツールによって開発された既存の BIOS にモジュールを追加する場合、そのモジュールが開発されたツールにかかわらず BIOS 全体をデバッグすることができます。
EFI フェーズをデバッグする際は、EFI "LOAD" コマンドでシンボル情報をロードせずに、EFI "LOADTHIS" のみ使用してください。"LOADTHIS" はシンボルをロードし、モジュールの場所に応じて正しく再配置します。モジュールは、例えばフラッシュから RAM へ移動される可能性があるため、これは重要です。
モジュールの場所を特定する
モジュールの場所を特定する方法はいくつかあります。ここでは次の 2 つの方法を説明します。
EFI シェルとシステムテーブルからモジュールを見つける
システムテーブルを使ってロードされているすべてのモジュールの場所を取得できます。システムテーブルの場所を特定する 1 つの方法は、EFI シェルを利用することです。
XDB をターゲットに接続して、ターゲットにアクセスできることを確認します。ターゲットをリセットして、EFI シェルが起動されるのを待ちます。次に、アドレス・パラメーターを指定せずに mem コマンドを実行します。すると、EFI システム・テーブル・ポインターが表示されます。以下に例を示します。
Memory Address 00000000A27EEF18 200 Bytes A27EEF18: 49 42 49 20 53 59 53 54-1F 00 02 00 78 00 00 00 *IBI SYST....x...* A27EEF28: EA EF B4 8D 00 00 00 00-98 72 7D A2 00 00 00 00 *.........r......* A27EEF38: 00 00 01 00 00 00 00 00-98 47 36 9F 00 00 00 00 *.........G6.....* A27EEF48: 60 03 82 95 00 00 00 00-98 7F 48 9F 00 00 00 00 *`.........H.....* A27EEF58: F8 D9 19 A1 00 00 00 00-18 3F 36 9F 00 00 00 00 *.........?6.....* - 途中の行省略 - A27EF108: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF *................* Valid EFI Header at Address 00000000A27EEF18 -------------------------------------------- System: Table Structure size 00000078 revision 0002001F ConIn (9F364798) ConOut (9F487F98) StdErr (9F363F18) Console Out on PciRoot(0x0)/Pci(0x1D,0x0)/USB(0x1,0x0)/USB(0x5,0x0)/\/mem.txt Runtime Services 00000000A27EEE18 Boot Services 00000000A11B9A70 ACPI Table 00000000A2FE9000 ACPI 2.0 Table 00000000A2FE9014 MPS Table 00000000A2A43000 SMBIOS Table 00000000A2A9E000
アドレス 0x A27EEF18 が、システムテーブルの場所です。XDB でターゲットを停止し、コンソールで次のコマンドを実行します。
xdb> efi "setsystab 0xa27eef18" xdb> efi "showsystab" EFI System table at 0x00000000A27EEF18 Configuration Tables: ____________________________________________________________________ GUID: Pointer: Name: GUID f880aae0, e4ac, 4c64, {...} 0xa2fae000 GUID 05ad34ba, 6f02, 4214, {...} 0xa11b9bf0 DXE_SERVICES_TABLE GUID 7739f24c, 93d7, 11d4, {...} 0xa2fea018 HOB_LIST - 途中の行省略 - BootServices: EFI_RAISE_TPL 0x00000000A11A8300 EFI_RESTORE_TPL 0x00000000A11A8384 - 途中の行省略 - EFI_ALLOCATE_PAGES 0x00000000A11A494C EFI_FREE_PAGES 0x00000000A11A4A54 EFI_CREATE_EVENT_EX 0x00000000A11A33E8
このように、efi "showmodules" コマンドでさらに詳細な情報が得られます。XDB にシステムテーブルの場所を知らせると、ロードされているモジュールとその場所も分かります。
xdb> efi "showmodules" INFO: Using cached EFI State Information ____________________________________________________________________ ModuleID Base Size Name 00197 0x0000000095817000 0x000BC400 00196 0x0000000095B40000 0x00001780 00195 0x0000000095B1C000 0x0001F8C0 UsbRt.efi 00194 0x0000000095B7E000 0x00001A20 00193 0x0000000095B83000 0x00001AC0 LegacyInterruptHookDxe.efi - 途中の行省略 - 00170 0x000000009F232000 0x00004F00 HitachiH8s2113.efi 00169 0x000000009F23A000 0x00001740 NbInt15.efi 00168 0x000000009F23E000 0x000043A0 PlatformFviDxe.efi 00167 0x000000009F243000 0x00004480 OverClockDxe.efi 00166 0x000000009F248000 0x00005B20 ScsiDisk.efi 00165 0x000000009F24E000 0x00003A20 ScsiBus.efi 00164 0x000000009F252000 0x00003920 UsbMouseDxe.efi 00163 0x000000009F256000 0x00005EA0 UsbMassStorageDxe.efi 00162 0x000000009F25C000 0x00006300 UsbKbDxe.efi 00161 0x000000009F263000 0x0000A1A0 XhciDxe.efi 00160 0x000000009F26E000 0x00008F80 UsbBusDxe.efi - 途中の行省略 - 00016 0x000000009FEB7000 0x00001A40 Legacy8259.efi 00015 0x000000009FEB9000 0x00002680 DataHubDxe.efi 00014 0x000000009FEBD000 0x00015B60 HiiDatabase.efi 00013 0x000000009FED4000 0x00002060 CpuIo2Dxe.efi 00012 0x00000000A27C9000 0x00002680 CpuIoDxe.efi 00011 0x00000000A27CC000 0x00002EE0 ReportStatusCodeRouterRuntimeDxe.efi 00010 0x000000009FED7000 0x00001600 TbtDxe.efi 00009 0x000000009FED9000 0x000016A0 PlatformPrivateDxe.efi 00008 0x000000009FEDB000 0x000037C0 AcpiSupportDxe.efi 00007 0x000000009FEDF000 0x000010E0 AsfTable.efi 00006 0x000000009FEE1000 0x00002300 WdtDxe.efi 00005 0x000000009FEE4000 0x00002660 PchSerialGpio.efi 00004 0x000000009FF1A000 0x00001F80 ActiveBios.efi 00003 0x000000009FF1C000 0x00001280 PlatformInfoDxe.efi 00002 0x000000009FEE7000 0x00004420 PcdDxe.efi 00001 0x000000009FF1E000 0x00001CE0 MeUlvCheck.efi 00000 0x00000000A119E000 0x00020000 DxeCore.efi
これで、モジュール名または ID を介して、XDB にモジュールのデバッグ情報のロードを指示することができます。
xdb> efi "load HpetTimerDxe.efi"
デバッグ情報がロードされたら、XDB の [Source Files] ウィンドウを開いて ([View] > [Source Files])、ツリーからデバッグするファイル (この例では、PcAtChipsetPkg\HpetTimerDxe\HpetTimer.c) を指定します。ソースファイルを開いて、適切な場所にブレークポイントを追加し、デバッグを開始します。
独自のコードによりモジュールを見つける
モジュールを特定するもう 1 つの方法として、独自のコードを使う方法があります。エントリー関数に "jmp $" または "while(1);" を追加して、無限ループを作成します。システムが無限ループに入ったら停止することで、独自のモジュールを実行することができます。
この方法を示す簡単な例として、インテル® UEFI Development Kit 2010 に含まれる "HelloWorld" サンプルコードを使用します。ここでは、「到達不能なコード」の警告を無効にするため、サンプルコードにプラグマを追加しています。デフォルト設定では、この警告はエラーと見なされ、コードが生成されません。また、「Hello World」を 10 回出力するようにサンプルコードを変更しました。以下に変更後のコードを示します。
/** @file このサンプル・アプリケーションは、HelloWorld PCD 設定に基づいて UEFI コンソールに "UEFI Hello World!" を出力します。 © 2006 - 2008 Intel Corporation. 無断での引用、転載を禁じます。 このプログラムと添付資料は、ともに配布されるBSD ライセンスの 条件の下で提供されます。 以下の日本語訳と原文に齟齬がある場合は、常に原文が適用されるもの とします。ライセンスの全文は、次の Web サイト (英語) をご覧ください。 http://opensource.org/licenses/bsd-license.php 本プログラムは、BSD ライセンスの下で「現状のまま」提供されており、 明示黙示を問わず、いかなる保証もありません。 **/ #include#include #include #include #pragma warning (disable : 4702) /** アプリケーションのユーザー・エントリー・ポイント。 アプリケーションの実際のエントリーポイントとして、 この関数からユーザーコードを開始します。 @param[in] ImageHandle ファームウェアにより割り当てられた EFI イメージのハンドル @param[in] SystemTable EFI システムテーブルへのポインター @retval EFI_SUCCESS エントリーポイントが正常に実行されたことを示します。 @retval other エントリーポイントの実行時にエラーが発生したことを示します。 **/ EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { UINT32 Index; endless: ; goto endless; Index = 0; // // 例として 3 つの PCD 型 (FeatureFlag、UINT32、String) を使用します。 // if (FeaturePcdGet (PcdHelloWorldPrintEnable)) { for (Index = 0; Index < 10; Index ++) { // // UefiLib Print API を使用して UEFI コンソールに文字列を出力します。 // Print ((CHAR16*)PcdGetPtr (PcdHelloWorldPrintString)); } } return EFI_SUCCESS; }
次のコマンドでビルドします。
Build -a X64 -p MdeModulePkg\MdeModulePkg.dsc -m MdeModulePkg\Application\HelloWorld\HelloWorld.inf
モジュールをビルドしたら、EFI シェルから起動します。無限ループに入るまで実行し (この例ではすぐです)、XDB でシステムを停止します。IP は、無限ループを指しているはずです (割り込みルーチンの場合は IP になるまで試してください)。efi "loadthis" を使って、IP アドレスからモジュールヘッダーの検索を開始し、このモジュールのデバッグ情報をロードするように XDB に指示します。
その後、前述のステップに従ってソースコードを開き、ブレークポイントを設定します。実行を継続するには、IP を jmp の次の命令に設定し、システムを実行する必要があります。
Windows の subst に関するヒント
多くの場合、BIOS の開発ではシステム上に複数のプロジェクトが存在します。パスを簡潔で似たものにするため、Windows ではよく subst を使ってプロジェクトのルートへのドライバーを定義し、そのドライブでプロジェクトをビルドします。ソースとデバッグ情報へのパスは、モジュールに組込まれます。XDB は、この情報を利用してデバッグ情報とソースファイルを見つけます。
subst の 1 つの欠点は、現在のユーザーにのみ適用され、システム全体には適用されず、再起動後に情報が失われることです。代わりに、subst の不変版と言える psubst をお勧めします。このコマンドは、すべてのユーザーが subst を利用でき、再起動後も情報が保持されるようにレジストリー・エントリーを設定します。詳細およびバイナリーとソースコードは、http://alter.org.ua/en/soft/win/psubst/ (英語) を参照してください。関連情報は、http://networkadminkb.com/KB/a446/how-to-use-drive-letters-mount-points-the-same-disk-drive.aspx (英語) などを参照してください。
[Evaluation] ウィンドウを活用する
デバッグに役立つツールとして、[Evaluation] ウィンドウがあります。変数を右クリックして [On Selection "xxx"] > [Add To Eval Window] を選択すると、その変数が [Evaluation] ウィンドウに表示されます。例えば、変数が構造体の場合、そのすべての要素の内容が表示されるため便利です。
ここで紹介した新しいコマンドにより、皆さんが EFI デバッグを簡単に行えるように願っています。
関連情報 (英語)
EFI シェルおよびスクリプト: http://software.intel.com/en-us/articles/efi-shells-and-scripting/
- EFI シェルコマンドの説明と参考になる例
簡単な EDK II UEFI アプリケーションの作成: http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=Getting_Started_Writing_Simple_Application
- 便利な EFI アプリケーション開発のハウツーガイド
UEFI Development Kit UDK2010 のダウンロード: http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=UDK2010
- サンプル・アプリケーションの作成に必要なすべてのツールとソース
subst の不変版である psubst : : http://alter.org.ua/en/soft/win/psubst/
Windows の subst の説明: http://networkadminkb.com/KB/a446/how-to-use-drive-letters-mount-points-the-same-disk-drive.aspx
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。