Matter 网关的 Linux 系统怎么做:在 WSL 里编译 RDK X5 SDK 和 Buildroot 镜像
用 WSL 编译地瓜 RDK X5 SDK,把 Rust 网关程序做进 Buildroot 系统镜像:交叉编译、覆盖目录、停用服务、开机自启,以及内核默认没开 IPv6、没有 bash、分区表这些坑。
上一篇讲了用 Rust 和 rs-matter 写网关程序,它目前跑在电脑上。要变成一台能卖的网关,还差一层:网关自己的 Linux 系统。
开发板到手,通常都带一个 Ubuntu 镜像,插电就能用。很多人的第一反应是:直接在 Ubuntu 上装好网关程序,不就行了?
开发阶段可以。量产不行。这篇记录我们在 WSL 里编译地瓜 RDK X5 SDK、用 Buildroot 做网关系统镜像的过程,以及几个不动手就发现不了的坑。
为什么不直接用 Ubuntu
地瓜的 X5 SDK 本身就给了两种根文件系统:Ubuntu 22.04 镜像给开发用,Buildroot 给量产用。两者的差别放在网关这个场景里很明显:
| Ubuntu 镜像 | Buildroot 定制系统 | |
|---|---|---|
| 体积 | 几个 GB | 几百 MB,system 分区只有 250MB |
| 里面有什么 | 桌面级的一整套软件包 | 只有你选进去的东西 |
| 开机服务 | 很多,不清楚哪些在跑 | 每一个都看得见、能删 |
| 升级方式 | apt,逐个包升级 | 整个系统镜像替换,配合 A/B 分区可以回滚 |
| 安全审核 | 要解释一大堆用不上的软件 | 攻击面小,好讲清楚 |
网关是一台放在用户家里、几年不会有人登录的设备。它需要的是:开机就跑网关程序,系统只读不会被写坏,升级失败能自动回到旧版本。这些在 Buildroot 里都是常规做法,在 Ubuntu 上反而要费劲去裁。
为什么先在 RDK X5 上做
主控选型那篇里,RDK X5 被我们放在「能用,但不适合做网关」:10 TOPS 的 AI 算力,网关用不上,成本也偏高。
但它有一个很实在的优点:SDK 公开可下载,编译、系统裁剪、安全启动、A/B OTA、OP-TEE 安全存储都有正式文档。瑞芯微的 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 上执行一行就行:
wsl -d x5build -- bash /mnt/d/<项目目录>/x5/build.sh
脚本做了这几件事:同步源码、交叉编译网关程序、放进根文件系统、重新打包镜像。中间有几个值得说的细节。
源码先同步到 Linux 文件系统里再编
项目代码在 Windows 的 D 盘,WSL 里对应 /mnt/d。直接在 /mnt/d 下编 Rust,速度慢很多,因为每次文件访问都要跨一次 Windows 文件系统。所以脚本先用 rsync 把代码同步到 WSL 自己的目录(排除 target 和 .git),再在那里编。
用 glibc 目标,而不是 musl
之前做主控选型时,我们计划用 aarch64-unknown-linux-musl 编一个静态链接的二进制,瑞芯微和地瓜通用。
到了 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/ 删掉一个文件,镜像里就恢复成 SDK 原版,不会残留上一次的修改。
停掉用不上的开机服务
停服务也是靠覆盖:放一个同名脚本,内容只有 exit 0,替换掉 SDK 原来的启动脚本。目前停了这些:
| 服务 | 停用原因 |
|---|---|
S50telnet |
明文远程登录,不符合欧盟 EN 18031、英国 PSTI 这类网络安全法规的要求 |
S40bluetoothd |
模组自带的蓝牙不用,蓝牙 Mesh 走单独的芯科模组 |
S65ptp4l、S66phc2sys |
PTP 精确时间同步,网关不需要 |
S90cam-service |
摄像头服务 |
S99iiod |
IIO 传感器服务 |
sshd 和 adb 开发阶段先留着,量产再停。
开机自启:一个 & 引出的坑
网关程序要开机自动启动,崩了还要自动拉起来。X5 用的是 SysV init,写一个 /etc/init.d/S95gateway 就行。
第一个要注意的:板子上没有 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 默认配置
内核默认没开 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 永远找不到它。
脚本里会检查这两个配置,发现没开就改成 CONFIG_IPV6=y,再用 ./bd.sh boot 重编内核。以后换新版 SDK,这一项要重新确认。
分区表:去掉 700MB 的 app 分区
SDK 默认的分区表里有一个 700MB 的 app 分区,里面只装了地瓜的示例程序,网关用不上。脚本用一小段 Python 把它从 x5-soc-debug-gpt.json 和 x5-soc-release-gpt.json 里删掉。
分区表一改,就要整体重编一次,生成新的 GPT。脚本会判断是否已经改过,改过就跳过,不会每次都全量编译。
去掉之后,system 分区 250MB,userdata 只有 50MB。50MB 存网关的设备表和 Matter 数据目前够用,但量产前一定要按实际需求重新规划。
编出来的东西
| 文件 | 用途 |
|---|---|
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 的示例分区。每一个都不会报错,只会让网关在某个环节悄悄不工作。
下一步是等板子到手,把镜像烧进去,同时把芯科蓝牙模组接上,让网关第一次在真实硬件上控制一盏蓝牙 Mesh 灯。
如果你也在用地瓜 X5、瑞芯微或者其他 Buildroot SDK 做网关和 IoT 设备,欢迎交流:linktr.ee/weiyii