🐧 Linux 総合学習プラットフォーム
ブートローダ U-Boot ・ 上級

ブートローダの役割

電源投入直後のSoCはROM内の小さなコードしか実行できず、Linuxカーネルをいきなり起動することはできません。ブートローダは、最低限のハードウェア(クロック・DRAM・コンソール)を初期化し、ストレージやネットワークからカーネルを読み込んでメモリ上に配置し、制御を渡す橋渡し役です。組込みでは多段構成(ROMコード→SPL/一次→U-Boot/二次)を取ることが多く、各段が次段を呼び出します。最終的にカーネルへ起動引数(bootargs)とデバイスツリーのアドレスを渡して実行を移譲します。

パソコンでもラズベリーパイのような組込みボードでも、電源を入れた瞬間にいきなりLinuxカーネルが動き出すわけではありません。電源投入直後のSoC(System on Chip)が実行できるのは、チップの中に焼き込まれたごく小さなROMコードだけです。この段階ではメインメモリであるDRAMすらまだ使える状態になっておらず、数十KB程度の内蔵SRAMしか触れません。一方でLinuxカーネルは数MBから十数MBあり、展開すればさらに大きな領域を必要とします。つまり「カーネルを置く場所」も「カーネルを読み出す手段」も、電源直後にはまだ存在しないのです。この大きな隔たりを埋め、最低限のハードウェアを目覚めさせてからカーネルへ制御を渡す橋渡し役が、ブートローダ(bootloader)です。

ブートローダの最初の仕事は、ハードウェアの基礎部分を使える状態にすることです。具体的には、SoC内部のクロック(PLL)を設定して各回路を正しい周波数で動かし、DRAMコントローラを初期化してメインメモリを読み書きできるようにし、シリアルコンソール(UART)を有効にして人間が状況を文字で確認できるようにします。とりわけDRAMの初期化は重要で、これが終わって初めて大きなカーネルイメージを置く広大なメモリ空間が手に入ります。コンソールが立ち上がれば、起動の途中経過やエラーがシリアル端末に文字として流れるようになり、トラブルの切り分けが一気にやりやすくなります。逆に言えば、ブートローダの初期段階でつまずくと画面には何も出ず、原因究明が難しくなるため、ここは組込み開発で最初に向き合う関門になります。

多段構成でつなぐ

組込みでは、ブートローダを一つの大きなプログラムで済ませるのではなく、多段構成(multi-stage)にするのが一般的です。理由は単純で、最初に動けるROMコードや内蔵SRAMがあまりに小さく、フル機能のブートローダをそのまま載せられないからです。そこで処理を段階に分け、各段が次の段を呼び出していく方式を取ります。典型的には、まずチップ内蔵のROMコード(boot ROM)が起動し、それがストレージの先頭にある小さな一次プログラムローダ(SPL: Secondary Program Loader、一次段ローダ)を内蔵SRAM上に読み込んで実行します。SPLはDRAMを初期化したうえで、機能の豊富な二次ブートローダ本体(多くの場合U-Boot)をDRAM上へ読み込み、制御を渡します。

この「ROMコード → SPL/一次 → U-Boot/二次 → Linuxカーネル → ルートファイルシステム」という流れは、組込みLinuxの起動を理解するうえでの背骨になります。各段は「自分より少しだけ賢く大きい次の段を、使えるようになったメモリ領域に読み込んで起動する」という同じ役割を、規模を変えながら繰り返しているだけです。バトンを順に渡すリレーのようなもので、前の走者が次の走者を呼び出し、最後の走者がカーネルにゴールテープを渡します。一次段は小さいSRAMで動く必要から機能を絞り、二次段は広いDRAMの上で動けるため対話コンソールやネットワークなど豊富な機能を持てる、という棲み分けです。

なぜわざわざ段を分けるのか、と疑問に思うかもしれません。理由は、各段に課される制約と求められる機能が大きく異なるからです。最初に動くROMコードはチップ製造時に焼き込まれて後から変更できず、容量も極小です。だからROMコードは「ストレージの決まった場所から次の段を読んで実行する」という必要最小限の働きだけを担います。SPLは内蔵SRAMという狭い土俵で動くため、DRAM初期化のような重要だが限定的な仕事に集中します。そしてU-Boot本体は、SPLが用意した広いDRAMの上で初めて、対話コンソール・各種ストレージ・ネットワークといった重装備を展開できます。一枚岩の巨大なプログラムを最初から動かそうとすると、最も制約の厳しい初期段階の都合にすべてが縛られてしまいます。段階に分けることで、それぞれの段が自分の持ち場に最適化でき、全体として無理なく起動を成立させられるのです。

カーネルへ渡すもの

最終段のブートローダがカーネルを起動するとき、ただプログラムへジャンプするだけでは足りません。カーネルが正しく動くために、二つの大切な情報を一緒に渡します。一つはカーネル起動引数(bootargs)で、ルートファイルシステムがどこにあるか、コンソールにどのデバイスを使うかといった、起動時の振る舞いを決める文字列です。もう一つはデバイスツリー(DTB)のメモリ上のアドレスで、これによりカーネルは自分が載っているハードウェアの構成を知ります。ブートローダはストレージやネットワークからカーネルとDTBをメモリ上の決まったアドレスに配置し、bootargs を整え、最後にカーネルのエントリポイントへ制御を移譲します。この受け渡しがうまくいって初めて、カーネルは周辺機器を認識し、ルートファイルシステムをマウントして、本来のLinuxとして動き出せます。逆に、bootargs の指定が誤っていてルートファイルシステムが見つからない場合、カーネルは起動の最後で「VFS: Unable to mount root fs」のようなメッセージを出して止まります。ブートローダが渡す情報の正しさが、そのままカーネルの起動成否を左右するわけです。

実務では、ブートローダは単なる「起動の通過点」ではなく、開発と量産の両方を支える重要な道具です。開発中はカーネルやルートファイルシステムを何度も差し替えるため、ブートローダの対話機能を使ってネットワーク越しにイメージを読み込み、反復を高速化します。量産時には、確定した起動手順をブートローダに固定し、電源を入れれば誰の操作も要らずにカーネルへ進むよう仕立てます。後続のトピックで扱うU-Boot(boot-uboot)、その環境変数、カーネルとDTBのロード、TFTP起動といったテーマは、すべてこの「ブートローダがカーネルへ橋を架ける」という土台の上に積み上がっていきます。まずは、電源直後の無力なチップが、段階を踏んでLinuxを起動できる状態まで自分を引き上げていく——その全体像を頭に入れておきましょう。

この項目に出てくる用語

ブートローダぶーとろーだ
OS本体を起動する前に走り、カーネルを読み込んで制御を渡すプログラム。
セカンドステージせかんどすてーじ
ROMコードやSPLの次に動く本格的なブートローダ段。U-Bootが担う。
U-Bootゆーぶーと
組込みLinuxで広く使われるオープンソースの二次ブートローダ。

関連コマンド

bootbootm

▶ 学習アプリでこの続きを学ぶ・演習する