読者です 読者をやめる 読者になる 読者になる

Raspberry Piでカーネルをカスタムして構築

電波 Raspberry Pi

f:id:karaage:20150921233833j:plain:w640

そろそろカーネルを弄ってみたい

 最近結構Raspberry Piを色々弄っています。

 ただ、ソフト的にはほんとにちょっとしたスクリプトをかいているだけで非常に浅いので、もうちょっと深いところもやりたいなと思い、今まで一度もやったことのないカーネルのカスタムにチャレンジしようかなと思い立ちました。
 カーネルというはRaspberry PiのOSのずばり核となる部分ですが、オープンソースなので基本公開されていて自由にソースを弄ってビルド(コンパイル)することができるわけです。カーネルを弄ると普通のアプリではできないような機能を加えたり、逆に普通は削除できない機能を削除して起動を極限まで高速化したり色々できるのです(多分)。ゴイスー。逆にいうと色々出来すぎるが故に、失敗するとRaspberry Piが起動しなくなったり使えなくなる機能が出てきたりします(多分)。
 といっても失敗しても特に怒られるわけでもないので、軽い気持ちでカーネルをビルドしてみることにしました。

 この後は基本マニアックなので、続き読みたい人だけ続きを読んでください(投げ槍)

カーネル構築

 基本的には以下の公式サイトの情報の通りにやるのがよさそうです。ただ英語なのと、少し順番が前後したりしていて自分みたいな初心者には分かりにくいところもあったので、メモ代わりにやったことを記載していきます。

 まず前提としてカーネルをビルドする方法には、セルフコンパイルとクロスコンパイルがあって、Raspberry Pi2のカーネルの場合だと、カーネルを動かす環境とコンパイル(ビルド)する環境が同じ場合、つまりRaspberry Piでビルドする場合をセルフコンパイルと呼び、カーネルを動かす環境とコンパイル(ビルド)する環境が異なる場合、例えば別のPCでRaspberry Piのカーネルをコンパイル(ビルド)することをクロスコンパイルと呼びます。クロスコンパイルすると、パワーのあるPCを使って早くビルドを完了させることができるのですが、その分ビルドに色々手間が必要となります。今回は最初ということもありますので、Raspberry Piでのセルフコンパイルを実施します。

作業ディレクトリ作成・移動

 参考サイトによるとカーネルを置く場所は /usr/local/src でなく /home/pi 以下が正しいらしいです。なので以下のコマンドで、ホームディレクトリ以下にkernelというディレクトリ作成して、以下その中で作業します。raspbianのREADMEにも自分がパーミッションを持っているディレクトリに置けって書いてありますね。

$ cd
$ mkdir kernel
$ cd kernel

カーネルダウンロード

 linuxのカーネルを、gitというソフト開発のシステムを用いてダウンロード(clone)。--depth=1 というオプションで最新版だけ持ってきます。カーネルは容量大きいのでオプション付けた方がよいと思います。以下コマンドね。

$ git clone --depth=1 https://github.com/raspberrypi/linux

 あとは、よくわからないのですが、bcというやつを入れないと、カーネルをビルドできないらしいので何も考えずついでにダウンロードしてインストール。

$ sudo apt-get update
$ sudo apt-get install bc

カーネルのコンフィギュレーション(Configuration)作成

 カーネルをビルドするときの設定を色々弄ります。まずは、以下のコマンドでRaspberry Pi 2のデフォルトのコンフィグ設定を作成します。

$ cd linux
$ KERNEL=kernel7
$ make bcm2709_defconfig

 上記コマンドで.configというファイルが生成されます。これがカーネルのビルドの設定が書き込まれたファイルです。RASPBERRY PI 1だとコンフィギュレーション異なるので注意下さい。

カーネルのコンフィギュレーション設定カスタム

 カーネルのビルドの設定を変更します。特に設定デフォルトのままでよいのであれば、ここは飛ばして次のカーネルビルドに進んでOKです。

コンフィグファイル(.config)をエディタで直接編集

 以下のコマンドでエディタで直接コンフィグファイルを編集できます。

$ vi .config

 ただ、設定が膨大で普通の人には難易度が高いので、configファイルを設定する専用ツールを使うのが一般的なようです。

menuconfigでコンフィグファイル設定

 コンフィグファイルを設定するツールとしては、menuconfigがよく使われるようなので使ってみます。menuconfig使うためにはlibncurses5-devというライブラリが必要らしいので以下のコマンドでまずはインストール。

$ sudo apt-get install libncurses5-dev

 後は、以下コマンドでmenuconfigが起動します。

$ make menuconfig

 これで、簡単に設定変更できる…といいたいところですが、正直これでも私には難しすぎてチンプンカンプンでした。

aufsとfsprotectで書き込み禁止(read only)化する

 折角カーネルビルドするのに、何もしないのは何か勿体ないので今回はシステム全体を書き込み禁止(read only)化してやろうと思います。書き込みできなくして何がうれしいのという感じですが、Raspberry Piの素の状態は要はパソコンなので、いきなり電源を切るとデータが壊れてプログラムが立ち上がらなくなったり、SDにデータをバンバン書いているとSDの書き込み回数の限界に達してこれまたSDが壊れてしまったりと信頼性が低いものなのです、なので組み込み的に使うならシステムを書き込み禁止にしてやることで信頼性を高めることができるわけです。

 まずカーネルのバージョンに合わせたaufsをダウンロードしてインストールしてやる必要があります。当たり前ですが、ダウンロードしたカーネルであって、今動作しているカーネルではないことに注意してください(私はハマってしまいました…)。今回ダウンロードしたカーネルのバージョンは、以下サイトにいくと rpi-4.1.yみたいなことが書いてあるので、4.1なんだと思います。

 ダウンロードした後にバージョン確認したい場合は、Makefileを見るとVERSION = 4, PATCHLEVEL = 1みたいなことが書いてあるので、ここからバージョンを読み取ってもOKです。

$ git clone https://github.com/sfjro/aufs4-standalone.git
$ cd aufs-aufs4-standalone
$ git checkout aufs4.1

 まずは、以下でカーネルにパッチを当ててやりましょう。patchコマンドを知らない人は調べておくと理解が深まるかもしれません。

$ cd ~/kernel/linux
$ patch -p1 < aufs-aufs4-standalone/aufs4-kbuild.patch
$ patch -p1 < aufs-aufs4-standalone/aufs4-base.patch
$ patch -p1 < aufs-aufs4-standalone/aufs4-mmap.patch

 通常はここでエラー無しでパッチ当てが完了します。エラーがでたら、バージョンかディレクトリの配置を疑ってみてください。私はバージョン違いでエラーがでていました。
 次に色々必要ファイルを下記コマンドでコピー。

$ cd aufs-aufs4-standalone
$ cp -rp fs/ ../
$ cp -rp Documentation/ ../
$ cp -rp include/uapi/linux/aufs_type.h ../include/uapi/linux/

 以上が完了したら、以下コマンドでmenuconfigを起動します。

$ make menuconfig

f:id:karaage:20150921233907j:plain:w640

 aufs4のパッチ当てがうまくいっていれば、以下の通りにAufsの設定ができるようになっています。もしAufsが出てこなかったら残念ながらパッチ当てで失敗しています。
File systems -> Miscellaneous filesystems -> Aufs (Advanced multi layered unification filesystem) support (NEW)

 Aufsの設定をON(スペースキーで*をつける)してSaveしましょう。.configに設定が書き込まれます。

カーネルビルド

 いよいよお待ちかねのビルド。Raspberry Pi 2だとネット情報だと2時間くらいという噂を聞いていたのですが、実際試すとこれが終わらない終わらない。夜7時にビルド初めて12時回ってもビルド終わらなかったので結局寝てしまい、何時間かかったのかわからずじまいでした。ビルドは夜寝る前にやりましょう。ちなみに時間かかるのはmake zImage modules dtbsのとこね。
 2016/01/11追記:ビルドに時間がかかった理由、makeのオプション指定不足でした。下記本読んで気づきました。追記します。


Makefieの編集

 以下コマンドでエディタでMakefileを開きます。

$ cd ~/kernel/linux
$ vi Makefile

 以下好きな名前をつけておきましょう。あとでカスタムしたことがわかるので便利です。

EXTRAVERSION = karaage

カーネルバックアップ

 事前にバックアップしておくのがよいかもしれません。そんなんいるか!って人はここは飛ばして次にいきましょう。

$ cd /boot
$ sudo mkdir -p boot_org/overlays
$ sudo cp *.dtb boot_org/
$ sudo cp overlays/*.dtb boot_org/overlays/
$ sudo cp overlays/README boot_org/overlays/
$ sudo cp kernel7.img boot_org/

カーネルビルド

 以下のコマンドでカーネルをビルドして入れ替えましょう。
2016/01/11追記:Raspberry Pi2の場合は1行目を$ make -j4 zImage modules dtbsとオプションつけることで4コア使えるので4倍ほどビルドが早くなるらしいです。

$ make zImage modules dtbs
$ sudo make modules_install
$ sudo cp arch/arm/boot/dts/*.dtb /boot/
$ sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
$ sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
$ sudo scripts/mkknlimg arch/arm/boot/zImage /boot/$KERNEL.img

f:id:karaage:20150921233808j:plain:w640

 ちなみにmakeをやり直したいときは、make cleanでなく以下のようにmake mrproperを使うのがよいみたいです。make cleanとの違いは.configureファイルまで消してくれるかどうかのもよう。

$ make mrproper

カーネル起動

 いよいよ緊張の再起動です。以下のコマンドを震える手で打ちましょう。

$ sudo shutdown -r now

 緊張の一瞬…


f:id:karaage:20150921233833j:plain:w640

 キターー!!

 といっても見分けがつきませんね。以下コマンドを実行して自分がビルドしたバージョンになっていたら成功です。

$ uname -a

 自分の場合は以下の通り表示されました。カーネルに自分の名前がついてるってちょっとワクワクですね(自分だけ?)。

Linux <ホスト名> 4.1.7karaage-v7+

fsprotect

 ファイルシステムを書き込み禁止(read only)化する仕上げです。書き込み禁止化しない場合は以降は不要です。

 まずはfsprotectのインストール

$ apt-get install fsprotect

 以下コマンドでinitrd.imgファイルを作成

$ sudo update-initramfs -c -k `uname -r`

 ファイルが作成できているか確認。

$ ls /boot/initrd*

 以下が表示されていたらOK

initrd.img-4.1.7karaage-v7+

 次に/boot/config.txtを編集

$ vi /boot/config.txt

 以下追記

initrd.img-4.1.7karaage-v7+


 /boot/cmdline.txtも編集

$ sudo vi /boot/cmdline.txt

 以下の通り、最後にfsprotectを追記

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p6 rootfstype=ext4 elevator=deadline rootwait fsprotect

 再起動

$ sudo shutdown -r now

 書き込み禁止になっているか確認。

$ mount

 以下のような表示が出てくる。

sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
udev on /dev type devtmpfs (rw,relatime,size=10240k,nr_inodes=108773,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=88296k,mode=755)
/dev/mmcblk0p6 on /fsprotect/system type ext4 (ro,relatime,data=ordered)
none on /fsprotect/tmp type tmpfs (rw,relatime,size=524288k,mode=755)
none on / type aufs (rw,noatime,si=a4f01158)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /run/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=176580k)
/dev/mmcblk0p5 on /boot type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)

 参考サイトによると/dev/mmcblk0p6 (本来の root パーティション)が/fsprotect下にro (read only)でマウントされていることと、aufsの記述があることの2点が重要のようなので確認しましょう。ちゃんとなっていますね。
 /bootも保護したい場合は/etc/default/fsprotectを編集。

$ sudo vi /etc/default/fsprotect

 以下を追記すればOK。

PROTECT=”/boot=32M”

 これで完了。後はファイルが保護されているか確認しましょう。正しく設定できていれば、どんなにファイルを削除しても作成しても再起動したら元どおりになっているはずです。以下みたいな恐ろしいコマンドをやっちゃっても大丈夫!のはずです(自分はヘタレなのでできませんが)。

$ sudo rm -rf /

aufs+fsprotectを使う意義(2015/10/25追記)

 ファイルシステムを書き込み禁止(read only)化するのにaufs+fsprotectを使う理由というかうれしさが実は今ひとつ分かってなかったのですが、ようやくわかったのでメモ。単純に書き込み禁止にするだけなら、リードオンリーでマウントするように設定すればよいだけなのですが、それだと一切ファイルを作成できなくなってしまうのに対して、aufs+fsprotectだと、ファイルの一時的な作成やソフトのインストールが可能(ただし電源を切ると元の状態に戻る)ということができるのが利点のようです。UbuntuのLive起動とかはaufs+fsprotectみたいですね。
 単純に書き込み禁止にするなら、例えば以下の記事を参考にすればよさそうです。

Protect your Raspberry PI SD card, use Read-Only filesystem – Charles's Blog

まとめ

 カーネルを初めてビルドしてみました。ほぼデフォルトでビルドしただけなのに、ちょっとだけLinuxが分かった的な何の根拠もない自信がついた気がします(大いなる誤解)。しかしRaspberry Pi 2でもビルドに5時間以上かかるのは誤算でした(私のオプション指定不足でした。本文中に追記してあります)。といってもクロスコンパイルできるようなPCが無いので、当分はRaspberry Pi2でセルフコンパイルしようと思います。寝る前に仕掛ければ朝には終わるし。
 そしてビルドしたカーネルだと、カメラモジュールが動かなくなりました。/dev/vchiqというファイルが無くなっているのが原因みたいですが、どうやって直したらいいのか全然わかりません。他にも気づいて無いだけで色々問題あるのかも。うーむ、先は長そうです。

参考リンク

 色々参考にしたリンク。ほぼここらへんの情報の寄せ集めです。

カーネルビルド方法


 Raspberry Pi公式。とりあえずここを見るべき


 上記記事によると、カーネルの置く場所は/usr/local/srcでなくて/home/pi以下が正しいらしいです(Raspberry Piのデフォルトの場合)。

aufs+fsprotectで書き込み禁止(Read only)化してファイル保護する方法


 Read only化試すきっかけとなった記事




【RPi】fsprotect | MKNOD

カーネルカスタムしないで書き込み禁止(Read only)化してファイル保護する方法

 カーネルをビルドし直さなくても書き込み禁止にする方法があるみたいです。unionfsを使うのが一般的らしいです。今回は試していませんが今後の参考に。手軽にできる一方で、パフォーマンスは低下するらしいです。

Protect your Raspberry PI SD card, use Read-Only filesystem – Charles's Blog



/log — Raspberry Pi を UnionFS-fuse でプロテクトする

カーネルコンフィギュレーション



 今後の参考のメモ。今回の記事では特に参考にしていません。

関連記事