インテル® System Studio 2014 – UEFI BIOS デバッグ

インテル® System Studio組込み

この記事は、インテル® デベロッパー・ゾーンに掲載されている「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] ウィンドウに表示されます。例えば、変数が構造体の場合、そのすべての要素の内容が表示されるため便利です。

[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

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました