はじめに (対象読者・この記事でわかること)

この記事は、組み込みLinux開発に携わる方、Linuxカーネルのビルドに興味がある方、特にU-Bootブートローダ環境でカスタムカーネルを導入したい方を対象としています。

この記事を読むことで、Linuxカーネルの特殊なイメージ形式であるuImageの概念を理解し、LinuxカーネルのソースコードからuImageとデバイスツリー(DTB)をビルドする方法、そしてU-Bootブートローダを介してそれらをターゲットボードにロードし、ブートさせる一連の手順を習得できます。組み込みシステム開発で頻繁に遭遇するuImageの扱いに戸惑うことがなくなるでしょう。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - Linuxコマンドラインの基本的な操作 - クロスコンパイルの概念(特にARMアーキテクチャ向け) - U-Bootブートローダに関する基本的な理解

Linux KernelにおけるuImageとは?その必要性

Linuxカーネルイメージには、いくつかの種類があります。一般的なPC環境ではvmlinuzや圧縮されたbzImage/zImageが使われますが、組み込みシステム、特にU-Bootブートローダを使用するARMアーキテクチャのボードでは、uImageという形式がよく利用されます。

uImageは、標準的なカーネルイメージにU-Bootが理解できる特殊なヘッダ情報が付加されたものです。このヘッダには、イメージのサイズ、ロードアドレス、エントリーポイント、OSタイプ、アーキテクチャ、イメージタイプ、圧縮タイプなどのメタデータが含まれています。U-Bootはこのヘッダを読み取ることで、イメージがLinuxカーネルであることを認識し、正しいアドレスにロードして実行することができます。

なぜuImageが必要なのでしょうか?それは、U-Bootが単純なバイナリイメージをロードするだけでなく、ロードするイメージの種類やそのプロパティを事前に知る必要があるためです。これにより、U-Bootはより柔軟かつ安全にカーネルをブートできます。また、現代のARM Linuxでは、ハードウェア構成を記述するデバイスツリーバイナリ(DTB: Device Tree Blob)が不可欠です。uImageはDTBと連携してロードされることが多く、DTBをカーネルイメージとは別にロードしたり、あるいはuImage内にDTBを組み込んだりする方法があります。

uImageのビルドとU-Boot環境へのインストール手順

ここからは、実際にLinuxカーネルのソースコードからuImageをビルドし、U-Bootを介してターゲットボードにインストールする具体的な手順を解説します。

前提準備: 開発環境のセットアップ

ターゲットボード向けにカーネルをビルドするには、クロスコンパイラとU-Bootツールが必要です。

  1. クロスコンパイラのインストール: ターゲットのARMアーキテクチャに合ったクロスコンパイラをインストールします。例えば、Debian/Ubuntu系の場合は以下のコマンドでインストールできます。

    bash sudo apt update sudo apt install gcc-arm-linux-gnueabihf build-essential libncurses-dev

  2. mkimageツールのインストール: uImageを生成するために必要なmkimageコマンドは、U-Bootツールの一部です。

    bash sudo apt install u-boot-tools

  3. Linuxカーネルソースの取得: ビルドしたいLinuxカーネルのソースコードをgitでクローンします。

    bash git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git cd linux 必要に応じて特定のバージョンにチェックアウトしてください。

ステップ1: カーネルソースの取得と設定

クローンしたカーネルソースのディレクトリに入り、ターゲットボードに合わせた設定を行います。

  1. ビルドディレクトリの作成 (オプションだが推奨): ソースツリー外にビルドディレクトリを作成することで、ソースツリーをクリーンに保てます。

    bash mkdir ../linux_build export KERNEL_OUT_DIR=$(readlink -f ../linux_build)

  2. クロスコンパイラパスの設定: ビルド時に使用するクロスコンパイラのプレフィックスをCROSS_COMPILE環境変数に設定します。

    bash export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf-

  3. デフォルトコンフィグの適用: ターゲットボードに合わせたデフォルトコンフィグファイルを適用します。<board_defconfig>はターゲットボードのdefconfig名に置き換えてください(例: multi_v7_defconfig, am335x_evm_defconfigなど)。これらのファイルはarch/arm/configs/以下にあります。

    bash make O=$KERNEL_OUT_DIR <board_defconfig>

  4. 設定のカスタマイズ (必要に応じて): menuconfigを使って、カーネルオプションをカスタマイズします。

    bash make O=$KERNEL_OUT_DIR menuconfig ここで、CONFIG_ARM=yCONFIG_UBOOT_COMPAT=yなどが有効になっていることを確認してください。General setup -> U-Boot / RedBoot firmware compatibilityなどでU-Boot互換性が有効になっているか確認できます。

ステップ2: uImageとDTBのビルド

設定が完了したら、いよいよuImageとDTBをビルドします。

Bash
make O=$KERNEL_OUT_DIR uImage dtbs

このコマンドはuImage (arch/arm/boot/uImage) と、指定したボード用のDTBファイル(arch/arm/boot/dts/*.dtb)を生成します。ビルドには時間がかかる場合があります。-jオプションで並列ビルドを行うと高速化できます(例: make -j$(nproc) O=$KERNEL_OUT_DIR uImage dtbs)。

ビルドが成功すると、$KERNEL_OUT_DIR/arch/arm/boot/uImage$KERNEL_OUT_DIR/arch/arm/boot/dts/以下にDTBファイルが生成されます。

ステップ3: uImageとDTBのインストールとU-Boot設定

ビルドしたuImageとDTBファイルをターゲットボードに転送し、U-Bootからブートさせます。転送方法はSDカード、NFS、TFTPなど、環境によって異なります。ここでは、SDカードのFATパーティションに配置し、U-Bootからロードする例を挙げます。

  1. ファイルの転送: ビルドしたuImageと、ターゲットボードに対応するDTBファイル(例: am335x-evm.dtb)をSDカードの第一パーティション(FAT32形式)のルートディレクトリにコピーします。

    ```bash sudo cp $KERNEL_OUT_DIR/arch/arm/boot/uImage /media/$USER/BOOT/ sudo cp $KERNEL_OUT_DIR/arch/arm/boot/dts/am335x-evm.dtb /media/$USER/BOOT/

    /media/$USER/BOOT/ はSDカードのマウントポイントに置き換えてください

    ```

  2. U-Bootからのブート: ターゲットボードを起動し、U-Bootプロンプト(=>)が表示されたら以下のコマンドを実行します。 (アドレスはボードのRAM構成によって適宜変更してください。0x80000000はカーネルロードアドレス、0x81000000はDTBロードアドレスの例です。)

    text => fatload mmc 0:1 0x80000000 uImage => fatload mmc 0:1 0x81000000 am335x-evm.dtb => setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait # ルートファイルシステムやコンソール設定 => bootm 0x80000000 - 0x81000000 * fatload mmc 0:1: SDカードのパーティション1からファイルをロードします。 * 0x80000000: uImageをロードするメモリのアドレス。 * uImage: SDカード上のファイル名。 * bootargs: Linuxカーネルに渡す起動引数。ルートファイルシステムやコンソールポートなどを指定します。 * bootm: uImageをブートするコマンド。-はRAMディスクを使用しないことを示します。0x81000000はDTBのアドレスです。

  3. boot.scrによるスクリプト化: U-Bootコマンドを毎回手入力するのは手間がかかります。boot.scrというスクリプトファイルを作成することで、自動的にこれらのコマンドを実行できます。

    まず、boot.txtというテキストファイルを作成します。

    ```text

    boot.txt の例

    setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait fatload mmc 0:1 0x80000000 uImage fatload mmc 0:1 0x81000000 am335x-evm.dtb bootm 0x80000000 - 0x81000000 ```

    次に、mkimageを使ってこのテキストファイルをU-Bootが解釈できるboot.scrバイナリファイルに変換します。

    bash mkimage -A arm -O linux -T script -C none -d boot.txt boot.scr 生成されたboot.scrファイルをuImageやDTBと同様にSDカードのFATパーティションにコピーします。U-Bootは起動時に自動的にこのboot.scrを探して実行します。

ハマった点やエラー解決

mkimageコマンドが見つからない

症状: make uImageコマンドやmkimage -A ...コマンドを実行した際に、mkimage: command not foundのようなエラーが表示される。 原因: u-boot-toolsパッケージがインストールされていないため、mkimageコマンドがシステムパス上に存在しません。 解決策: u-boot-toolsパッケージをインストールします。

Bash
sudo apt install u-boot-tools

CROSS_COMPILEが正しく設定されていない、またはコンパイラが見つからない

症状: makeコマンド実行時に、「arm-linux-gnueabihf-gcc: command not found」や「No rule to make target 'uImage'」といったエラーが表示される。 原因: CROSS_COMPILE環境変数が正しく設定されていないか、指定されたクロスコンパイラがシステムパス上にありません。 解決策: 1. export CROSS_COMPILE=arm-linux-gnueabihf- のように、コンパイラのプレフィックスを正しく設定しているか確認します。 2. which arm-linux-gnueabihf-gccを実行し、クロスコンパイラがパス上に存在するか確認します。存在しない場合は、クロスコンパイラがインストールされているディレクトリをPATH環境変数に追加します。 bash export PATH=/opt/toolchains/arm-linux-gnueabihf/bin:$PATH # 例

DTBが見つからない、またはU-BootがDTBを認識しない

症状: U-Bootのbootmコマンドでカーネルが起動しない、または起動してもDTB関連のエラーが出て停止する。 原因: 1. DTBファイルがターゲットボードに転送されていない、またはファイル名が間違っている。 2. U-Bootのfatloadコマンドで指定したDTBファイルのパスやアドレスが間違っている。 3. ロードしたDTBファイルが、実際に使用しているボードと一致しない(カーネルがDTBを解釈できない)。 解決策: 1. uImageと一緒に正しいDTBファイルをSDカードにコピーし、ファイル名を確認します。 2. U-Bootプロンプトでfatloadコマンドが成功しているか(bytes readの表示)を確認します。ロードアドレスも正確か再確認します。 3. make O=$KERNEL_OUT_DIR dtbsでビルドされたDTBファイルが、実際にターゲットボードで使用されているdefconfigと対応するものであることを確認します。必要であれば、カーネルソースのarch/arm/boot/dts/ディレクトリにある該当の.dtsファイルを確認し、DTBを再ビルドします。

U-BootがuImageを認識しない

症状: bootmコマンド実行時に「Bad magic number」や「Wrong Image Format」のようなエラーが表示される。 原因: ロードされたイメージがuImage形式として正しくない可能性があります。 1. mkimageコマンドが正しく実行されていない。 2. make uImage時に何らかのエラーが発生していた。 3. コピー中にファイルが破損した。 解決策: 1. $KERNEL_OUT_DIR/arch/arm/boot/uImageが正しく生成されているか確認します。 2. mkimage -l $KERNEL_OUT_DIR/arch/arm/boot/uImageを実行し、uImageのヘッダ情報が正しく表示されるか確認します。表示されない場合は、ビルドプロセスに問題がある可能性があります。 3. ファイルの転送方法を再確認し、ファイルが破損していないことを保証します。

解決策

上記のエラーは、多くの場合、開発環境の準備不足やコマンドの入力ミス、パスの誤りなどが原因で発生します。問題が発生した場合は、エラーメッセージを注意深く読み、以下の点を確認することが重要です。

  • 環境変数: ARCHCROSS_COMPILEKERNEL_OUT_DIRが正しく設定されているか。
  • ツールのインストール: mkimageやクロスコンパイラが正しくインストールされ、パスが通っているか。
  • コンフィグ: ターゲットボードに合ったdefconfigが適用されているか、menuconfigでU-Boot互換性が有効になっているか。
  • ビルド出力: makeコマンドの出力にエラーがないか、uImagedtbファイルが期待通りの場所に生成されているか。
  • ファイル転送: ターゲットボードにコピーしたファイルが破損しておらず、正しい場所に配置されているか。
  • U-Bootコマンド: fatloadbootmの引数(アドレス、ファイル名)が正確か。

これらのチェックリストを順に確認することで、多くの問題は解決できるはずです。

まとめ

本記事では、Linux KernelのuImageに関する基本的な知識から、そのビルド、そしてU-Boot環境へのインストール方法までを詳細に解説しました。

  • uImageの理解: uImageはU-Bootがカーネルを認識し、ブートするために必要なヘッダ情報を持つ特殊なカーネルイメージ形式です。
  • ビルドプロセス: クロスコンパイラとu-boot-toolsを準備し、カーネルソースの設定、make uImage dtbsコマンドでuImageとデバイスツリー(DTB)を生成します。
  • U-Bootへの導入: 生成されたuImageとDTBをターゲットボードに転送し、U-Bootのfatloadbootmコマンド、またはboot.scrを利用してカーネルを起動させます。

この記事を通して、U-Bootブートローダを使用する組み込みシステムで、カスタムLinuxカーネルを導入する基礎的なスキルと、トラブルシューティングの基本的な考え方を得られたことでしょう。

今後は、initramfsの導入によるルートファイルシステムの起動、カスタムデバイスツリーの作成、カーネルデバッグ手法など、さらに発展的な内容についても記事にする予定です。

参考資料