FreeBSD でカスタムカーネルのビルドとインストール
カスタムカーネルを構築する理由と前提
現在の FreeBSD のカーネルはモジュール方式である.多くのデバイスドライバや機能は /boot/kernel 以下のモジュールとして提供され,kldload による動的な読み込み,あるいは /boot/loader.conf への記述による起動時の読み込みが可能である.そのため,デバイスを追加で使いたいだけであれば,カーネルの再構築は必要ないことが多い.
カーネルの再構築が必要になるのは,主に次の場合である.
- モジュールとして提供されていない機能(一部のサブシステムやカーネルオプション)を有効にする場合
- セキュアレベルの運用などでカーネルモジュールの読み込みを禁止し,必要な機能を静的にカーネルへ組み込む必要がある場合
- GENERIC に含まれないデバイスのサポートを追加する場合
GENERIC 以外の構成は GENERIC ほど広くテストされていないため,構築や起動の不具合が起きる可能性がある.不具合が起きたときに自分で切り分けを行える場合に限って実施することを勧める.
このページの手順は,FreeBSD 14 以降のソースツリーを前提とする.作業はすべて root 権限で行う.
【関連する外部ページ】 https://docs.freebsd.org/ja/books/handbook/kernelconfig/
カーネルのセキュアレベル
セキュアレベルは /etc/rc.conf で設定する.kern_securelevel_enable="YES" を指定したときに kern_securelevel の値が適用される(既定は無効,値は -1).
kern_securelevel_enable="YES"
kern_securelevel="1"
セキュアレベルが 1 以上のとき,カーネルモジュールの読み込みと取り外しは禁止される.そのため,この運用を行う場合は,必要な機能をあらかじめカーネルに静的に組み込んでおく. /etc/rc.conf の変更を有効にするためには,reboot を行ってください.
参考 Web ページ: https://docs.freebsd.org/ja/books/handbook/security/
ソースコードの準備
/usr/src/UPDATING の確認
ビルドの前後に必要となる手動の作業は /usr/src/UPDATING に記載される.ビルドを始める前に,この内容を確認する.
ソースコードの取得と更新
ソースコードは git で管理する./usr/src が git の作業ツリーであるかを確認する.
cd /usr/src
git remote -v
git の作業ツリーであれば,次のコマンドで更新する.
git -C /usr/src pull
ソースコードが無い場合,あるいは「fatal: not a git repository」と表示される場合は,稼働中のバージョンに対応するブランチを指定して取得する.ブランチ名は「uname -r」の出力から決める(例: 14.3-RELEASE なら releng/14.3,14-STABLE なら stable/14).
uname -r
git clone --branch releng/14.3 https://git.FreeBSD.org/src.git /usr/src
カーネルコンフィグファイルの新規作成と編集
ハードウェアの確認
コンフィグファイルを編集する前に,実際に搭載されているハードウェアを確認する.必要なドライバを削らないためである.
dmesg
cat /var/run/dmesg.boot
pciconf -lv
ifconfig -a
カーネルコンフィグファイルの新規作成
下記のamd64のところは,マシンのアーキテクチャに応じて読み替える(amd64, arm64, i386, powerpc, riscv など). GENERIC は直接編集せず,別名でコピーしたファイルを編集する.名前はすべて大文字にするのが慣例である.
cd /usr/src/sys/amd64/conf
cp GENERIC NEWKERNEL
カーネルコンフィグファイルの編集
コンフィグファイルの各行は,デバイスやサブシステムを表すキーワードと引数からなる.「#」以降はコメントである.機能を外すときは,その行の先頭に「#」を付ける.
- ident
ident の行は GENERIC 以外に書き換える.uname の出力などでカーネルの識別ができるようになる.
* 設定例: カーネル識別名を NEWKERNEL に設定
ident NEWKERNEL
- include を使った差分の記述
GENERIC からの差分だけを記述する方法がある.この書き方では,ソースツリーの更新で GENERIC に追加された機能が自分のカーネルにも反映される.不要な項目は nooptions, nodevice で個別に除外する.
include GENERIC ident NEWKERNEL options IPFIREWALL options IPFIREWALL_DEFAULT_TO_ACCEPT nodevice sound - 行を削るときの注意
意味を理解していない行は変更しない.ストレージ関連(ahci, nvme, scbus, da など)やファイルシステム関連の行を外すと,システムが起動しなくなることがある.
ネットワークインタフェースの行を整理する場合,「ifconfig -a」「pciconf -lv」で確認した実機のドライバは残す.PCI Ethernet を使う構成では,多くのドライバが PHY の制御に miibus を必要とするため,miibus は残す.
options INET6 は,多くの設定ファイルやアプリケーションが IPv6 の存在を前提にしているため,有効のままにしておく.
- 各項目の説明
コンフィグファイル中の短い説明に加えて,GENERIC と同じディレクトリにある NOTES,およびアーキテクチャに依存しない項目については /usr/src/sys/conf/NOTES に詳しい説明がある.
編集したコンフィグファイルは,/usr/src の外にバックアップを取っておく.
/etc/make.conf の設定
カーネル識別名を毎回コマンドラインで指定しない場合は,/etc/make.conf に次の行を書いておく.
KERNCONF= NEWKERNEL
既定では,カーネルの構築時にすべてのカーネルモジュールが再構築される.構築するモジュールを制限する場合は,/etc/make.conf で次の変数を指定する.
# 構築するモジュールを列挙する場合
MODULES_OVERRIDE = linux acpi
# 構築から除外するモジュールを列挙する場合
WITHOUT_MODULES = linux acpi sound
# モジュールを構築せず,カーネル本体のみを構築する場合
NO_MODULES = yes
graphics/drm-kmod や emulators/virtualbox-ose-kmod のように,カーネルのインタフェースに依存する ports を使っている場合は,PORTS_MODULES を指定しておくと,カーネルの構築時にこれらの ports も併せて再構築される.
PORTS_MODULES = drm-kmod virtualbox-ose-kmod
* CFLAGS などの最適化オプションを /etc/make.conf で独自に変更することは勧められない.ベースシステムのビルドは既定の設定で検証されており,変更するとビルドや実行時の不具合の原因になる.設定可能な項目は man make.conf と /usr/share/examples/etc/make.conf で確認する.
* プロファイル版ライブラリを生成したくない場合は,/etc/src.conf に WITHOUT_PROFILE=yes を記述する(設定可能な項目は man src.conf で確認する).
GENERIC カーネルの確保
カスタムカーネルが起動しないときの復旧用に,確実に起動する GENERIC カーネルを /boot/GENERIC に残しておく.freebsd-update によるバージョンアップでも /boot/GENERIC が必要になる.
◆ インストールメディアから取り出す場合
mount /media
cd /media/usr/freebsd-dist
tar -C/ -xvf kernel.txz boot/kernel/kernel
◆ ソースコードから構築する場合
cd /usr/src
make kernel __MAKE_CONF=/dev/null SRCCONF=/dev/null
* 「make installkernel」を実行すると,直前のカーネルが /boot/kernel.old に退避される.ただし,カーネルをインストールするたびに /boot/kernel.old は上書きされるため,起動を確認できているカーネルは別名のディレクトリ(例: /boot/GENERIC)に保存しておく.
カーネルのビルドとインストール
カーネルだけを再構築する場合の手順である. ソースツリーを更新した場合は,カーネルだけでなくシステム(world)も併せて更新する.その場合は,次の「システムとカーネルのビルドとインストール」の手順による.
- カーネルのビルド操作
KERNCONF には,作成したカーネルコンフィグファイルの名前を書く.「script ...」を実行しておくと,以下「exit」までの出力が /var/tmp/buildkernel.out に記録される.エラーの確認に役立つ.
-j に指定する数は CPU のスレッド数を目安にする(スレッド数は「sysctl -n hw.ncpu」で確認できる).
script /var/tmp/buildkernel.out cd /usr/src make -j4 buildkernel KERNCONF=NEWKERNEL◆ GENERIC カーネルを再構築する場合
カーネルコンフィグファイルを作成していない場合は,KERNCONF=GENERIC を指定する.
script /var/tmp/buildkernel.out cd /usr/src make -j4 buildkernel KERNCONF=GENERIC - script の終了
exit - カーネルのインストール操作
KERNCONF には,作成したカーネルコンフィグファイルの名前を書く.新しいカーネルが /boot/kernel/kernel にインストールされ,直前のカーネルが /boot/kernel.old に退避される.
cd /usr/src make installkernel KERNCONF=NEWKERNEL
◆ GENERIC カーネルをインストールする場合
cd /usr/src make installkernel KERNCONF=GENERIC - 再起動
shutdown -r now - 確認
起動後,カーネルの識別名とバージョンを確認する.
uname -a
起動時のメニューで「Escape to loader prompt」を選び,loader のプロンプトで,起動実績のあるカーネルを指定して起動する.
boot kernel.old
/boot/GENERIC に保存したカーネルを使う場合は次のようにする.
unload
load /boot/GENERIC/kernel
boot
起動後,/var/log/messages と「dmesg」の出力を確認し,コンフィグファイルを見直したうえで再構築を行う.
システムとカーネルのビルドとインストール
ソースコードからシステム全体(world)とカーネルを更新する手順である.world とカーネルはバージョンを揃える必要がある.片方だけを更新すると,ps や vmstat などのコマンドが正しく動作しないことがある.
etcupdate の準備
設定ファイルのマージには etcupdate を使う(mergemaster は FreeBSD 14 で削除された). etcupdate を初めて使う場合は,ソースコードの更新と world の構築を行う前に,次のコマンドで基準となる /etc のデータベースを作成しておく.これを行わないと,初回のマージで誤った差分や不要な衝突が生じることがある.
etcupdate extract
etcupdate diff
「etcupdate diff」の出力を確認し,不要になったローカルの変更を整理しておくと,以後の更新での衝突が減る.
システムのビルド
- /usr/src/UPDATING に目を通す
ビルドの前後に必要な手動の作業が記載されている.
- システムのビルド操作
「script ...」を実行しておくと,以下「exit」までの出力が /var/tmp/makeworld.out に記録される.エラーの確認に役立つ.ビルドには時間がかかる.
script /var/tmp/makeworld.out cd /usr/src make -j4 buildworld
- ビルドの結果の確認
エラーメッセージが出ていないことを確認する.
- script の終了
exit - カーネルのビルドとインストール
KERNCONF には,作成したカーネルコンフィグファイルの名前を書く.カーネルコンフィグファイルを作成していない場合は KERNCONF=GENERIC を指定する.
cd /usr/src make -j4 buildkernel KERNCONF=NEWKERNEL make installkernel KERNCONF=NEWKERNEL - 再起動
新しいカーネルで起動する.
shutdown -r now
システムのインストール
- (オプション)シングルユーザモードで起動
稼働中のプロセスの影響を避けるため,シングルユーザモードで作業することができる.
shutdown now fsck -p mount -u / mount -a -t ufs swapon -a
- システム設定ファイルのバックアップ
更新の前に /etc のコピーを作成しておく.問題が起きたときに /etc.org のファイルを参照できる.
cp -Rp /etc /etc.org - installworld の前に必要な設定ファイルの更新
installworld の実行に必要となるアカウントやグループなどの設定ファイルを先に更新する.
etcupdate -p - システムのインストール
インストールの処理は逐次実行される.make installworld では -j を指定しない.
cd /usr/src make installworld
- 設定ファイルのマージ
残りの設定ファイルを更新する.衝突が生じた場合は,「etcupdate resolve」で解決する.
etcupdate -B etcupdate resolve - 再起動
shutdown -r now - 不要ファイルの削除
新しいバージョンで使われなくなったファイルを削除する.
cd /usr/src make delete-old
- バージョンの確認
uname -a freebsd-version -ku
(オプション)古いライブラリの削除
古い共有ライブラリを削除する.削除したライブラリを参照する既存のアプリケーションは動作しなくなるため,すべてのアプリケーションを新しいシステムで再構築するか,パッケージを更新した後に実行する.
cd /usr/src
make delete-old-libs
更新後のアプリケーションの扱い
メジャーバージョンをまたぐ更新では ABI が変わるため,インストール済みのアプリケーションを更新する必要がある.また,カーネルモジュールを含む ports やパッケージは,カーネルと同じソースツリーで構築されている必要がある.
パッケージで運用している場合は,次のコマンドでバージョン番号が変わっていないパッケージも含めて入れ替える.
pkg-static upgrade -f
ports で運用している場合は,該当の ports を入れ直す.
cd /usr/ports/カテゴリ/ソフトウェア名
make deinstall
make clean
make
make reinstall