MatterゲートウェイのLinuxをどう作るか:WSLでRDK X5 SDKとBuildrootをビルド
WSLでD-Robotics RDK X5 SDKをビルドし、RustのゲートウェイをBuildrootイメージに組み込む。クロスコンパイル、オーバーレイ、サービス停止、自動起動、IPv6無効のカーネルなどの落とし穴。
前回の記事では、Rust と rs-matter でゲートウェイプログラムを書いた話をしました。今のところ、これは PC 上で動いています。製品として売れるゲートウェイにするには、もう一層足りません。ゲートウェイ自身の Linux システムです。
開発ボードを手に入れると、たいてい Ubuntu イメージが付いていて、電源を入れればすぐ使えます。多くの人がまず考えるのは「Ubuntu にゲートウェイプログラムを入れればそれで済むのでは?」ということでしょう。
開発段階ならそれで構いません。量産ではダメです。この記事では、WSL 上で D-Robotics の RDK X5 SDK をビルドし、Buildroot でゲートウェイ用のシステムイメージを作った過程と、実際に手を動かさないと気づけない落とし穴をいくつか記録しておきます。
なぜ Ubuntu をそのまま使わないのか
D-Robotics の X5 SDK には、もともと 2 種類のルートファイルシステムが用意されています。開発用の Ubuntu 22.04 イメージと、量産用の Buildroot です。ゲートウェイという用途で比べると、違いははっきりしています。
| Ubuntu イメージ | Buildroot カスタムシステム | |
|---|---|---|
| サイズ | 数 GB | 数百 MB、system パーティションはわずか 250MB |
| 中身 | デスクトップ級のパッケージ一式 | 自分で選んだものだけ |
| 起動時のサービス | 大量にあり、何が動いているか把握しにくい | すべて見えていて、削除もできる |
| アップデート方式 | apt でパッケージ単位に更新 | システムイメージ丸ごと置き換え、A/B パーティションと組み合わせればロールバック可能 |
| セキュリティ審査 | 使わないソフトウェアを山ほど説明する必要がある | 攻撃面が小さく、説明しやすい |
ゲートウェイは、ユーザーの家に置かれ、何年も誰もログインしない機器です。必要なのは、起動したらゲートウェイプログラムが走ること、システムは読み取り専用で書き込みで壊れないこと、アップデートに失敗したら自動で旧バージョンに戻ること。どれも Buildroot では定番のやり方ですが、Ubuntu だとむしろ削る手間がかかります。
なぜまず RDK X5 でやるのか
メイン SoC 選定の記事では、RDK X5 を「使えるが、ゲートウェイ向きではない」と位置づけました。10 TOPS の AI 性能はゲートウェイでは使い道がなく、コストも高めです。
ただし、非常に現実的な長所が一つあります。SDK が公開されていてダウンロードできることです。ビルド、システムのカスタマイズ、セキュアブート、A/B OTA、OP-TEE セキュアストレージまで、正式なドキュメントが揃っています。Rockchip の SDK も同じく Buildroot ベースで、やり方はほぼ共通です。そこでシステムカスタマイズの段階は、まずドキュメントが一番充実している X5 SDK で一連の流れを通すことにしました。
先にお断りしておくと、この記事はイメージのビルドまでで、まだボードに書き込んでの実機検証はしていません。 ボードが届いてからの書き込みと実際の動作は、後の記事で扱います。
環境:WSL ディストリビューションを別に用意する
X5 SDK LNX6.1.176_PL5.1_V2.0.0 は Ubuntu 22.04 が必要で、展開後は約 9.5GB になります。普段使っているのは Ubuntu 24.04 で、SDK の依存関係を混ぜたくなかったので、別のディストリビューションを入れ、仮想ディスクは D ドライブに置きました。
wsl --install -d Ubuntu-22.04 --name x5build --location 'D:\wsl\x5build' --no-launch
Git Bash から wsl コマンドを実行するときは MSYS_NO_PATHCONV=1 を付けてください。付けないと、/ で始まるパスが Git Bash によって Windows パスに書き換えられてしまいます。
SDK の主なコンポーネントは次のとおりです。
| コンポーネント | バージョン |
|---|---|
| Linux カーネル | 6.1.176 |
| U-Boot | 2022.10 |
| ATF | 2.8 |
| Buildroot | 2025.02 |
| ルートファイルシステム | glibc、SysV init |
| クロスツールチェーン | ARM GNU Toolchain 11.3(aarch64)、SDK 同梱 |
ソースの取得、依存関係のインストール、初回のフルビルドです。
B=https://archive.d-robotics.cc/downloads/sdk/LNX6.1.176_PL5.1_V2.0.0/board_support_package
curl -O $B/platform_source_code.tar.gz.md5sum
curl -C - --retry 5 -O $B/platform_source_code.tar.gz # 约 4.1GB,断了能续传
md5sum -c platform_source_code.tar.gz.md5sum
tar -I pigz -xf platform_source_code.tar.gz && cd platform_source_code
sudo build/install_host_deps.sh
sudo tar -xf toolchain/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/
./bd.sh lunch 8 # board_x5_soc_debug_config.mk
./bd.sh # 第一次完整编译,约 12 分钟
lunch 8 は debug 構成で、adb とデバッグツールが入っています。量産では lunch 9 の release 構成に切り替えます。
メモリにも注意が必要です。私の WSL のグローバル上限は 8GB で、SDK の推奨は 16GB です。今はゲートウェイのビルドと再パッケージだけなので 8GB で足りていますが、将来 Buildroot 全体をソースからビルドするならメモリが足りなくなるので、C:\Users\<用户名>\.wslconfig の memory= を変更してから wsl --shutdown します。これはすべての WSL ディストリビューションを停止させる点に注意してください。
ゲートウェイプログラムをシステムに組み込む
一連の流れはスクリプトにまとめたので、普段はコードを修正したら Windows 側で 1 行実行するだけです。
wsl -d x5build -- bash /mnt/d/<项目目录>/x5/build.sh
スクリプトがやっていることは、ソースの同期、ゲートウェイプログラムのクロスコンパイル、ルートファイルシステムへの配置、イメージの再パッケージです。途中にいくつか触れておきたい細かい点があります。
ソースはまず Linux ファイルシステムに同期してからビルドする
プロジェクトのコードは Windows の D ドライブにあり、WSL からは /mnt/d として見えます。/mnt/d 上で直接 Rust をビルドするとかなり遅くなります。ファイルアクセスのたびに Windows のファイルシステムをまたぐからです。そこでスクリプトでは、まず rsync でコードを WSL 自身のディレクトリに同期し(target と .git は除外)、そこでビルドしています。
musl ではなく glibc ターゲットを使う
メイン SoC 選定の段階では、aarch64-unknown-linux-musl で静的リンクのバイナリを作り、Rockchip と D-Robotics の両方で使い回す計画でした。
X5 では aarch64-unknown-linux-gnu に切り替え、リンカには SDK 同梱の GCC 11.3 を指定しています。
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc \
cargo build --release --target aarch64-unknown-linux-gnu
理由は、X5 のルートファイルシステムがそもそも glibc だからです。SDK のツールチェーンでリンクすれば、同梱の glibc はファームウェアのものより古いので、できたプログラムはボード上で確実に動きます。musl の互換性問題を別途気にする必要もありません。
オーバーレイディレクトリ:毎回まず元に戻してから重ねる
SDK にはルートファイルシステム用のオーバーレイディレクトリ system/buildroot/prebuilt/boot-utils-runtime が用意されていて、ここに置いたファイルはそのままイメージに入ります。自分たちのファイルはプロジェクトの x5/overlay/ に置いています。
スクリプトの初回実行時に、SDK オリジナルのオーバーレイディレクトリをスナップショットとしてバックアップします。以降のビルドでは毎回、まずスナップショットに戻し、それから自分たちのファイルを重ねます。
こうしておくと変更が可逆になります。x5/overlay/ からファイルを 1 つ消せば、イメージ側も SDK オリジナルに戻り、前回の変更が残ることはありません。
使わない起動サービスを止める
サービスの停止もオーバーレイで行います。中身が exit 0 だけの同名スクリプトを置いて、SDK 元の起動スクリプトを置き換えるわけです。現在止めているのは次のサービスです。
| サービス | 停止理由 |
|---|---|
S50telnet |
平文のリモートログインで、EU の EN 18031 や英国 PSTI といったサイバーセキュリティ規制の要件を満たさない |
S40bluetoothd |
モジュール内蔵の Bluetooth は使わない。Bluetooth Mesh は別の Silicon Labs モジュールで扱う |
S65ptp4l、S66phc2sys |
PTP による高精度時刻同期で、ゲートウェイには不要 |
S90cam-service |
カメラサービス |
S99iiod |
IIO センサーサービス |
sshd と adb は開発段階ではひとまず残し、量産時に止めます。
自動起動:一つの & が招いた落とし穴
ゲートウェイプログラムは起動時に自動で立ち上がり、クラッシュしたら自動で再起動する必要があります。X5 は SysV init なので、/etc/init.d/S95gateway を 1 本書けば済みます。
まず注意すべき点は、ボード上に bash がないことです。X5 SDK は V2.0.0 から bash を同梱しなくなったので、システムスクリプトは POSIX sh で書くしかありません。
二つ目の落とし穴はもっと気づきにくいものです。一番素直な書き方は、起動スクリプトの中で監視ループをバックグラウンドに回すことです。
while true; do /usr/bin/gateway; sleep 2; done &
一見問題なさそうですが、起動時に rcS がすべての起動スクリプトを実行し終えて自身が終了するとき、自分が起動したバックグラウンドプロセスにハングアップシグナルを送るため、このループも一緒に殺されてしまいます。ゲートウェイプログラムは起動時に一瞬だけ立ち上がり、そのまま消えます。
正しいやり方は start-stop-daemon -b を使い、デーモンを rcS のセッションから切り離すことです。
LOOP="cd /userdata/gateway && while true; do /usr/bin/gateway </dev/null 2>&1 | logger -t gateway; sleep 2; done"
start-stop-daemon -S -b -m -p /var/run/gateway.pid -x /bin/sh -- -c "$LOOP"
プログラムが異常終了すると 2 秒後に自動で再起動し、出力は logger 経由でシステムログに入ります。X5 には logread がないので、ログは /userdata/log/kernel/message にあり、grep gateway で確認します。ゲートウェイのデータは /userdata/gateway/data/ に置いています。このパーティションは書き込み可能で、システムパーティションは読み取り専用です。
必ず変更すべき SDK のデフォルト設定 2 つ
カーネルがデフォルトで IPv6 を有効にしていない
これが全工程で一番致命的な落とし穴でした。
X5 SDK のデフォルトのカーネル設定には # CONFIG_IPV6 is not set と書かれています。hobot_x5_soc_defconfig と hobot_x5_soc_perf_defconfig のどちらもです。
一方、Matter は IPv6 の上に成り立っています。デバイス探索に使う mDNS も、Apple Home とゲートウェイ間の通信も、すべて IPv6 です。SDK デフォルトのカーネルのままだと、ゲートウェイプログラムは起動するのに、Apple Home からは永遠に見つかりません。
スクリプトではこの 2 つの設定をチェックし、無効なら CONFIG_IPV6=y に書き換えて、./bd.sh boot でカーネルを再ビルドします。今後 SDK を新しいバージョンに上げるときは、この項目を改めて確認する必要があります。
パーティションテーブル:700MB の app パーティションを削除する
SDK デフォルトのパーティションテーブルには 700MB の app パーティションがありますが、中身は D-Robotics のサンプルプログラムだけで、ゲートウェイでは使いません。スクリプトでは短い Python で、x5-soc-debug-gpt.json と x5-soc-release-gpt.json からこれを削除しています。
パーティションテーブルを変えたら、一度フルビルドし直して新しい GPT を生成する必要があります。スクリプトは変更済みかどうかを判定し、変更済みならスキップするので、毎回フルビルドになることはありません。
削除後は、system パーティションが 250MB、userdata はわずか 50MB です。ゲートウェイのデバイステーブルと Matter のデータを保存するには今のところ 50MB で足りていますが、量産前には実際の要件に合わせて必ず計画し直す必要があります。
できあがったもの
| ファイル | 用途 |
|---|---|
out/product/emmc_disk.img |
eMMC の完全なイメージ。fastboot、DFU、TFTP で書き込み可能 |
out/product/emmc_disk.simg |
スパースイメージ。サイズは小さいが DFU では使えない |
out/product/uart_usb/ |
未書き込みチップへの書き込み用ブートファームウェア |
out/product/*.img |
各パーティション個別のイメージ |
書き込みツールの xburn は、SDK ダウンロードディレクトリの software_tools/download_tools/ にあります。
まだやっていないこと
イメージがビルドできたのは最初の一歩にすぎません。量産までには、まだ次の作業が残っています。
- ボードに書き込んで実機検証:自動起動、IPv6、Matter のコミッショニング、ログを、すべて実機で一通り確認する。
- A/B パーティションと OTA:SDK には A/B シームレスアップデートが組み込まれていて、
./bd.sh otapackageでフル・差分のアップデートパッケージを生成でき、デバイス側にはlibupdate.soの C インターフェースがあります。ダウンロードと呼び出しは自分たちのプログラムが担当します。ただしデフォルトはシングルパーティションなので、パーティションテーブルを変更して有効にする必要があります。これで RAUC や SWUpdate を別途組み込む必要がなくなります。 - release 構成への切り替え:adb とデバッグツールを外し、sshd を止める。
- セキュアブート:U-Boot 以降の各段の検証を自分たちの署名鍵に切り替え、eFuse に書き込む。eFuse の書き込みは元に戻せないので、まず少数の試作機で検証する。
おわりに
振り返ってみると、この段階で時間がかかったのはビルドコマンドそのものではありません。SDK の bd.sh はよくまとまっています。本当に時間を取られたのはデフォルト値の数々でした。カーネルで IPv6 が無効、ボードに bash がない、バックグラウンドプロセスが rcS に殺される、パーティションテーブルに 700MB のサンプル用パーティションが居座っている。どれもエラーは出ず、ただゲートウェイがどこかの段階で黙って動かなくなるだけです。
次のステップは、ボードが届いたらイメージを書き込み、同時に Silicon Labs の Bluetooth モジュールをつないで、ゲートウェイが初めて実機上で Bluetooth Mesh のライトを制御するところまで持っていくことです。
D-Robotics X5、Rockchip、あるいはその他の Buildroot SDK でゲートウェイや IoT デバイスを作っている方がいれば、ぜひ情報交換しましょう:linktr.ee/weiyii