前阵子在闲鱼捡了块二手的 Toybrick RK3588,跑起来 cat /proc/device-tree/model 显示的是 Rockchip RK3588 TOYBRICK X10 Board。
拿回来后我直接刷了官方的 Debian 11(内核 5.10.110)。本以为能安稳用着,结果一装 Tailscale 发现根本跑不起来,仔细一扒,发现这官方固件的限制还挺多:
• 网络受限:内核没有编译 TUN 模块,Tailscale 这类组网工具根本跑不起来,只能走 userspace 模式绕路。 • AI 算力吃灰:NPU 那套栈太老了,rknpu 驱动才 v0.8.2,librknnrt 还是 1.4.0,对新一点的 YOLO 模型算子支持很差。 • 想改没源码:想自己编个内核补全功能?找不到源码。运行的内核 commit 号在公开仓库里根本搜不到,而且带有 -dirty后缀,说明编译时还有没提交的本地改动。
既然官方固件不好使,干脆自己从源码编一套完整的系统。内核、rootfs 全按自己的需求定制。
结果这一编,踩出了一堆坑。今天把整个折腾过程和避坑经验复盘一下。
第一个坎:公开源码里没有我的板子
Rockchip 官方的 rockchip-linux/kernel 仓库里,Toybrick 的设备树只有一个 x0(rk3588-toybrick-x0-linux.dts)。
而我的板子是 X10。同一条产品线,底板外设的定义差挺多。如果直接拿 x0 的设备树强刷,网口、PCIe、NPU 供电能不能对上完全是开盲盒。
在网上翻了很久,最后在一个意想不到的地方找到了突破口。
转机来了:有一家做 RK3588 板卡的厂商,自建了一个 GitLab,里面镜像了完整的 Rockchip BSP 源码树。Rockchip 的 BSP 是同一棵树发给所有下游厂商的,里面自带全部参考板的设备树。人家镜像这棵树是为了自家的板子,但顺手把 Toybrick 的也带进来了。
更关键的是,他们这份是 Linux 6.1 的 SDK(linux-6.1-stan-rkr5),比官方固件的 5.10 新了一代,正好满足我“新内核 + 新 NPU 栈”的需求。
拉下源码后,我核对了一下设备树,发现一个极其容易被绕进去的坑:文件名虽然叫 rk3588-toybrick-x0-linux.dts,但打开看里面的 model 和 compatible,写的其实是 X10。
所以,查设备树千万别只看文件名,一定要打开确认内容。核对无误后,这才敢继续往下走。
第二个坎:厂商没给这块板子配 lunch
SDK 拉完,按正常流程应该执行 ./build.sh lunch,选个板级配置就能开编了。
但我一跑发现,列表里全是那家厂商自家的板子和 Rockchip 官方 EVB,一个 Toybrick 的影子都没有。grep 搜了一圈也是空的。这就意味着:设备树在,但没人给它写板级配置。
没办法,只能自己造一个。好在扒开源码一看,Rockchip 这套 SDK 的板级配置文件极其简单,比如随便看一份现成的:
cat device/rockchip/rk3588/rockchip_rk3588_xxx_linux_f0_defconfig内容居然只有两行:
RK_KERNEL_DTS_NAME="rk3588-xxx-linux-f0"RK_USE_FIT_IMG=y绝大部分配置都继承自公共默认值,板级配置只负责指定“用哪个设备树”。照葫芦画瓢,给自己造一份:
cat > device/rockchip/rk3588/rockchip_rk3588_toybrick_x0_linux_defconfig <<'EOF'RK_KERNEL_DTS_NAME="rk3588-toybrick-x0-linux"RK_USE_FIT_IMG=yEOF注意 RK_KERNEL_DTS_NAME 的值就是设备树文件名去掉 .dts 后缀。再跑 lunch,我的板子终于出现在列表里了,果断选中,设备树匹配成功。
第三个坎:编到一半,仓库失踪
跑 ./build.sh 全量编译,将近一个小时后卡住了,报错:ERROR: /data/rk3588_sdk/external/libmali does not exist
去 external/ 目录一看,整个仓库根本没同步下来,后来看 tools 目录也是空的。
遇到这种情况直接单独补。从 manifest 里能查到仓库地址和对应分支,直接 clone 下来就行。
这里分享一个关键经验:SDK 根目录一般有个 manifest-frozen.xml,里面记录了每个仓库精确到 commit 的版本。比分支名靠谱得多,想严格对齐环境,一定要以 frozen 那份为准。补完仓库继续编,这次终于一路到底,镜像打包成功。
刷机与系统切换:从 Buildroot 到 Ubuntu
编译产物都在 output/firmware/ 下,包括 boot.img(内核+设备树)、uboot.img、rootfs.img 等。注意这些全是软链接,如果要打包传到别的机器,tar 必须加 -h 跟随软链,不然打出来全是断链。
刷机用 Windows 上的 RKDevTool。板子进 maskrom 模式,加载 parameter.txt 后,勾选对应的 img 刷入。第一版我用了默认的 Buildroot,体积小,系统正常跑通了。
但 Buildroot 太精简,我需要完整的 apt 生态,于是决定换 Ubuntu。这套 SDK 直接支持:
./build.sh jammy这里有一个非常实用的机制:只刷 rootfs,不用重刷整机。看一眼分区表就会发现,每个分区都是固定槽位。换 rootfs 就只刷 0xF8000 这个地址,内核、引导原封不动;反过来如果只改内核,就只刷 boot 分区。互不影响,极其方便。
(注:我也试了编 Debian,但因为宿主机的 live-build 版本不兼容报错了。既然 Ubuntu 能用,就没再死磕 Debian。)
解决痛点:Docker 与 TUN
回到最初的痛点——缺 TUN 模块。这次自己编,本打算手动开一堆内核选项,结果 grep 一看,这套 6.1 的 defconfig 里居然默认已经开启了 CONFIG_TUN、CONFIG_BRIDGE 等网络组件。
跑 Docker 时如果提示缺 netfilter 模块,SDK 里也备了现成的配置片段:kernel-6.1/arch/arm64/configs/rockchip_linux_docker.config里面 overlayfs、cgroup、IPVS 全套都有,合并进主 defconfig,重编一下内核(./build.sh kernel),然后单刷 boot.img 即可,rootfs 里的数据一点不丢。
NPU 性能验证
系统跑起来,最后验证最核心的 NPU 算力。查版本:cat /sys/kernel/debug/rknpu/version,显示 v0.9.8,比出厂的 0.8.2 新了一代。
拉官方仓库跑 YOLOv5 demo,跑那张经典的公交车测试图。4 个人 + 1 辆公交全部精准检出,平均 16.37ms 一次,约 61 FPS。
重点提醒:驱动、运行时(librknnrt)、PC 端转模型的 toolkit2,这三者的版本必须严格对齐,否则转出来的模型在板子上绝对跑不起来。
踩坑血泪史,集中排雷
整个流程踩了不少坑,按顺序整理出来,供大家避雷:
1. manifest 别信错版本:网上乱七八糟的 manifest 很多,一定要以你本地 SDK 里的 manifest-frozen.xml为准,精确到 commit 最稳妥。2. 设备树看内容不看文件名:文件名叫 x0,里面可能是 x10。一定要打开代码确认 compatible字段。3. 打包记得加 -h:tar -czhf,不加-h你打包带走的只是一堆没用的快捷方式。4. 警惕 /data软链坑:在板子上拉源码提示磁盘空间不足,一看根目录还有 22G。排查发现/data是软链到/userdata,而那个分区只有 124M(Android 遗留习惯)。拉大文件前务必df -h .确认当前目录的真实分区大小。5. 大镜像先压缩:Ubuntu 桌面版镜像将近 5G,里面大量空块,传文件前用 zstd 压一下( zstd -T0 ubuntu.img -o ubuntu.img.zst),能缩小到 1.6G。6. HDMI 接口盲盒:系统刷好后屏幕死活不亮,排查半天 drm 驱动,最后发现板子有两个 HDMI 输出口,只有其中一个是通的,我插错了。 7. 善用 adb 调试:Rockchip 的 Linux 系统默认带 adbd,黑屏或没网的时候,USB 插电脑上直接 adb shell就能进终端,比接串口方便太多了。
回顾这次折腾,技术上并没有多高深,难点全在于在一堆型号相近、版本混乱的资料里,确认哪一份才能为你所用。
夜雨聆风