BIOS Setupとは: クラシックな青い画面からHIIと現代のNVRAMまで
現代のBIOS SetupはHII上に構築されている: VFR、VarStore、NVRAM。structの途中にフィールドを追加するとなぜシステムが壊れるのか、そして保存されない設定のデバッグ方法。
「BIOSに入る」とはfirmwareのSetupに入ること
2000年代以前のコンピュータで育った人なら、あの画面を覚えているでしょう。電源を入れた直後にDelかF2を押すと、白いテキストが並んだ青や灰色の画面が出てくる。矢印キーで移動、Enterで選択、+/-で値を変える。マウスなし、グラフィックなし、何もモダンなものはない。
それがLegacy BIOS Setup ——AMI BIOS、Award BIOS、Phoenix BIOSでした。その時代には十分に機能しましたが、UIの柔軟性、多言語サポート、拡張性において厳しい制限がありました。
今日、最新のラップトップやサーバーの「BIOS」に入ると何が見えるでしょうか?マウスサポートがある多彩なグラフィカルインターフェース、検索ボックス、時にはベンダーロゴまで。複数の言語に対応。タブと構造化されたサブメニューで整理されている。
多くの人はまだこれを「BIOSに入る」と呼んでいて、機能的には同じことをするという意味では完全に間違いではありません。しかし内部的にはHII (Human Interface Infrastructure)と呼ばれるまったく異なるアーキテクチャで構築されています。
Setupへの入り方、そして「BIOS」という名前が残り続けた理由
UEFI Setupに入る一般的な方法:
DelかF2— 最も一般的、ほとんどのPCとラップトップで動作F10— HPで一般的F1— 伝統的なIBM/Lenovo ThinkPadEsc— 一部のAsusやSonyのラップトップ
一部の最新システムではブートウィンドウが速すぎてキーストロークを挟めません。Windowsから: 設定 → 回復 → 詳細な起動 → 今すぐ再起動 → トラブルシューティング → UEFIファームウェアの設定。
HII: 洗練されたUIの背後にあるアーキテクチャ
Legacy BIOS Setupが画面に直接描画するモノリシックなCコードだとすれば、UEFI HIIはデータ、インターフェース、ストレージを明確に分離した構造化されたパイプラインです。
HII (Human Interface Infrastructure)は以下から構成されます。
VFRソース → フォーム、クエスチョン、表示条件を記述する
.UNIファイル → 多言語表示文字列 (英語、日本語、ベトナム語...)
ビルドツール → VFRをIFRバイナリにコンパイル、UNIを文字列パッケージに
DXEドライバー → パッケージをHII Databaseにinstallする
Setup Browser → IFRを読み取り、UIをレンダリングし、入力を処理する
VarStore → 各クエスチョンの値のバッキングストレージ
Config Access → 保存/検証/通知のドライバーコールバック
NVRAM → データが最終的に永続化される場所
このアーキテクチャの優れた点: 各DXEドライバーはBrowserのコードを変更せずに独自のフォームをSetupに追加できます。BrowserはHII Databaseを読み取ってレンダリングするだけで、何個のドライバーがフォームを提供しているかを知る必要がありません。
これが現代のサーバーのSetupメニューが複雑なサブメニューにわたって何百ものオプションを持ちながら、コードベースは明確な構造を保てる理由です。
VFR: Setup UIのソースコード
開発者は画面描画コードを書きません。フォーム、クエスチョン、表示条件を記述する言語であるVFR (Visual Forms Representation)を書きます。
varstore SETUP_DATA,
varid = Setup,
name = Setup,
guid = SETUP_GUID;
form formid = FORM_BOOT_MAIN,
title = STRING_TOKEN(STR_BOOT_FORM);
oneof varid = Setup.SataMode,
prompt = STRING_TOKEN(STR_SATA_MODE),
help = STRING_TOKEN(STR_SATA_MODE_HELP),
questionid = QUESTION_ID_SATA_MODE;
option text = STRING_TOKEN(STR_AHCI), value = 0, flags = DEFAULT;
option text = STRING_TOKEN(STR_RAID), value = 1;
endoneof;
checkbox varid = Setup.FastBoot,
prompt = STRING_TOKEN(STR_FAST_BOOT),
help = STRING_TOKEN(STR_FAST_BOOT_HELP),
questionid = QUESTION_ID_FAST_BOOT;
endcheckbox;
endform;
VFRはバイナリバイトコードであるIFR (Internal Forms Representation)にコンパイルされます。Setup BrowserはVFRソースではなくIFRを読みます。これはデバッグに重要な意味を持ちます: IFR Extractorがあれば、ソースコードなしにBIOSイメージからSetupフォームの構造を逆読みできます。
VarStoreとstruct: データが実際に住む場所
VFR内の各クエスチョンにはVarStore——値を読み書きするバッキングストレージ——が必要です。
最も一般的なタイプはC structに直接マップするBuffer VarStoreです。
// ヘッダーファイルで定義
typedef struct {
UINT8 FastBoot; // オフセット 0x00
UINT8 SataMode; // オフセット 0x01
UINT8 NetworkStack; // オフセット 0x02
UINT8 Reserved; // オフセット 0x03、アラインメントパディング
UINT16 BootTimeout; // オフセット 0x04
UINT8 TpmDevice; // オフセット 0x06
UINT8 SecureBootUi; // オフセット 0x07
} SETUP_DATA;
ユーザーが「SATAモード」をRAIDに切り替えて保存すると、firmwareはこのバッファのオフセット0x01に値1を書き込みます。次のブート時に、PEIまたはDXEドライバーがバッファを読み戻し、Setup.SataModeを読み、それに応じてストレージコントローラーを設定します。
オフセットの問題: なぜ小さな変更がシステムを壊すのか
これは私が一度やって長い時間をかけて理解したタイプのバグです。POSのfirmwareに新機能のフラグを追加していました——単なるUINT8で、「きれいに見える」という理由でstructの途中に挿入しました。ビルドして、フラッシュして、テストマシンで起動: ハードドライブが見えない。シリアルログは特に問題なし。ケーブルを確認、SATAコントローラーを確認、DXEドライバーを確認——すべて問題なし。
NVRAMをダンプして新しいstructと各バイトのフィールドオフセットを比較することを思いつくのに半日近くかかりました。1バイトずれていました。SataModeが隣のフィールドの値を読んでいました。
なぜこうなるかを説明します。SETUP_DATAに新しいフィールドを追加する必要があるとします。途中に挿入します。
// フィールドを追加する前
typedef struct {
UINT8 FastBoot; // オフセット 0x00
UINT8 SataMode; // オフセット 0x01
UINT8 NetworkStack; // オフセット 0x02
UINT8 Reserved; // オフセット 0x03
UINT16 BootTimeout; // オフセット 0x04
} SETUP_DATA;
// 途中にフィールドを追加した後
typedef struct {
UINT8 FastBoot; // オフセット 0x00
UINT8 NewFeature; // オフセット 0x01、ここに追加
UINT8 SataMode; // オフセット 0x02、ずれた
UINT8 NetworkStack; // オフセット 0x03
UINT8 Reserved; // オフセット 0x04
UINT16 BootTimeout; // オフセット 0x05、ずれた
} SETUP_DATA;
C structは変わりました。しかしNVRAMは古いレイアウトでデータを保持したままです。新しいfirmwareが起動してSetup.SataModeをオフセット0x02で読むとき、実際には古いレイアウトのNetworkStackだった値を読んでいます。
結果: SATAモードが間違い、ストレージコントローラーがドライブを認識しない、マシンが起動しない。あるいはさらに悪く、TPMのようなセキュリティ関連のフィールドが誤読されて連鎖的なデバッグしにくい結果につながります。
これはまた、BIOSプロジェクトをメンテナンスのために引き継ぐとき、多くのエンジニアが最初にすることがすべてのNVRAM variableをダンプしてソースコード内のstructとフィールドオフセットを比較することである理由でもあります。1バイトずれただけですべてが間違って読まれます。
完全なフロー: ユーザーのクリックからブート時に使われる設定まで
ユーザーがSetupに入ってオプションを変更し保存するとき、実際には以下のことが起きています。
Browserが一時保持
新しい値はブラウザーストレージに保持される。NVRAMにはすぐに書かない場合がある。ドライバーが登録していればコールバックが呼ばれる
RouteConfig()が呼ばれる
BrowserがFormSetを所有するドライバーのRouteConfig()を呼ぶ。これが実際にデータをコミットするステップ
NVRAMに書き込む
ドライバーがSetVariableを呼ぶ。ここで失敗すると、UIは正しい値を表示するがリブート後に元に戻る
システムがリブート
マシンがSPI flash内の新しいデータで再起動する
PEI/DXEが設定を読む
GetVariable → Setup.SataMode == 1 → コントローラーがRAIDモードで設定される
見落としやすいステップ: 多くの実装では、Setup BrowserはユーザーがオプションをA変更した瞬間にNVRAMを書き込みません。Save & ExitのときだけRouteConfig()が呼ばれ、Config Accessドライバーが実際のvariable storeに書き込みます。
SetVariable()がエラーを返した場合——例えばフラッシュ保護がすでに有効でEFI_WRITE_PROTECTEDが返る場合——UIは新しい値を表示しますがNVRAMには古い値のまま。リセット後、すべてが元に戻ります。
デバッグ: リブート後に保存されない設定
BIOSの作業で最も一般的なケース。アプローチ:
| ステップ | 確認すること | 失敗の意味 |
|---|---|---|
| 1 | RouteConfigは実行されるか? | されない場合、問題はFormSet GUIDかConfig Access Protocolの登録にある可能性がある |
| 2 | SetVariableは成功するか? | 失敗する場合、EFI_WRITE_PROTECTEDまたはEFI_ACCESS_DENIEDなどのステータスを確認 |
| 3 | リブート後のNVRAMは正しいか? | オフセットがずれている場合、マイグレーションなしにstructレイアウトが変わった可能性がある |
| 4 | ConsumerはA正しいフィールドを読んでいるか? | PEI/DXEモジュールが間違ったGUID、名前、またはオフセットを読んでいる可能性がある |
| 5 | デフォルト復元パスが上書きしていないか? | CMOS clearまたはLoad Defaultがvariableを上書きする可能性がある |
ツーリング
AMIやInsydeのようなベンダーは通常、Setupフォーム、variable、IFRコンテンツ、BIOSイメージを検査するツールを持っています——AMI Debug Rx、InsydeH2O variable editor、Lauterbachの UEFI awareness。これらは特にソースコードへの完全なアクセスがない場合に役立ちます。
しかしどのツールを使っても、デバッグする人はHIIパイプラインとVarStoreのレイアウトを理解する必要があります。ツールは「このオフセットがずれているかもしれない」とヒントを出せますが、それがなぜ危険で古いバージョンからのアップグレードパスを壊さずにどう修正するかは——技術的な基盤がなければ判断できません。
まとめ
- **「BIOSに入る」**とは今日ではUEFI Setup: HIIアーキテクチャであり、Legacy BIOSではない
- HIIはUI (VFR/IFR)、ストレージ (VarStore/NVRAM)、ロジック (Config Access/コールバック)を明確に分離する
- VarStoreのオフセットは極めてデリケート——structの途中にフィールドを追加すると古いNVRAMデータが壊れる
- 保存フローはBrowser → RouteConfig → SetVariable → NVRAM → consumerを経由する
- デバッグは表示 → ストレージ → consumerの同じレイヤーをたどるべき
参考資料
- UEFI Specification, HII Chapter — 公式のHII spec、第32章と33章
- EDK II HII modules, GitHub — リファレンス実装
- Insyde Software Development Tools — Insyde H2O開発ツール
- Lauterbach UEFI H2O Awareness — HII可視化付きデバッグツール
次に読む
この記事は役に立ちましたか?
ファームウェア、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.
BIOSとUEFIはどこが違うのか?
現代のマシンがUEFIを使っているのに「BIOSに入る」と言い続けるのはなぜか。BIOS・UEFI・ブートフロー・Secure Boot・Boot####とOSが動く前にfirmwareがシステムを構築する仕組みを解説。
ACPIとは: firmwareがGPIOをトグルして電源を切ってはいけない理由
なぜfirmwareがGPIOをトグルして電源を落としてはいけないのか。ACPIはfirmwareが書きOSが読む契約: テーブル、AML、電力状態、壊れたスリープ/ウェイクのデバッグ。
DXEフェーズ: ドライバー、Protocolと何も動かないときのログの読み方
DXEの深掘り: dispatcher、DEPEX、Driver Binding、protocolデータベース。シリアルログ、POST code、UEFI Shellを使って実際の障害をデバッグする方法。
Đọ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.