ビルドしたLinuxカーネルをブートできる最低限の環境を用意する(with Busybox & qemu)

最近Linuxカーネル周りを触り始めています。Linuxカーネル周りを触り始めたといってもそもそもLinux歴が浅く、menuconfigを本格的にいじりながらビルドするのは今回が初めてだったりしてブートするだけでも大変でした。そこらへんをまとめます。

カスタムビルドしたLinuxカーネルをブートできる環境を簡単に用意する

今更何だという感じですが、Linuxとは正確にはカーネルの部分だけのことを指していて、ユーザーランドを含みません。巷でよく言われるアレですね。(もしあなたが十分に信心深いなら、Linuxシステム全体をLIGNUXと呼んでもよいです。)
なのでkernel.orgから引っ張ってきたカーネルソースをビルドしても、すぐには起動して試すことができません。ubuntuなどのディストリのカーネルを入れ替える形でブートさせてもいいのですが、 カーネルソース自体をいじっていてテストをしたいときなど、あまりデカいディストリを振り回したくない状況もあるでしょう。本記事ではBusyBoxで簡単なユーザーランドを作ってqemu上でカーネルをブートし、カーネルの動作を確認できるように基本的なコマンドを使える環境を構築する方法を紹介します。

注意

この記事は、特にARMをターゲットとしていないことと一般的なカーネルビルドの方法もまとめて書いてあることを除いては Busybox for ARM on QEMU | Freedom Embedded に書いてあること以上のことは何も書いていません。BusyBoxを用いてファイルシステムを構築する方法を手早く知りたければこちらを参照することをオススメします。

カーネルをビルドする

まず一般的なカーネルのビルド方法をメモ書きしておきます。

とにもかくにもカーネルのソースが必要です。kernel.orgからほしいバージョンのソースを入手しましょう。適当にアーカイブを展開し、展開したディレクトリに移動すれば

$ make menuconfig

を叩くことによってカーネルの各種オプションを設定できます。なおこの記事ではinitramfsを使ってブートするので、General Optionsの中のinitramfsをサポートするオプションは無効化しないでください。
ちなみにこれは、GUIバージョンもあり、

$ make xconfig

および

$ make gconfig

を使うことができます。それぞれKDEGNOMEを使うGUIです。
また、色々いじった設定を元に戻したければ、

$ make defconfig

すればオーケーです。

次にやることは本番のビルドです。

$ make -j4

とすればオーケー。普通ですね。もちろん-j4の部分のスレッド数は適宜あなたのコア数などに合わせて置き換えてください。だいたい10分くらいビルドに時間がかかるように思います。

さて、ubuntuなどのディストリのカーネルを入れ替える場合は普通この後make installとかするわけですが、今回はこのカスタムカーネルをとりあえずブートしてテストしたいだけなのでやりません。ユーザーランドをBusyBoxで作っていきます。

BusyBoxでユーザーランドを作る

次にBusyBoxを使ってinitramfsを作っていきます。なお、今回qemu上で動かすLinuxは、RAMのみのディスクなし、ユーザーランドもBusyBoxのコマンド群のみという構成になります。またアーキテクチャは、特に指定していない場合ホストのマシンと一致されます。

カーネルの時と同じく、まずBusyBoxのソースを用意しましょう。適当に展開した後は、そのディレクトリにて

$ make menuconfig

を叩くと、BusyBoxの設定のコンフィグ画面が表示されるので、ライブラリを静的にリンクするようにしてください。
その後、

$ make install

を実行してください。make installではありますが、ホスト上のマシンに何かがインストールされるわけではなく、単に_installというディレクトリができるだけです。sudoはつけないようにしましょう。
次に、起動後ルートファイルシステムとなることになるファイルを作ります。

$ cd _install
$ find . | cpio -o --format=newc > ../rootfs.img

もし容量を小さくしたい場合は、さらに先ほどの参考サイトに従って圧縮をかけてもいいです。

qemuカーネルをブートする

qemuは-kernelというオプションがあり、カーネルのelfバイナリを読んで直接メモリ上に配置してくれます。これによって特にブートローダを用意せずともカーネルをブートすることができます。さらにinitrdオプションを使って先ほど作ったrootfs.imgを渡します。
以下のコマンド

$ qemu-system-x86_64 -kernel bzImage -initrd rootfs.img -append "root=/dev/ram rdinit=/bin/sh"

を走らせることで、BusyBoxによるシェルが起動します。あとは普通にlsやらcatやらが使用できると思います。お疲れ様でした。

オプション:/procや/sysを機能させる

参考サイトにもあるフォローアップをこの記事でも書いておきます。
上までの手順では、/procや/sysが必要になる機能、たとえばpsコマンドが動きません。なのでこれらを機能させたければきちんとマウントする必要があります。

# mkdir /proc
# mount -t proc none /proc
# mkdir /sys
# mount -t sysfs none /sys
# mdev -s

これらを毎回行いたければ、proc, sysディレクトリおよびetc/init.d/rcSスクリプトをrootfs.imgの中に追加し、qemuのappendに書くブートパラメータのrdinitを/bin/shではなく/sbin/initに変更しましょう。
rcSの内容はだいたい以下のようになります。

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s

ざーっと要点だけ書いてしまっていますので、詳しくは元ネタのBusybox for ARM on QEMU | Freedom Embeddedの方を参照してください。