DXEフェーズ: ドライバー、Protocolと何も動かないときのログの読み方

DXEの深掘り: dispatcher、DEPEX、Driver Binding、protocolデータベース。シリアルログ、POST code、UEFI Shellを使って実際の障害をデバッグする方法。

更新 5 分で読めます
Đọc bằng 日本語 Tiếng Việt English
BIOS / UEFI cover

DXEは最もデバッグすることになるフェーズ

新しいBIOSをフラッシュしたら、NVMeはOSでは見えているのにUEFIブートメニューには出てこない。ドライバーはクリーンにビルドでき、ソースにエラーはなく、イメージのフラッシュも成功した。バグはどこか: FDF、DEPEX、Driver Binding、それともBDS?

これはDXEで典型的な種類の障害です。DXEが他のフェーズより壊れやすいわけではなく、DXEはすべてが集まる場所だからです: ドライバーがここで動き、デバイスがここでenumerateされ、protocolがここでinstallされます。何かがおかしいとき、DXEは最初に症状が現れる場所であることが多い——たとえroot causeがそれより早い段階にあったとしても。

私の仕事はPOSシステム、キオスク、決済端末のBIOSカスタマイズです。その環境では「ゆっくりデバッグしよう」の余裕はありません。ピーク時間にお店でマシンが死んだら、障害レイヤーを素早く特定する必要があります。

この記事はまさにその視点に従います: デバッグの角度からDXEを読む方法 ——ログが何を言っているか、POST codeはどこを指すか、デバイスが出てこないときにどのレイヤーをチェックするか。


DXEが何をするか、システム視点から

PEIがハンドオフすると、DXE CoreはメモリマップとfirmwareVolumeを記述するHOBリストを受け取ります。そこから、DXE Coreは:

  1. メモリサービスを構築する (AllocatePool, AllocatePages, …)
  2. DXE Dispatcherを起動する
  3. DispatcherはFirmware Volumeをスキャンし、DEPEXをチェックし、ドライバーをdispatchする
  4. 各ドライバーはhandle databaseにprotocolをinstallする
  5. 新しいprotocol → 待機中のドライバーがunlock → さらにdispatch
  6. 最終的にBDSに移行する

重要な点: DXEはファイルの順序でドライバーを動かしません。 dispatch順序は依存関係チェーンに従います——誰が何のprotocolを必要としているか、そのprotocolがinstall済みかどうか。


DXEのPOST code: ポート0x80から読む

POST code (チェックポイント)とはfirmwareがデバッグ出力——多くのx86プラットフォームではI/Oポート0x80、またはプラットフォーム/ベンダーが提供する同等のパス——に書き込む1バイトで、現在のステップを示します。シリアルログが利用できる前にシステムがハングしたときの最初のデバッグツールです。

AMI Aptio 5.xの公開AMIドキュメントによるDXEレンジ:

レンジ意味
0x60–0x8FDXE実行 (BDS前)
0x90–0xCFBDS実行
0xD0–0xDFDXEエラー

主要なDXEチェックポイント:

コード説明
0x60DXE Core起動
0x61NVRAM初期化
0x62South Bridge Runtime Services
0x63CPU DXE初期化開始
0x68PCIホストブリッジ初期化
0x69North Bridge DXE init
0x6ANorth Bridge DXE SMM init
0x70South Bridge DXE init
0x71South Bridge DXE SMM init
0x72South Bridgeデバイスinit

DXEエラーコード:

コード説明
0xD0CPU初期化エラー
0xD1North Bridge初期化エラー
0xD2South Bridge初期化エラー
0xD3Architectural Protocolの一部が見つからない
0xD4PCIリソース割り当てエラー
0xD5Legacy Option ROMのスペースなし
0xD6Console Outputデバイスが見つからない
0xD7Console Inputデバイスが見つからない
0xD8パスワードが無効
0xD9ブートオプションのロードエラー (LoadImage失敗)
0xDAブートオプションがアクティブでない

シリアルログ: PEIを過ぎたときの主なツール

POST codeはどのステップかを教えますが、なぜ失敗したかは教えません。詳細な情報があるのはシリアルログです。

EDK II / MdePkg DEBUGマクロ

UEFI/PIモデルに基づくfirmwareやEDK II互換のツーリングを使うfirmwareでは、ログは通常MdePkgのDEBUG()マクロのパターンに従います。AMI ApticとInsyde H2Oはそれぞれ独自のコードベースとツールチェーンを持ちますが、ログレベルとステータスの読み方は通常非常に似ています。

DEBUG ((DEBUG_INFO, "MyDriver: LocateProtocol returned %r\n", Status));
DEBUG ((DEBUG_ERROR, "MyDriver: Fatal error at line %d\n", __LINE__));

%rはEDK II固有のフォーマット指定子で、EFI_STATUSを16進数の代わりに"Success""Not Found""Invalid Parameter"などの文字列として出力します。ログを読むときに非常に便利です。

デバッグレベル (ErrorLevel)

各メッセージにはレベルがあります。レベルフィルターはPCD PcdDebugPrintErrorLevelで設定されます。

Define意味
DEBUG_INIT0x00000001初期化
DEBUG_WARN0x00000002警告
DEBUG_LOAD0x00000004イメージロードイベント
DEBUG_FS0x00000008ファイルシステム
DEBUG_POOL0x00000010Pool確保/解放
DEBUG_PAGE0x00000020Page確保/解放
DEBUG_INFO0x00000040一般情報
DEBUG_DISPATCH0x00000080Dispatcher情報
DEBUG_VARIABLE0x00000100Variable操作
DEBUG_BM0x00000400Boot Manager
DEBUG_ERROR0x80000000エラー (常に表示)

シリアルログを読む際の重要なパターン

シリアルログがある場合、以下のパターンを探します。

ドライバーエントリーポイント:

Loading driver: MyDriverDxe
MyDriver: Entry point called

DEPEXが未満足でドライバーがdispatchされない: DXE dispatcherのログ (DEBUG_DISPATCH) が有効な場合:

DEPEX not satisfied for: MyDriverDxe.efi
  Needs: gEfiPciIoProtocolGuid (not installed yet)

Protocolのinstall成功:

MyDriver: InstallProtocolInterface success, Handle=0x...

Protocolのlocate失敗:

MyDriver: LocateProtocol(gEfiBlockIoProtocolGuid) = Not Found

DXE Dispatcher: ドライバーが動かない理由

これはDXEで最もよくある質問です: 「ビルドが成功しているのに、なぜエントリーポイントが動かないのか?」

最もよくある3つの原因:

1. FV/FDFにない

# DSC — このドライバーがビルドされる
[Components]
  MyPkg/MyDriverDxe/MyDriverDxe.inf

# FDF — firmware volumeにない → ROMに含まれない
[FV.MAIN_FV]
  # MyPkg/MyDriverDxe/MyDriverDxe.inf  ← この行が欠けている

ドライバーはビルドされ.efiファイルも存在しますが、firmware imageにパッケージされていません。DXE Dispatcherはそれを見つけられません。

2. DEPEXがまだ未満足

# ドライバーのINFで
[Depex]
  gEfiPciRootBridgeIoProtocolGuid AND
  gEfiCpuArchProtocolGuid

Dispatcherが最初のパスを実行したときにgEfiPciRootBridgeIoProtocolGuidがinstallされていなければ、このドライバーは待機に置かれます。新しいドライバーがprotocolをinstallするたびに、dispatcherは待機中のドライバーがunlockできるかを確認するために再び実行されます。

DEPEXが満足されない一般的な理由:

  • 依存protocolが本当にまだない (提供するドライバーも待機中か動いていない)
  • DEPEXのGUIDにタイポがある: 他のドライバーがinstallしたGUIDと一致しない

3. ドライバーは動いたがログがフィルタリングされている

エントリーポイントは呼ばれましたが、すべてのメッセージがDEBUG_INFOを使っているのにPcdDebugPrintErrorLevelにそのビットが含まれていません。ドライバーが動いていないように見えますが、実際はサイレントに動いています。

ドライバーが動かないときのチェックリスト


Driver Binding: デバイスがブートメニューに出ない理由

ドライバーはdispatchされ、エントリーポイントは動いたが、デバイスはまだブートメニューに出てこない。次に確認するレイヤーはDriver Bindingです。

UEFIでは、バス/デバイスドライバーはエントリーポイントが動いた瞬間にバインドしません。エントリーポイントはEFI_DRIVER_BINDING_PROTOCOLをinstallするだけです。実際のバインドはfirmwareがConnectController()を呼ぶときに起きます——通常はBDSから、またはfirmwareが明示的にデバイスを接続するときです。

01 ConnectController

BDSトリガー

BDSがストレージ/バスコントローラーにConnectControllerを呼ぶ

02 Supported()

プローブ

firmwareが各ドライバーに尋ねる: このコントローラーをサポートできるか?

03 Start()

バインド

ドライバーがコントローラーをclaim、ストレージドライバーにBlock I/O + Device Pathをinstall; パーティションドライバーがパーティションハンドルを作成; ファイルシステムドライバーがSimple File Systemをinstall

04 BDS Boot

ブートオプション

BDSがESP上のSimple File Systemを見つけてブートオプションを解決する

Driver Bindingフロー: 各ドライバーレイヤーが独自のprotocolをpublishする

Supported()が厳しすぎるか、ロジックが間違っているのが、デバイスがバインドされない最も一般的な原因です。

EFI_STATUS
EFIAPI
MyDriverSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  // このコントローラーがサポートするストレージかチェック
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiPciIoProtocolGuid,
                  (VOID **)&PciIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;  // PCI I/Oがない → サポートしない
  }

  // Vendor ID / Device IDを読んで確認
  PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, 1, &VendorDeviceId);

  // returnする前にCloseProtocolしなければならない
  gBS->CloseProtocol (
         ControllerHandle,
         &gEfiPciIoProtocolGuid,
         This->DriverBindingHandle,
         ControllerHandle
         );

  if (VendorDeviceId != MY_DEVICE_ID) {
    return EFI_UNSUPPORTED;
  }
  return EFI_SUCCESS;
}

UEFI Shellでのデバッグ

UEFI Shellが使えるとき、DXEの状態を確認するいくつかのコマンドが役立ちます。

drivers — どのドライバーがロードされたか

Shell> drivers
DRV  VERSION  DRIVER NAME          IMAGE NAME
===  =======  ====================  =======================
1F   00000010 Decompress Driver    Decompress
20   00000010 PC-AT ISA            IsaIo
2C   00000010 PCI Bus Driver       PciBusDxe
...

あなたのドライバーがここにリストされていなければ、まだdispatchされていません (またはFVにありません)。

dh -d <handle> — ハンドルのprotocolを見る

Shell> dh -d 4A
Handle 4A: PciIo DevicePath BlockIo DiskIo SimpleFileSystem

PciIoは見えるがSimpleFileSystemがない場合、ストレージドライバーはコントローラーを認識したが、BDSがそこからブートするのに必要なprotocolを十分にpublishしていません。

devtree — デバイスツリーを見る

Shell> devtree
...
  Ctrl[4A] PCI(0x1F,0x2) - SATA Controller
    Ctrl[4B] Unit 0 ATA Hard Drive (8.0 GB)
      Ctrl[4C] - FAT File System

ストレージコントローラーがdevtreeにあるがATA Hard Driveの子がない場合: Start()でdriver bindingが失敗しています。ドライブはあるがFAT File Systemがない場合: ファイルシステムドライバーがバインドできませんでした。


実際のシナリオ: NVMeディスクがブートメニューに出ない

これはかなり頻繁に遭遇します: 新しいBIOSをフラッシュしたら、NVMeはOSでは検出されているのにUEFIブートメニューには表示されない。

レイヤーごとのアプローチ:

ステップ1: DXEがNVMeドライバーをロードしたか確認

UEFI Shellでdriversを実行します。「NVM Express」またはNVMeドライバーの名前を探します。なければ → FVにドライバーがない、またはDEPEX失敗。

ステップ2: NVMeハンドルに必要なprotocolがあるか確認

Shell> dh -p BlockIo

BlockIoを持つすべてのハンドルをリストします。NVMe関連のハンドルが見えなければ、Start()がまだ動いていないかBlockIoのinstall時に失敗しています。

ステップ3: 強制接続を試みる

BDSがすべてのコントローラーを自動接続しない場合があります。試してみます。

Shell> reconnect -r
Shell> map -r

reconnect -rはフル再接続を強制します。map -rはファイルシステムマッピングをリフレッシュします。これでNVMeが現れれば、問題はドライバーではなくBDSの接続ポリシーです。

ステップ4: UEFIのvariableブートオプションを確認

Shell> dmpstore BootOrder
Shell> dmpstore Boot0000

Boot####がstaleなDevice Pathを持つか、オプションがinactiveであれば、NVMeは正常に動いていても起動選択されません。


まとめ

DXEは最もデバッグすることになるフェーズです。何かがおかしいとき、レイヤーごとにアプローチします。

症状別DXEデバッグ
症状 疑うべきレイヤー 確認すること
POST code 0xD3 Architectural Protocolが欠けている CPU Arch、Timer、またはその他の主要なprotocolがinstallされていない
POST code 0xD4 PCIリソースエラー PCIデバイスのMMIO/IOアパーチャ
POST code 0xD6/D7 Consoleデバイスが欠けている Console output/inputデバイスが作成されていない
POST code 0xD9 LoadImageが失敗 ブートオプションまたはDevice Pathが間違っている
`drivers`にドライバーが見えない Dispatcherがまだ動かしていない FDF、DEPEX、MODULE_TYPE
ドライバーはあるがBlockIoがない Driver Bindingが失敗 Supported()またはStart()
BlockIoはあるが起動しない BDSのパスが間違っている Device Path、Boot####、BootOrder

参考資料


次に読む

この記事は役に立ちましたか?

ファームウェア、BIOS/UEFI、組み込みシステムを学んでいる人に共有できます。

Nội dung liên quan

Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.

Đọc thêm về BIOS/UEFI

Khám phá các bài viết về BIOS/UEFI, embedded firmware, debugging và system-level thinking.