1. 为什么要在RK3588上折腾Ubuntu 26.10
把Ubuntu 26.10跑在RK3588这块板子上,听起来像是个"吃饱了撑的"项目——毕竟RK3588出厂通常配的是Debian或者Ubuntu 22.04的BSP,厂商给的镜像开箱即用,何必自找麻烦?但如果你真的在嵌入式圈子里待过一段时间,就会明白这件事的价值远不止"尝鲜"两个字。
先说清楚这个项目的定位:RK3588是一颗8核ARM处理器(4×Cortex-A76 + 4×Cortex-A55),带Mali-G610 GPU和6TOPS NPU,被大量用在边缘计算盒子、工控机、NAS、AI推理设备上。而Ubuntu 26.10代表的是更新的内核、更新的GCC、更新的glibc、更新的Mesa和更完整的Wayland支持。把这两者结合,本质上是在解决一个很现实的问题:厂商BSP的内核版本往往停留在5.10或6.1,用户空间软件包老旧,很多新版本的AI框架、图形栈、容器工具链在上面跑起来各种别扭。
我自己手上有几块不同厂商的RK3588板子,从4GB到16GB内存的都有。过去一年里,我反复遇到同一类问题:想跑一个新版的推理框架,结果glibc版本不够;想用最新的Mesa做OpenGL ES 3.2的测试,结果驱动版本对不上;想用较新的Docker和containerd组合,结果内核的cgroup v2支持不完整。每次都要么降级软件,要么自己编译一堆依赖,非常痛苦。所以当Ubuntu 26.10的镜像和源码树可以获取之后,我就决定认真做一次移植。
这篇文章适合三类人看:第一类是手里有RK3588板子、想跑更新系统的嵌入式开发者;第二类是想了解ARM64平台Linux移植完整流程的爱好者;第三类是需要在RK3588上部署较新软件栈、被旧BSP折磨过的工程师。我会把整个流程拆开讲,包括为什么这么选、每一步在干什么、哪些地方容易翻车,以及我实际踩过的坑。
需要提前说明的是,这不是一篇"复制粘贴就能跑"的教程。RK3588的板子型号太多,电源管理芯片、DDR颗粒、eMMC、网卡、WiFi模组各不相同,设备树必须针对你的具体板子调整。我会给出通用的方法和判断依据,但具体参数你得自己对着原理图和厂商资料改。
2. 移植前的底层认知:RK3588的启动链路和Ubuntu的适配边界
2.1 RK3588从上电到进系统的完整链路
很多人移植失败,根本原因是不清楚RK3588的启动流程。这块芯片的启动链路比树莓派那种"GPU固件直接加载内核"要复杂得多,它是典型的多级引导:
第一级是BootROM,固化在芯片内部,上电后先跑。它会根据OTP配置和外部引脚状态,决定从SPI Flash、eMMC、SD卡还是USB下载模式启动。BootROM会去加载一个叫**TPL(Tiny Program Loader)**的东西,通常放在存储介质的特定偏移位置。
第二级是TPL + SPL。TPL负责初始化最基础的DDR控制器,把SPL搬到SRAM里运行;SPL再完成更完整的DDR训练和初始化,然后加载U-Boot proper。这一步是整个移植里最敏感的环节,因为DDR的时序参数、电压配置如果和你的板子不匹配,表现就是上电后串口毫无输出,或者输出几行乱码就死掉。
第三级是U-Boot proper,它负责加载设备树、内核镜像和initramfs,然后跳转到内核。U-Boot阶段需要正确配置的是启动介质、环境变量、以及传给内核的bootargs。
第四级才是Linux内核,内核启动后挂载根文件系统,最后交给systemd拉起用户空间。
理解这条链路的意义在于:当你遇到"系统起不来"的时候,必须能判断问题出在哪一级。串口完全无输出,大概率是TPL/SPL或DDR的问题;有U-Boot输出但卡在加载内核,是U-Boot配置或镜像格式的问题;内核有输出但卡在挂载根文件系统,是bootargs或存储驱动的问题;内核完全启动但卡在systemd,才是用户空间的问题。这个判断逻辑贯穿整个移植过程。
2.2 为什么Ubuntu 26.10的内核不能直接用
Ubuntu官方为ARM64服务器提供的内核镜像是为服务器平台(比如Ampere、鲲鹏等)准备的,它不包含RK3588所需的任何设备树和平台驱动。你直接把Ubuntu的generic内核放到RK3588上,结果就是内核根本认不出这块SoC,连串口都起不来。
所以正确的思路是:用Ubuntu 26.10的用户空间(rootfs),搭配一个为RK3588适配过的内核。这个内核可以来自几个渠道:Rockchip官方维护的kernel分支、主线Linux内核(mainline)加上RK3588的设备树、或者社区维护的针对RK3588优化的内核树。
我个人的选择是主线内核 + Rockchip的补丁集。原因很直接:主线内核版本新、代码质量高、后续升级方便,但RK3588的某些外设(尤其是NPU、VPU硬件编解码、部分显示输出)在主线里支持还不完整,需要Rockchip的补丁来补齐。Ubuntu 26.10对应的内核版本比较新,主线里RK3588的基础支持(CPU、DDR、eMMC、SD、串口、以太网、USB)已经相当成熟,日常使用没问题。
2.3 用户空间和内核的版本匹配问题
这里有个容易被忽略的细节:Ubuntu 26.10的glibc版本较新,它要求内核提供较新的系统调用和ABI支持。如果你用一个太老的内核(比如5.10)去跑Ubuntu 26.10的rootfs,某些程序会因为系统调用缺失而崩溃。反过来,用太新的内核跑老rootfs通常问题不大,因为内核保持向后兼容。
实测下来,内核版本建议不低于6.6,最好是6.12或更新的LTS版本。Ubuntu 26.10的用户空间在这个内核版本上跑起来比较稳。如果你的内核版本偏低,可能会遇到systemd报错、某些库加载失败等问题。
另外要注意固件文件(firmware)。RK3588的GPU(Mali-G610)、VPU、NPU都需要对应的固件和用户空间驱动。Ubuntu的linux-firmware包里不一定包含Rockchip的这些固件,需要从Rockchip的仓库单独获取并放到/lib/firmware下。这一步漏了,表现就是GPU加速用不了、视频硬解不工作。
3. 交叉编译环境的搭建与内核配置的取舍
3.1 工具链选择:为什么不用Ubuntu自带的gcc
在x86主机上给ARM64编译,你需要一套交叉编译工具链。Ubuntu仓库里有gcc-aarch64-linux-gnu,装起来方便,但我实测下来更推荐用ARM官方或Linaro维护的预编译工具链,原因是Ubuntu自带的版本有时候和内核源码里的某些特性(比如特定版本的binutils对某些指令的支持)配合不好,编译内核时可能报奇怪的错误。
我用的是一套基于GCC 13的aarch64工具链。安装方式很简单,下载解压后把bin目录加到PATH里就行。验证方法是:
aarch64-linux-gnu-gcc --version能正确输出版本号就说明工具链可用。这里有个小坑:如果你的主机上同时装了多个版本的交叉工具链,一定要确认PATH里优先的是你想要的那个,否则编译到一半发现用的是系统自带的旧版本,会浪费很多时间。
3.2 内核配置:defconfig只是起点
拿到内核源码后,第一件事是选一个基础配置。Rockchip的kernel仓库里有rockchip_linux_defconfig,主线内核里有defconfig(ARM64的通用配置)。我的做法是以主线的defconfig为基础,再用Rockchip的配置片段补充。
具体操作是先用make ARCH=arm64 defconfig生成基础配置,然后通过scripts/config或者直接编辑.config打开RK3588相关的选项。关键选项包括:
| 配置项 | 作用 | 是否必须 |
|---|---|---|
| CONFIG_ARCH_ROCKCHIP | 启用Rockchip平台支持 | 必须 |
| CONFIG_ARM64_CRYPTO | ARM64加密指令加速 | 建议 |
| CONFIG_ROCKCHIP_IODOMAIN | IO域电源管理 | 必须 |
| CONFIG_DRM_ROCKCHIP | 显示子系统 | 需要显示则必须 |
| CONFIG_ROCKCHIP_IOMMU | IOMMU支持 | 建议 |
| CONFIG_PHY_ROCKCHIP_* | 各类PHY驱动 | 按外设需要 |
| CONFIG_MMC_DW_ROCKCHIP | eMMC/SD控制器 | 必须 |
| CONFIG_STMMAC_ETH | 千兆以太网 | 按需 |
配置完成后用make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)编译。第一次编译建议不要开太多并行任务,如果报错,日志更容易定位。等确认能编过了再加大并行度。
3.3 设备树:移植里最需要耐心的地方
设备树(Device Tree)是ARM Linux移植的核心,它描述了板子上的硬件怎么连接、地址是多少、用什么驱动。RK3588的SoC级设备树(rk3588.dtsi)通常由内核或Rockchip提供,但板级设备树(rk3588-yourboard.dts)必须你自己写或改。
板级设备树里最关键的几块:
内存节点要写对DDR的容量和起始地址。RK3588的DDR起始地址通常是0x0,容量按你的板子填(4GB、8GB、16GB)。填错了内核启动时会报内存相关错误。
存储节点要指定eMMC和SD卡控制器的状态、总线宽度、时钟频率。如果你的板子eMMC是HS400模式,还要配置对应的PHY。
串口节点是调试的生命线,必须确保调试串口的引脚复用(pinctrl)配置正确,否则你连日志都看不到。
电源管理涉及一堆regulator节点,描述各个电源域怎么供电。这块最容易出错,因为不同板子的PMIC型号和连接方式不同。如果regulator配置错了,表现可能是某个外设不工作,或者系统运行中随机死机。
我的经验是:先让最小系统跑起来——只配置CPU、DDR、串口、eMMC/SD,确认能进U-Boot、能加载内核、能挂载rootfs。然后再一个一个加外设,每加一个测试一次。不要一次性把所有外设都配上,出了问题根本不知道是哪个节点的锅。
4. 从U-Boot到rootfs:让系统真正跑起来的完整链路
4.1 U-Boot的编译和烧录位置
U-Boot这块,Rockchip有自己维护的分支,主线U-Boot对RK3588的支持也在逐步完善。我建议优先用Rockchip的U-Boot,因为它的DDR初始化和TPL/SPL支持更成熟,尤其是DDR训练部分,主线版本在某些板子上可能不稳定。
编译U-Boot需要指定板子配置,比如make rk3588_defconfig,然后编译出u-boot.bin、u-boot.itb、idbloader.img等文件。烧录位置非常关键:
idbloader.img烧到存储介质的0x40扇区(偏移32KB)u-boot.itb烧到0x4000扇区(偏移8MB)
这两个偏移是RK3588 BootROM约定的,写错了BootROM就找不到引导程序。用dd命令烧录时一定要算清楚:
dd if=idbloader.img of=/dev/sdX seek=64 conv=fsync dd if=u-boot.itb of=/dev/sdX seek=16384 conv=fsync注意seek的单位是扇区(512字节),64扇区就是32KB,16384扇区就是8MB。烧录前务必确认/dev/sdX是你的目标设备,写错盘会直接毁掉主机数据,这个坑我见过不止一个人踩。
4.2 内核镜像的格式和加载方式
RK3588的U-Boot支持多种内核镜像格式,最常用的是FIT image(.itb),它把内核、设备树、initramfs打包在一起,U-Boot通过配置里的地址去加载。也可以用传统的Image+ 单独的设备树dtb。
我倾向于用FIT image,因为它把内核和设备树的对应关系固化在镜像里,减少了U-Boot环境变量配错的风险。生成FIT image需要一个.its配置文件,描述各个组件的路径和加载地址。RK3588的内核加载地址通常是0x02080000,设备树在0x0a100000附近,具体值参考Rockchip的文档。
U-Boot的bootargs里必须包含正确的root=参数,指向你的根文件系统所在的分区和类型。比如从eMMC的第三个分区启动:
root=/dev/mmcblk0p3 rootwait rw如果是用initramfs,还要加initrd相关参数。bootargs里一个字符写错,内核就找不到rootfs,表现是内核panic然后停在"VFS: Unable to mount root fs"。
4.3 rootfs的制作:从Ubuntu base到可启动系统
Ubuntu 26.10的ARM64 rootfs可以用官方提供的base tarball,也可以用debootstrap自己构建。我推荐用官方base tarball,因为它的包依赖关系已经处理好,省去很多麻烦。
拿到tarball后,解压到一个目录,然后chroot进去做基础配置:
sudo tar -xpf ubuntu-base-26.10-base-arm64.tar.gz -C rootfs/ sudo cp /usr/bin/qemu-aarch64-static rootfs/usr/bin/ sudo chroot rootfs/chroot之后要做的几件事:配置apt源(换成ARM64的镜像源)、安装必要的包(比如systemd、udev、netplan、openssh-server)、设置root密码、配置网络。这里有个关键点:chroot环境下很多服务不能正常启动,所以不要在里面执行systemctl命令,等真正启动到板子上再配置。
rootfs准备好后,把它写入eMMC或SD卡的对应分区。分区布局我一般用:第一个分区放引导文件(FAT32),第二个分区放内核和设备树,第三个分区放rootfs(ext4)。ext4分区建议留足空间,Ubuntu 26.10装完基础系统加上常用工具,10GB起步比较稳妥。
4.4 首次启动的调试策略
第一次启动大概率不会一次成功。我的调试策略是串口日志优先,从BootROM的输出开始看,逐级确认:
- BootROM有没有输出?没有的话检查启动介质选择和烧录偏移。
- TPL/SPL有没有输出?没有的话检查DDR配置。
- U-Boot有没有输出?有的话看它有没有找到内核镜像。
- 内核有没有输出?有的话看它卡在哪一步。
- 内核启动完成后,systemd有没有正常拉起?没有的话看是哪个服务失败。
串口工具用picocom或minicom都行,波特率通常是1500000(RK3588的调试串口默认是这个非标准波特率,不是115200,这点很多人第一次会搞错)。如果串口输出乱码,先确认波特率,再确认引脚复用配置。
5. 实测中暴露的问题和对应的解决思路
5.1 显示输出:Mali-G610的驱动适配
RK3588的显示子系统在主线内核里的支持是分阶段的。HDMI输出相对成熟,DisplayPort和MIPI-DSI的支持要看具体内核版本。我实测下来,用主线6.12内核,HDMI能正常输出到1080p和4K,但刷新率和色彩格式需要根据显示器调整。
Mali-G610的GPU驱动分两部分:内核态的Panfrost/Panthor驱动,和用户态的Mesa。Ubuntu 26.10自带的Mesa版本对Mali-G610的支持已经不错,但需要确认内核里的Panfrost驱动被编译进去了,并且固件文件mali_csffw.bin放在/lib/firmware/下。缺了固件,GPU初始化会失败,表现是dmesg里有mali相关的报错,图形界面只能用软件渲染,非常卡。
验证GPU是否工作,可以用glmark2-es2跑个分,或者看/sys/class/drm/下的设备节点。如果glxinfo或eglinfo报错,基本就是驱动或固件的问题。
5.2 网络:以太网和WiFi的坑
RK3588的千兆以太网在主线内核里用stmmac驱动,通常能直接工作。但PHY的配置因板子而异,有的板子用的是RTL8211,有的是其他型号,设备树里的PHY地址和复位引脚要写对。如果网口灯不亮,先查PHY的电源和复位,再查MDIO总线上的地址。
WiFi这块比较麻烦,因为WiFi模组型号太多(AP6275、RTL8822等),驱动有的在主线里,有的需要额外打补丁。而且WiFi还需要固件文件(.bin),这些固件不一定在Ubuntu的linux-firmware包里。我的建议是先把有线网络调通,用有线网络装好必要的工具,再慢慢搞WiFi。
5.3 散热和功耗:别忽视物理层面
RK3588满负荷跑起来发热不小,没有散热片的情况下,编译大型项目几分钟就会触发降频。我实测过,加一个普通的铝制散热片,CPU持续满载温度能控制在70度左右;不加散热片,很快冲到85度以上开始降频,编译时间直接翻倍。
功耗方面,RK3588的典型功耗在5W到15W之间,取决于负载和外设。用USB-C供电的话,要确保电源能提供足够的电流(至少3A),否则大负载下会掉电重启。这个现象很容易被误判成软件问题,其实是供电不足。
5.4 常见启动失败的症状对照
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
| 串口无任何输出 | DDR配置错误、烧录偏移错误 | 检查TPL/SPL、确认烧录位置 |
| 串口乱码 | 波特率不对、时钟配置错误 | 确认1500000波特率、检查时钟树 |
| U-Boot找不到内核 | 镜像格式错误、加载地址错误 | 检查FIT配置、确认bootargs |
| 内核panic找不到rootfs | root=参数错误、存储驱动缺失 | 检查bootargs、确认eMMC/SD驱动 |
| 卡在systemd | rootfs不完整、服务配置错误 | 看具体失败的服务、检查依赖 |
| 运行中随机死机 | 电源不稳、regulator配置错误 | 检查供电、核对设备树电源节点 |
这张表是我自己排查问题时总结的,大部分启动问题都能通过串口日志定位到具体阶段,关键是不要慌,按链路一级一级往下查。
6. 移植完成后的实际体验和后续可做的事
系统跑起来之后,我做了几项基础测试来验证稳定性。CPU方面,用stress-ng跑满8个核心半小时,没有出现死机或降频异常(散热到位的前提下)。内存方面,用memtester测了4GB和8GB的板子,通过。存储方面,eMMC的读写速度在HS400模式下能到读300MB/s、写200MB/s左右,符合预期。网络方面,千兆以太网用iperf3测能跑到940Mbps左右,基本跑满。
Ubuntu 26.10的用户空间体验比旧BSP好很多。apt能直接装到较新的软件包,不用自己编译一堆依赖。Docker和containerd跑起来很顺,cgroup v2支持完整。Python环境和各种AI框架的安装也比以前省心。
后续可以做的事还有不少:NPU的驱动和RKNN工具链适配是下一个重点,目前主线内核对NPU的支持还在完善中;硬件视频编解码(VPU)的驱动也需要额外配置;PCIe和USB 3.0的外设兼容性可以进一步测试。这些我都会在后续的折腾中继续记录。
最后分享一个我在整个移植过程中体会最深的点:嵌入式移植没有"标准答案",只有"针对你的板子的答案"。同一份内核源码,在不同板子上表现可能完全不同,因为设备树、电源、DDR时序这些板级差异太大了。所以遇到问题不要急着怀疑内核有bug,先回头看看你的设备树和硬件配置对不对。串口日志是你最好的朋友,学会读日志、定位阶段,比记住任何具体命令都重要。