はじめに
ラズパイでカーネルモジュールをビルドする際、カーネルのソースコードとカーネルの部分的なコンパイルが必要になるが、その準備手順について備忘録を兼ねて記しておく。本記事の方法ではソースのダウンロードからコンパイルまで手作業で行う。
(参考にした主たるサイト:https://qiita.com/mt08/items/1ebbdbf304f6f2362024 )
実行環境(例)
Machine: Raspberry Pi 4 Model B
OS: Raspbian Buster Lite (relase date: 2020-02-13, kernel version: 4.19.97)
実行するマシンに依存する部分はシンボルファイルのダウンロード時ぐらい。OSはRaspbianなら古いバージョンでも同じ手順でいけるはず(少なくともStretchはOK)。
ビルド環境の準備
まず、git, bc, bison, flex, libssl-dev が必須なのでインストール。すでに入っているなら問題ない。
rpi-updateは不要、むしろすべきでない(情報源: https://www.raspberrypi.org/documentation/linux/kernel/updating.md )。
raspberrypi:~# apt install git bc bison flex libssl-dev
Firmware revision の取得
GitHubのサイトから現在稼働中のカーネルバージョンの firmware revision ハッシュ値を取得して /boot/.firmware_revision
に格納する(別のファイルでも構わない)。最初からこのファイルが存在する場合は、その中のハッシュ値を用いてよいが、稼働中のカーネルと合致するかは確認しておく方が無難だろう。
まずは、現在のカーネルバージョンを確認。
raspberrypi:~# uname -r
4.19.97-v7l+
バージョン(上記では4.19.97)を把握したら、https://github.com/Hexxeh/rpi-firmware/commits/master で同じバージョンの項目を探し、そこに記載されているハッシュ値を /boot/.firmware_revision
に書き込む。あるいはシェルの環境変数として保存する(変数名を FIRMWARE_REV にしておけば、以降のコード例をコピペして利用可能になる)。
どこにfirmware revisionが記されているのか?
赤で囲った部分がカーネルのバージョン、緑がクリップボードマーク。 クリップボードマークをクリックすればハッシュ値をクリップボードにコピーしてくれる。kernel: 4.19.97 を例に取ると、firmware revision は993f47507f287f5da56495f718c2d0cd05ccbc19
となる。
カーネルソースのダウンロード
下記のようにfirmware revisionをGitHubに投げるとkernel revisionを取得できる。このkernel revisionを元にしてDLすべきカーネルソースのURLが定まる。
カーネルソースをDLしたいディレクトリで下記コードを実行する。ソースアーカイブは150MBを超えるし、展開すると1GB近くになるので、ディスクの空き容量に注意しながらディレクトリを選択する。
FIRMWARE_REV=`cat /boot/.firmware_revision`
KERNEL_REV=`curl -L https://github.com/Hexxeh/rpi-firmware/raw/${FIRMWARE_REV}/git_hash`
curl -L https://github.com/raspberrypi/linux/archive/${KERNEL_REV}.tar.gz > linux-kernel-src.tar.gz
firmware revisionの値を環境変数に登録し(1行目)、これをもとにkernel revisionをGitHubからダウンロードし(2行目)、さらにkernel revisionの値からカーネルソースのURLが決まり、これをDLして linux-src-kernel.tar.gz
に書き出す(3行目)。
HTTPのレスポンスにファイルサイズが記されてないため、DLに成功したか否かは後の解凍に成功するか否か見ないとわからないのが厄介。しかもDLはレジューム非対応なので、通信環境が悪いと難渋する。
提示している実行例におけるここまでの流れ
カーネルバージョンは `4.19.97-v7l+` Version 4.19.97の firmware revision は `993f47507f287f5da56495f718c2d0cd05ccbc19` 上記firmware revisionに該当する kernel revision は `43857965e5f526b9df807e543102a11fac1c0bcc` よって、求めるカーネルソースは `https://github.com/raspberrypi/linux/archive/43857965e5f526b9df807e543102a11fac1c0bcc.tar.gz`カーネルソースの展開
DLした linux-kernel-src.tar.gz
を展開する。ここでは/usr/src
に展開する。前述のように約1GBになるので、ディスク容量に注意。
raspberrypi:~# tar -C /usr/src -xvf linux-kernel-src.tar.gz
linux-43857965e5f526b9df807e543102a11fac1c0bcc/
linux-43857965e5f526b9df807e543102a11fac1c0bcc/.clang-format
linux-43857965e5f526b9df807e543102a11fac1c0bcc/.cocciconfig
linux-43857965e5f526b9df807e543102a11fac1c0bcc/.get_maintainer.ignore
:
(中略)
:
linux-43857965e5f526b9df807e543102a11fac1c0bcc/virt/lib/
linux-43857965e5f526b9df807e543102a11fac1c0bcc/virt/lib/Kconfig
linux-43857965e5f526b9df807e543102a11fac1c0bcc/virt/lib/Makefile
linux-43857965e5f526b9df807e543102a11fac1c0bcc/virt/lib/irqbypass.c
raspberrypi:~#
ラズパイ4ならメモリがたくさんあるので、1.5GBくらいのRAM diskをtmpfsで作って、そこに展開すると速いし、SDカードの書き込みダメージを減らすことができる。
正しく展開されたら、/usr/src/linux-(kernel revisionの値)
という名前のディレクトリが作られているはずである。
raspberrypi:~# ls /usr/src
linux-43857965e5f526b9df807e543102a11fac1c0bcc
シンボルファイルのダウンロード
シンボルファイルは先に展開したカーネルソースのルートディレクトリに保存する。
ルートディレクトリに移動すれば、以下のようなファイルが見えるはずである。
raspberrypi:~# cd /usr/src/linux-43857965e5f526b9df807e543102a11fac1c0bcc
raspberrypi:/usr/src/linux-43857965e5f526b9df807e543102a11fac1c0bcc# ls
arch certs CREDITS Documentation firmware include ipc Kconfig lib MAINTAINERS mm README scripts sound usr
block COPYING crypto drivers fs init Kbuild kernel LICENSES Makefile net samples security tools virt
ここでシンボルファイルをDLするわけだが、実行中のマシンによってDLすべきファイルが変わってくる。しかしコンパイル時はModule.symvers
で参照されるので、DL後にファイル名を変えるかシンボリックリンクを張って対応する。
Raspberry Pi 4 Model B (64bit)
curl -L https://github.com/Hexxeh/rpi-firmware/raw/${FIRMWARE_REV}/Module8.symvers > Module8.symvers
ln -s `pwd`/Module8.symvers /usr/src/linux-${KERNEL_REV}/Module.symvers
Raspberry Pi 4 Model B (32bit)
curl -L https://github.com/Hexxeh/rpi-firmware/raw/${FIRMWARE_REV}/Module7l.symvers > Module7l.symvers
ln -s `pwd`/Module7l.symvers /usr/src/linux-${KERNEL_REV}/Module.symvers
Raspberry Pi 3 Model B+
curl -L https://github.com/Hexxeh/rpi-firmware/raw/${FIRMWARE_REV}/Module7.symvers > Module7.symvers
ln -s `pwd`/Module7.symvers ./Module.symvers
Raspberry Zero WH
curl -L https://github.com/Hexxeh/rpi-firmware/raw/${FIRMWARE_REV}/Module.symvers > Module.symvers
シンボルファイルが何をするものなのかについては、下記を参照されたし。
https://kakurasan.hatenadiary.jp/entry/20100713/p1
https://elixir.bootlin.com/linux/v2.6.36/source/Documentation/kbuild/modules.txt#L442
#コンパイル
カーネルソースのルートディレクトリに移動。このようなファイルが見えていればOK。Module.symversがないと前項の作業に誤りがあるということになる。
pi@raspberrypi:/usr/src/linux-43857965e5f526b9df807e543102a11fac1c0bcc# ls
arch COPYING Documentation fs ipc kernel Makefile net scripts tools
block CREDITS drivers include Kbuild lib mm README security usr
certs crypto firmware init Kconfig MAINTAINERS Module.symvers samples sound virt
下記コードを実行。最後のmake -j4 modules_prepare
でコンパイル。
コンパイル時間はマシンスペックで大きく変わる(数十秒~数分)。
modprobe configs
zcat /proc/config.gz > .config
make -j4 modules_prepare
途中で下記のようにカーネルの設定に入力を求められる場合がある。
raspberrypi:/usr/src/linux-43857965e5f526b9df807e543102a11fac1c0bcc# make -j4 module_prepare
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
YACC scripts/kconfig/zconf.tab.c
LEX scripts/kconfig/zconf.lex.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
scripts/kconfig/conf --syncconfig Kconfig
*
* Restart config...
*
*
* Kernel hacking
*
Magic SysRq key (MAGIC_SYSRQ) [Y/?] y
Enable magic SysRq key functions by default (MAGIC_SYSRQ_DEFAULT_ENABLE) [0x1] 0x1
Enable magic SysRq key over serial (MAGIC_SYSRQ_SERIAL) [Y/n/?] y
Kernel debugging (DEBUG_KERNEL) [Y/?] y
Code coverage for fuzzing (KCOV) [N/y/?] (NEW)
何のことか分からないならデフォルト値(大文字で書かれている選択肢)を選んでおけば問題ないと思われる。その他のエラーが出た場合は、何かしらパッケージが足りないのか、あるいはカーネルソースのDLに失敗している可能性もあるので確認を。
エラー無く終了したら、カーネルソースのルートへのシンボリックリンクを/lib/modules/`uname -r`/build
として作成する。
ln -s /usr/src/linux-43857965e5f526b9df807e543102a11fac1c0bcc /lib/modules/`uname -r`/build
これでカーネルモジュールをコンパイルする準備が整った!
あとがき
apt upgrade
を実行すると、カーネルが勝手にバージョンアップされてしまうことがある。カーネルバージョンが変わると、カーネルモジュールは無効となり新たにビルドし直さなければならなくなる。しかもカーネルバージョンが変わるだけでコンパイルがうまくいかなくなることもあって、非常に厄介だ。
「予備機として同じRaspberry Piを用意 → 目標バージョンのカーネルで走らせてコンパイル実験 → 成功したら主機もカーネルを目標バージョンにアップデートし、予備機でコンパイルに成功したカーネルモジュールをコピー」とすれば主機の運用停止を最小にできるだろう。
小生はUSB接続のWiFiドングルにてインターネット接続しており、かつこのカーネルモジュールを自前でコンパイルしているので、主機を止めたら何もできなくなる(笑)