DXEフェーズ: ドライバー、Protocolと何も動かないときのログの読み方
DXEの深掘り: dispatcher、DEPEX、Driver Binding、protocolデータベース。シリアルログ、POST code、UEFI Shellを使って実際の障害をデバッグする方法。
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は:
- メモリサービスを構築する (AllocatePool, AllocatePages, …)
- DXE Dispatcherを起動する
- DispatcherはFirmware Volumeをスキャンし、DEPEXをチェックし、ドライバーをdispatchする
- 各ドライバーはhandle databaseにprotocolをinstallする
- 新しいprotocol → 待機中のドライバーがunlock → さらにdispatch
- 最終的に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–0x8F | DXE実行 (BDS前) |
0x90–0xCF | BDS実行 |
0xD0–0xDF | DXEエラー |
主要なDXEチェックポイント:
| コード | 説明 |
|---|---|
0x60 | DXE Core起動 |
0x61 | NVRAM初期化 |
0x62 | South Bridge Runtime Services |
0x63 | CPU DXE初期化開始 |
0x68 | PCIホストブリッジ初期化 |
0x69 | North Bridge DXE init |
0x6A | North Bridge DXE SMM init |
0x70 | South Bridge DXE init |
0x71 | South Bridge DXE SMM init |
0x72 | South Bridgeデバイスinit |
DXEエラーコード:
| コード | 説明 |
|---|---|
0xD0 | CPU初期化エラー |
0xD1 | North Bridge初期化エラー |
0xD2 | South Bridge初期化エラー |
0xD3 | Architectural Protocolの一部が見つからない |
0xD4 | PCIリソース割り当てエラー |
0xD5 | Legacy Option ROMのスペースなし |
0xD6 | Console Outputデバイスが見つからない |
0xD7 | Console 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_INIT | 0x00000001 | 初期化 |
DEBUG_WARN | 0x00000002 | 警告 |
DEBUG_LOAD | 0x00000004 | イメージロードイベント |
DEBUG_FS | 0x00000008 | ファイルシステム |
DEBUG_POOL | 0x00000010 | Pool確保/解放 |
DEBUG_PAGE | 0x00000020 | Page確保/解放 |
DEBUG_INFO | 0x00000040 | 一般情報 |
DEBUG_DISPATCH | 0x00000080 | Dispatcher情報 |
DEBUG_VARIABLE | 0x00000100 | Variable操作 |
DEBUG_BM | 0x00000400 | Boot Manager |
DEBUG_ERROR | 0x80000000 | エラー (常に表示) |
シリアルログを読む際の重要なパターン
シリアルログがある場合、以下のパターンを探します。
ドライバーエントリーポイント:
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が明示的にデバイスを接続するときです。
BDSトリガー
BDSがストレージ/バスコントローラーにConnectControllerを呼ぶ
プローブ
firmwareが各ドライバーに尋ねる: このコントローラーをサポートできるか?
バインド
ドライバーがコントローラーをclaim、ストレージドライバーにBlock I/O + Device Pathをinstall; パーティションドライバーがパーティションハンドルを作成; ファイルシステムドライバーがSimple File Systemをinstall
ブートオプション
BDSがESP上のSimple File Systemを見つけてブートオプションを解決する
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は最もデバッグすることになるフェーズです。何かがおかしいとき、レイヤーごとにアプローチします。
| 症状 | 疑うべきレイヤー | 確認すること |
|---|---|---|
| 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 |
参考資料
- AMI Aptio 5.x Status Codes, Public Document — 完全なチェックポイントレンジとDXEエラーコード
- AMI Aptio 4.x Status Codes, Public Document — 古いシステム用
- EDK II Debugging, TianoCore Wiki — PcdDebugPropertyMask、PcdDebugPrintErrorLevel
- MdePkg DebugLib.h, GitHub — すべてのDEBUGレベルとマクロ
- UEFI Shell Specification —
drivers、dh、devtree、dmpstore、reconnect
次に読む
- UEFIブートフロー: リセットベクターからExitBootServicesまで — DXEが全体のフローのどこにあるかのコンテキスト
- DXE Dispatcherとは?
- Driver Binding Flowとは?
- Protocolとは?
- ハンドルデータベースとは?
- UEFIのSMM: デバッグ視点からのSystem Management Mode (次の記事)
この記事は役に立ちましたか?
ファームウェア、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.
UEFIブートフロー: リセットベクターからExitBootServicesまで
フェーズ名を暗記するためではなく、マシンがどこで死んでいるかを知りデバッグする方向を掴むためにブートフローを学ぶ。SECからRuntimeまでの実際の障害モードへの地図。
ACPIとは: firmwareがGPIOをトグルして電源を切ってはいけない理由
なぜfirmwareがGPIOをトグルして電源を落としてはいけないのか。ACPIはfirmwareが書きOSが読む契約: テーブル、AML、電力状態、壊れたスリープ/ウェイクのデバッグ。
BIOS Setupとは: クラシックな青い画面からHIIと現代のNVRAMまで
現代のBIOS SetupはHII上に構築されている: VFR、VarStore、NVRAM。structの途中にフィールドを追加するとなぜシステムが壊れるのか、そして保存されない設定のデバッグ方法。
Đọ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.