
起動が遅い…(比較対象が悪い)
i7、32GB、SSD な環境だと5秒か6秒かで立ち上がる。(UEFIの時間除く)
それに比べて NanoPi NEO3 Plus は21~22秒というところ。
CPU もメモリも比較するのがかわいそうなレベルだし、SSD ではなく SD カードなのも、ねぇ。
けれど、10秒くらいに短縮出来たらいいよなぁ、と思い、カーネルビルドに乗り出してみた。
結果、どのくらい速くなったの?
上記の通り「10秒くらいになったらいいな~」とは思っていたけど、それは夢のまた夢。
5秒ちょっと速くなって、平均16秒というのが良いところ、か。
それでも、一般的な PC の場合はメモリチェックに SATA 接続チェックといった UEFI が立ち上がってチェックしている時間がそれなりに長い。
そうするとトータルでほぼほぼ同じくらいの時間になったかな。
若干、NEO3 Plus のほうが速いまである?
ちなみに、カーネルの起動だけなら3秒は切れるっぽい。
SD カードブートなのによく頑張った!w
ので、圧倒的に快適感を得られた!! …まぁ、「速度」なんてのにはすぐに慣れてしまうのだけれどね…

カーネルビルド:準備
やることはほぼほぼ公式の Wiki に書いてある。
ビルドするPCを別途用意出来ればいいけど、別に仮想マシンでも大丈夫だろうし、WSL2 でも良いと思う。
が、Ubuntu 20.04 が公式に動作確認取れている環境なので、24.04 とかはやめて 20.04 を用意した方が無難。
尚、26.04は明確に使えない。(ツール類の準備中に「対応してないバージョンやで」と怒られる)
などと言いながら、既に使用中の Ubuntu で22.04 があったからそれを使ってしまってはいるけれど…
公式 Wiki のままとはいえ、ちょっと自分用におさらい。
$ sudo apt update
$ sudo apt install curl ccache
$ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/friendlyarm/build-env-on-ubuntu-bionic/master/install.sh)"
(それなりに時間がかかるのでコーヒーでも飲みながら待つ…)
$ export PATH=/opt/FriendlyARM/toolchain/11.3-aarch64/bin:$PATH
$ export GCC_COLORS=auto
$ . ~/.bashrc
$ aarch64-linux-gcc -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-gcc
COLLECT_LTO_WRAPPER=/opt/FriendlyARM/toolchain/11.3-aarch64/libexec/gcc/aarch64-cortexa53-linux-gnu/11.3.0/lto-wrapper
Target: aarch64-cortexa53-linux-gnu
Configured with:(長いので省略)
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.3.0 (ctng-1.25.0-119g-FA)
これでカーネルのビルドに必要な下準備は完了
カーネルのビルド
最初だけやる準備
公式の手順とちょっとだけ変えてて、wget でやってるファイル集めは最初にまとめてしまった。
後でやっても最初にやっても別に変わらないので。(後半は作業:コマンド実行に集中したい)
$ git clone https://github.com/friendlyarm/kernel-rockchip --single-branch --depth 1 -b nanopi6-v6.1.y kernel-rockchip
$ cd kernel-rockchip
$ touch .scmversion
$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 nanopi-r2_linux_defconfig
$ wget https://raw.githubusercontent.com/friendlyarm/sd-fuse_rk3528/kernel-6.1.y/tools/mkkrnlimg
$ chmod 755 mkkrnlimg
$ wget https://raw.githubusercontent.com/friendlyarm/sd-fuse_rk3528/kernel-6.1.y/tools/resource_tool
$ chmod 755 resource_tool
$ wget https://raw.githubusercontent.com/friendlyarm/sd-fuse_rk3528/kernel-6.1.y/prebuilt/boot/logo.bmp
$ wget https://raw.githubusercontent.com/friendlyarm/sd-fuse_rk3528/kernel-6.1.y/prebuilt/boot/logo_kernel.bmp
カーネルの設定変更
自分は使わないであろうドライバとかが色々入っているので、今回のような軽量化目的だと、Device Driver あたりの項目をどんどん削る。
ただまぁ、カーネルなのでね、むやみやたらに削除したりすると当然起動しなくなる。
とりあえず、トライアル&エラーになるかな。
以下のようにすれば選択形式でいろいろと有効/無効/モジュール化みたいな形で組み込むかどうか選択できる。
$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 menuconfig
いざ、ビルド
$ make CROSS_COMPILE=aarch64-linux-gnu- \
ARCH=arm64 \
CC="ccache aarch64-linux-gnu-gcc" \
-j$(nproc)
初回だけは結構時間かかる…
Core i7-11700 な PC で VMwareWorkstation を使い、2コア割当、メモリ8GB割当した仮想PCで1時間くらいはかかったかな。
が、下記の通りメモリはともかく CPU が完全に不足してますねぇ…

環境がちょっと違うけど、割り当てコア数を4にしたら20分くらいにはなったかな。
20分くらいなら、まぁ、許容範囲だし、2回目以降 ccache が予想以上に頑張ってくれたので短時間化できるし、とりあえずこれで頑張ってみよう。
モジュールのビルド
処理時間的な山は越えたかな?と思ったら2つ目の山が来る。モジュールのビルド。
これもそこそこ時間かかるので実行したらとにかく待つ。
ただし、これも長いのは最初の1回だけ。2回目以降は ccache が凄い。
$ mkdir -p out-modules && rm -rf out-modules/*
$ make CROSS_COMPILE=aarch64-linux-gnu- \
ARCH=arm64 \
CC="ccache aarch64-linux-gnu-gcc" \
INSTALL_MOD_PATH="$PWD/out-modules" modules \
-j$(nproc)
次は make とはなっているけどすぐ終わるやつ。
$ make CROSS_COMPILE=aarch64-linux-gnu- \
ARCH=arm64 \
INSTALL_MOD_PATH="$PWD/out-modules" \
modules_install
後処理
$ KERNEL_VER=$(make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 kernelrelease)
$ [ ! -f "$PWD/out-modules/lib/modules/${KERNEL_VER}/modules.dep" ] && \
depmod -b $PWD/out-modules -E Module.symvers -F System.map -w ${KERNEL_VER}
$ (cd $PWD/out-modules && find . -name \*.ko | xargs aarch64-linux-strip --strip-unneeded)
出来上がったものをパッキング
カーネルのビルドやモジュールのビルド、各種設定ファイル的なものがここまでで出来上がった。
それらをまとめて、SD カードに書き込むためのファイルを作る。
NEO3 Plus の Ubuntu では役割毎に SD カードのパーティションを複数に分けているので、そのパーティション毎にイメージファイルを作成する感じ。
カーネルが入ったパーティションには kernel.img というファイルを一つ
設定ファイル等がまとめられたパーティションには resource.img というファイルを一つ
といった感じに。
また、モジュール類も対象としているシステムの /lib 配下にコピーする必要があるので、tar でまとめる。
$ ./mkkrnlimg arch/arm64/boot/Image kernel.img
$ ./resource_tool --dtbname arch/arm64/boot/dts/rockchip/rk3528-nanopi*-rev*.dtb \
logo.bmp logo_kernel.bmp
$ tar -czpf modules_arch.tar.gz -C ./out-modules/lib/ modules
これで、kernel.img と resource.img 、modules_arch.tar.gz が完成。
SDカードに焼き込む
さて、以上で次の4つのファイルが出来上がった。
- kernel.img
- resource.img
- modules_arch.tar.gz
これを SD カードに書き込んでいけばいいわけだ。
そして、この SD カードは現状で NEO3 Plus 上で FE 公式の Ubuntu が動いているという前提なので、何とかしてこれらのファイルを SD カードにコピーしたい。
ということで、Ubuntu 間のファイルのやり取り、しかも SSHで 接続ができるのなら SCP を使うのが手軽。
カーネルをビルドしたPCから下記のようにすれば、NEO3 Plus にファイルを転送できる。
$ scp [送りたいファイル] [送信先のuserName]@[送信先のIP address]:[送信先のパス]
hoge というユーザーが 192.168.10.20 いたとすると以下のようにする。
$ scp kernel.img hoge@192.168.10.20:/home/hoge
$ scp secure.img hoge@192.168.10.20:/home/hoge
$ scp modules_arch.tar.gz hoge@192.168.10.20:/home/hoge
$ scp kernel.img hoge@192.168.10.20:/home/hoge
で、送信が完了したら、操作を開発 PC から NEO3 Plus に変える。
念の為、パーティションの状態を確認。
# parted /dev/mmcblk0 print
Model: SD SE064 (sd/mmc)
Disk /dev/mmcblk0: 61.9GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 8389kB 12.6MB 4194kB uboot
2 12.6MB 16.8MB 4194kB misc
3 16.8MB 21.0MB 4194kB dtbo
4 21.0MB 37.7MB 16.8MB resource ← 書き換えるターゲット
5 37.7MB 79.7MB 41.9MB kernel ← 書き換えるターゲット
6 79.7MB 113MB 33.6MB boot
7 113MB 147MB 33.6MB recovery
8 147MB 2496MB 2349MB ext4 rootfs
9 2496MB 61.9GB 59.4GB ext4 userdata
パーティション番号の4、5が書き換えるターゲット。
ということで、それぞれに対して dd コマンドで新しい img ファイルを焼いてみよう。
※いや、マジでパーティション番号の間違いとか、そもそもストレージの取り違えとかには気を付けてね…
$ sudo dd if=resource.img of=/dev/mmcblk0p4 bs=1M
$ sudo dd if=kernel.img of=/dev/mmcblk0p5 bs=1M
次にモジュールを展開する。
$ cp /home/hoge/modules_arch.tar.gz /lib/modules_arch.tar.gz
$ cd /lib
$ tar -xzpf modules_arch.tar.gz
終わったら、起動してくることを祈りつつ…
$ sudo reboot
トライアル・アンド・エラー:カーネルの設定とビルドを繰り返す方法
さて、1回目のカーネルビルドは上記のような感じでいいでしょう。
けど、うまくいった、いかないにかかわらず、2回目、3回目と設定を変えて試すとき、どこからどうやればいい!?
となるのでそれをまとめたのが以下。
やることを大まかに
- カーネルの設定変更
- カーネルのビルド
- モジュールの作成
- パッキング
- SDカードへの書き込み
打ち込むコマンドは以下のようになる
開発環境の Ubuntu が再起動されていたり SSH 接続をいったん切ってしまった場合に必要な操作。
$ export PATH=/opt/FriendlyARM/toolchain/11.3-aarch64/bin:$PATH
$ export GCC_COLORS=auto
$ . ~/.bashrc
$ aarch64-linux-gcc -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-gcc
COLLECT_LTO_WRAPPER=/opt/FriendlyARM/toolchain/11.3-aarch64/libexec/gcc/aarch64-cortexa53-linux-gnu/11.3.0/lto-wrapper
Target: aarch64-cortexa53-linux-gnu
Configured with:(長いので省略)
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.3.0 (ctng-1.25.0-119g-FA)
環境変数は再起動等で元に戻ってしまうので再設定し、 . ~/.bashrc で反映するというところまで実行し、
続いて、aarch64-linux-gcc -v を実行してクロスコンパイルのターゲット設定が間違っていないか確認しておこう。
カーネルの再設定
$ make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 menuconfig
この時、カーネルのバージョンを追記しておくと良い。
ロールバックする時や比較などに超便利だから。
次にモジュールのビルドを行うとき、このバージョンごとのサブフォルダを作ってモジュール類を用意してくれるので、極力、バージョンを設定しておいた方が良さそう。
設定する場所は以下の通り。
General setup -> Local version – append to kernel release

ここに日付でも通し番号でも何かわかりやすいものを設定しておけば、カーネルのバージョンに反映される。
また、併せて設定内容も同じように上で追記した名前と同じ名前で保存しておく。

カーネル設定ファイルをコピー
$ cp mybuild_20260719.config .config
カーネルのビルド
$ make CROSS_COMPILE=aarch64-linux-gnu- \
ARCH=arm64 \
CC="ccache aarch64-linux-gnu-gcc" \
-j$(nproc)
モジュールのビルド&インストール
$ make CROSS_COMPILE=aarch64-linux-gnu- \
ARCH=arm64 \ CC="ccache aarch64-linux-gnu-gcc"\
INSTALL_MOD_PATH="$PWD/out-modules" modules \
-j$(nproc)
$ make CROSS_COMPILE=aarch64-linux-gnu- \
ARCH=arm64 \
INSTALL_MOD_PATH="$PWD/out-modules" \
modules_install
モジュールのインストール時に流れる文字列を見ていると、カーネル設定時にバージョン設定を追記したディレクトリに保存してくれているのがわかる。
各イメージファイルの作成
$ KERNEL_VER=$(make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 kernelrelease)
$ [ ! -f "$PWD/out-modules/lib/modules/${KERNEL_VER}/modules.dep" ] && \
depmod -b $PWD/out-modules -E Module.symvers -F System.map -w ${KERNEL_VER}
$ (cd $PWD/out-modules && find . -name \*.ko | xargs aarch64-linux-strip --strip-unneeded)
$ ./mkkrnlimg arch/arm64/boot/Image kernel.img
$ ./resource_tool --dtbname arch/arm64/boot/dts/rockchip/rk3528-nanopi*-rev*.dtb \
logo.bmp logo_kernel.bmp
$ tar -czpf modules_arch.tar.gz -C ./out-modules/lib/ modules
出来上がったファイルを開発 PC から NEO 3 PLUS に転送
$ scp kernel.img hoge@192.168.10.20:/home/hoge
$ scp modules_arch.tar.gz hoge@192.168.10.20:/home/hoge
$ scp resource.img hoge@192.168.10.20:/home/hoge
転送が完了したら、操作を NEO3 Plus に切り替える。
一応、パーティションを確認しておく。
$ sudo parted /dev/mmcblk0 print
Model: SD SE064 (sd/mmc)
Disk /dev/mmcblk0: 61.9GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 8389kB 12.6MB 4194kB uboot
2 12.6MB 16.8MB 4194kB misc
3 16.8MB 21.0MB 4194kB dtbo
4 21.0MB 37.7MB 16.8MB resource
5 37.7MB 79.7MB 41.9MB kernel
6 79.7MB 113MB 33.6MB boot
7 113MB 147MB 33.6MB recovery
8 147MB 2496MB 2349MB ext4 rootfs
9 2496MB 61.9GB 59.4GB ext4 userdata
対象となるパーティションに正しくイメージファイルを焼いていく…
$ sudo dd if=resource.img of=/dev/mmcblk0p4 bs=1M
$ sudo dd if=kernel.img of=/dev/mmcblk0p5 bs=1M
モジュールを展開
$ sudo cp /home/hoge/modules_arch.tar.gz /lib/modules_arch.tar.gz
$ cd /lib
$ sudo tar -xzpf modules_arch.tar.gz
再起動
$ sudo reboot
こうやって試行錯誤を繰り返し…と言っても、ChatGPT あたりに聞くとどの辺削ればいいかは教えてくれるので、その辺りを削っていったら割とカーネルはスリム化できると思う。
起動しなくなった場合の対処法
まぁ、カーネルのビルドなんかやってるとしばしば起こる沈黙やカーネルパニック。
昔々のはるか昔…そりゃぁ復旧は面倒だった。
けど、今は SD カードだもんねぇ…
引っこ抜いて、別の端末に挿して、動いていた時のイメージ焼けば復旧。
だもの。
いい時代になったもんだ…
その別の端末というのも、そもそも開発用 PC が Ubuntu で用意したから、そいつに繋げればいいわけで。
そして、実際繋げてみると下記のように認識される。
※もちろん、繋がっているHDDやSSDによって増減するので要注意。マジ要注意。
$ ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3
/dev/sdb /dev/sdb1 /dev/sdb2 /dev/sdb3 /dev/sdb4 /dev/sdb5 /dev/sdb6 /dev/sdb7 /dev/sdb8 /dev/sdb9
パーティションの数が多いから取り違えにくいとはいえ、本当に要注意やで。
で、これが見えてしまえば、あとは dd コマンドで起動してた時のイメージを焼けばいい。
ここで効くのさ、menuconfig でカーネルの設定をいろいろ変更していた時、「バージョン名に独自のものを付けた」「config ファイルの名前もそれと同じものを付けた」とやったことが。
例えば、「mybuild_20270718_04.config」といったように前の設定ファイルが残っていたら、これを「.config」とコピーして、カーネルとモジュールを再ビルドする上記手順をもう一度やってあげれば、その時のイメージがまた作れる。
んで、出来上がったイメージをパーティションの指示を間違えないように、慎重に実行する。
$ sudo dd if=resource.img of=/dev/sdb4 bs=1M
$ sudo dd if=kernel.img of=/dev/sdb5 bs=1M
また、モジュール類を /lib に展開しなくて良いのか!?
となるけど、tar でアーカイブしたモジュールはディレクトリ構造が上で付けた独自のバージョン名の部分を含んだ形で構成される。
なので、/lib/modules/ 配下に[本来のバージョン名]+[独自のバージョン名]のディレクトリが展開されているので、これを意図的に消していない限りは既にモジュールは”そこにある”状態になっている。
$ ls -al /lib/modules/6.1.141* -d
drwxr-xr-x 4 root root 4096 Apr 2 12:24 /lib/modules/6.1.141
drwxrwxr-x 3 hoge hoge 4096 Jul 18 18:48 /lib/modules/6.1.141-mybuild_20260718_01
drwxrwxr-x 3 hoge hoge 4096 Jul 20 18:57 /lib/modules/6.1.141-mybuild_20260718_02
drwxrwxr-x 3 hoge hoge 4096 Jul 20 19:18 /lib/modules/6.1.141-mybuild_20260718_03
drwxrwxr-x 3 hoge hoge 4096 Jul 20 19:44 /lib/modules/6.1.141-mybuild_20260719_01
もちろん、「カーネルはこれでOK」となったら過去に作ってきたモジュール類は削除でOK
だけど、ロールバックする可能性があるならば、多少残しておいた方が吉。
あと、これを書いてて気になったけど、/lib/module のパーミッションがちょっとアレなことになってるね…
どこでどう変更されてしまったかわからないけど、本来は下記のようになっているべきだと思う。
$ ls -al /lib/modules/6.1.141* -d
drwxr-xr-x 4 root root 4096 Apr 2 12:24 /lib/modules/6.1.141
drwxr-xr-x 3 root root 4096 Jul 18 18:48 /lib/modules/6.1.141-mybuild_20260718_01
drwxr-xr-x 3 root root 4096 Jul 20 18:57 /lib/modules/6.1.141-mybuild_20260718_02
drwxr-xr-x 3 root root 4096 Jul 20 19:18 /lib/modules/6.1.141-mybuild_20260718_03
drwxr-xr-x 3 root root 4096 Jul 20 19:44 /lib/modules/6.1.141-mybuild_20260719_01
まぁ、ここは最後に直しましょう。
ということで。
イメージを変更できたら、またまた SD カードを引っこ抜いて、NEO3 Plus に挿し直して起動してみる。
ちゃんと前のカーネルで起動してきたらOK
この記事にコメントしてみる