玩客云内核升级与 SD 卡救援系统制作
设备是一台迅雷玩客云 (OneCloud),SoC 是 Amlogic S805 (meson8b, 4×Cortex-A5)。原本跑的是自编译的 6.x 内核,这次升级到 7.1.4-suly。
考虑到 ARM 单板机的内核升级一旦翻车可能连 SSH 都进不去,我们同步制作了一张 SD 卡 live 系统作为救援环境——万一 eMMC 起不来,插卡就能进系统修。
准备工作站环境
以下操作在 x86_64 Linux 工作站上执行,目标架构为 ARM (armv7l)。
安装所需包
# 交叉编译工具链
pacman -S arm-linux-gnueabihf-gcc uboot-tools
# rootfs 操作相关
pacman -S arch-install-scripts archlinux-keyring ca-certificates qemu-user-static qemu-user-static-binfmt
| 包 | 用途 |
|---|---|
arm-linux-gnueabihf-gcc | 交叉编译器 |
uboot-tools | mkimage —— 将内核/initramfs 打包为 uImage/uInitrd |
arch-install-scripts | arch-chroot、pacstrap |
archlinux-keyring | pacman 签名验证 |
ca-certificates | HTTPS 下载所必需 |
qemu-user-static + qemu-user-static-binfmt | 跨架构 chroot |
搭建 ARM rootfs(live 系统用)
SD 卡需要一个完整的 Arch Linux ARM 根文件系统。直接从 Arch Linux ARM 官方获取,不能拿 x86 的 rootfs 直接改。
获取 rootfs
wget http://os.archlinuxarm.org/os/ArchLinuxARM-armv7-latest.tar.gz
bsdtar -xpf ArchLinuxARM-armv7-latest.tar.gz -C /mnt
跨架构 chroot
从 x86 机器上 chroot 进 ARM rootfs 需要打开 binfmt 支持,arch-chroot 会自动处理这一步:
systemctl restart systemd-binfmt
arch-chroot /mnt
注意:这里必须用
arch-chroot,不能用chroot。arch-chroot会额外挂载必要的虚拟文件系统并处理 DNS 解析。
初始化 pacman 密钥环
ARM 密钥链和 x86 不同,必须单独初始化:
pacman-key --init
pacman-key --populate archlinuxarm
不跑这一步的话,pacman 会报签名错误,所有包都装不了。
安装 live 系统必备包
pacman -S openssh
openssh—— 远程登录
交叉编译内核
获取 DTS
玩客云的设备树 meson8b-onecloud.dts 来自 Armbian 社区维护的源码。复制到内核源码树的 arch/arm/boot/dts/amlogic/ 下,并在同目录的 Makefile 中 meson8b-odroidc1.dtb 后面插入一行:
meson8b-onecloud.dtb \
内核配置
使用裁减过的无头服务器配置(禁用 DRM / SOUND / MALI),以下配置确认内置(不是模块):
| 配置 | 用途 |
|---|---|
CONFIG_F2FS_FS=y | rootfs 文件系统 |
CONFIG_STMMAC_ETH=y | 网卡 dwmac 驱动 |
CONFIG_DWMAC_MESON=y | Meson 平台网卡适配 |
CONFIG_REALTEK_PHY=y | RTL8211F 千兆 PHY |
版本号处理
确保内核版本不带 -dirty:
# 确保源码树干净
git status --porcelain
# 创建 localversion 文件(可选)
echo "-suly" > localversion-suly
# 或者打 tag
git tag v7.1.4-suly
编译
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make -j$(nproc) LOADADDR=0x00008000 uImage
make dtbs
make modules
make INSTALL_MOD_STRIP=1 INSTALL_MOD_PATH=/tmp/onecloud-modules modules_install
| 参数 | 值 |
|---|---|
| LOADADDR | 0x00008000 |
| 交叉编译器 | arm-linux-gnueabihf-gcc |
| uImage | uboot-tools 中的 mkimage |
打包为可分发归档
⚠️ 关键注意事项
Arch Linux ARM 的
/lib是到/usr/lib的符号链接。不能把模块放到lib/modules/, 必须放到usr/lib/modules/,否则解压时会覆盖符号链接导致系统所有动态链接的二进制无法工作。同时要创建
/boot/vmlinuz(mkinitcpio 预设需要)和pkgbase文件(供 ALPM 钩子识别)。
# 内核版本号
KVERSION=7.1.4-suly
# 准备打包目录
mkdir -p /tmp/onecloud/boot/dtb /tmp/onecloud/usr/lib/modules
# 内核与 DTB
cp arch/arm/boot/uImage /tmp/onecloud/boot/
cp arch/arm/boot/zImage /tmp/onecloud/boot/
cp /tmp/onecloud/boot/zImage /tmp/onecloud/boot/vmlinuz
cp arch/arm/boot/dts/amlogic/meson8b-onecloud.dtb /tmp/onecloud/boot/dtb/
# 模块(放到 usr/lib/modules/,不是 lib/modules/)
cp -a /tmp/onecloud-modules/lib/modules/${KVERSION} /tmp/onecloud/usr/lib/modules/
# pkgbase —— 让 mkinitcpio ALPM 钩子能自动识别这个内核
echo "onecloud" > /tmp/onecloud/usr/lib/modules/${KVERSION}/pkgbase
# 打包
tar -czvf /tmp/onecloud-kernel-${KVERSION}.tar.gz -C /tmp/onecloud .
打包后的目录结构:
boot/
├── uImage ← U-Boot 启动用
├── zImage ← 标准 zImage
├── vmlinuz ← mkinitcpio 预设需要(cp zImage)
└── dtb/
└── meson8b-onecloud.dtb
usr/
└── lib/modules/${KVERSION}/
├── pkgbase ← 内容为 "onecloud"
├── modules.dep
└── kernel/ ← 1523 个 ko 模块
(可选)打包内核头文件
如果需要在外挂模块编译场景(如 DKMS、WireGuard 模块)使用,额外打包头文件:
mkdir -p /tmp/onecloud-headers/usr/lib/modules/${KVERSION}
cp -a /tmp/onecloud-modules/lib/modules/${KVERSION}/build /tmp/onecloud-headers/usr/lib/modules/${KVERSION}/
tar -czvf /tmp/onecloud-headers-${KVERSION}.tar.gz -C /tmp/onecloud-headers .
部署到 OneCloud
解压到 / 即可:
sudo tar -xf /tmp/onecloud-kernel-7.1.4-suly.tar.gz -C /
# 如果触发了 mkinitcpio ALPM 钩子(有 pkgbase 时),
# 或者手动生成 initramfs:
sudo mkinitcpio -p onecloud
# 打包为 U-Boot 的 uInitrd 格式:
sudo mkimage -A arm -O linux -T ramdisk -C gzip \
-d /boot/initramfs-linux.img /boot/uInitrd
sudo reboot
mkinitcpio 预设说明
/etc/mkinitcpio.d/onecloud.preset(手动创建,不在 tarball 中):
ALL_config=/etc/mkinitcpio.conf
ALL_kver=/boot/vmlinuz
PRESETS=('onecloud')
onecloud_image=/boot/initramfs-linux.img
ALL_kver=/boot/vmlinuz:路径格式,告诉 mkinitcpio 内核在/boot/vmlinuz。 tarball 已将 zImage 复制一份为 vmlinuz。onecloud_image=/boot/initramfs-linux.img:initramfs 输出路径。
ALPM 钩子(自动触发)
安装标准 linux-armv7 包后,/usr/share/libalpm/hooks/90-mkinitcpio-install.hook
会在内核升级时自动触发 mkinitcpio。自编译内核通过 pkgbase 文件告知钩子使用哪个 preset:
# /usr/lib/modules/<kver>/pkgbase 内容
onecloud
钩子脚本会读取该文件,找到 /etc/mkinitcpio.d/onecloud.preset 并执行
mkinitcpio -p onecloud。
SD 卡 Live 系统制作
分区方案
分区表借用了 Armbian 已验证可启动的方案:
| 分区 | 格式 | 大小 | 起始扇区 |
|---|---|---|---|
| p1 | vfat | 256M | 32768 |
| p2 | ext4 | 1.1G | — |
写入内容
bsdtar -xpf ArchLinuxARM-armv7-latest.tar.gz -C /mnt/p2
cp uImage /mnt/p1/boot/
cp dtb/meson8b-onecloud.dtb /mnt/p1/boot/dtb/
cp uInitrd /mnt/p1/boot/
启动前必须的配置
1. SSH host key
ssh-keygen -A
sshd 没有 host key 会直接拒绝启动。
2. machine-id
systemd-machine-id-setup
或用固定值:
echo "00000000000000000000000000000000" > /etc/machine-id
不设则 MAC 地址随机化,DHCP 分配的 IP 每次重启都不一样。
3. sshd 启用
systemctl enable sshd
4. root 密码
passwd root
启动方式
玩客云的 U-Boot 默认已经 SD 卡优先启动,插卡上电即可。不需要修改 eMMC 里的 boot.scr。
故障排查
| 现象 | 排查方向 |
|---|---|
| 上电后没有 IP | DTB 文件名不匹配,内核没完整启动 |
| 有 IP 但 SSH 连不上 | 缺少 SSH host key |
| 每次重启 IP 不一样 | machine-id 未初始化 |
| 插卡还是从 eMMC 启动 | bootcmd 已被修改过,需串口确认 |
eMMC 内核升级(live 救援流程)
SD 卡 live 系统启动后,用它作为救援环境来升级 eMMC 里的系统。
mount /dev/mmcblk1p2 /mnt
mount /dev/mmcblk1p1 /mnt/boot
arch-chroot /mnt
tar xzf /tmp/onecloud-kernel-7.1.4-suly.tar.gz -C /
mkinitcpio -p onecloud
mkimage -A arm -O linux -T ramdisk -C gzip \
-d /boot/initramfs-linux.img /boot/uInitrd
exit
umount -R /mnt
重启后确认新内核生效:
uname -a
# Linux 7.1.4-suly #1 SMP armv7l GNU/Linux
附加收获:网卡千兆
升级到 7.1.4+ 后,之前旧内核上网卡只能协商 100M 的问题自动消失了。PHY(RTL8211F)被正确识别并协商到 1000Mbps:
meson8b-dwmac: PHY [RTL8211F Gigabit Ethernet]
Link is Up - 1Gbps/Full - flow control rx/tx
旧内核的驱动或配置问题在新内核不复存在。
运行状态展示
suly@CHIX-OneCloudA
-------------------
OS: Arch Linux ARM armv7l
Host: Xunlei OneCloud
Kernel: Linux 7.1.4-suly
Uptime: recently booted
Packages: 262 (pacman)
CPU: meson8b (4) @ 1.54 GHz
Memory: ~170 MiB / 987 MiB (17%)
Disk (/): 2.7 GiB / 7.0 GiB (39%) - f2fs
Disk (/mnt/data): 3.1 GiB / 29.8 GiB (10%) - f2fs
Local IP (eth0): 192.168.0.215/24
错误履历
2026-07-22:tarball 中使用了 lib/modules/ 导致系统崩溃
教训:Arch Linux 的 /lib 是指向 /usr/lib 的符号链接。将模块放入
lib/modules/ 打包后解压到 / 会覆盖此符号链接,导致动态链接器
/lib/ld-linux-armhf.so.3 不可用,所有动态链接的二进制(包括 sudo、sed)
全部崩溃。
修复方式:live-cd 启动 → 挂载 eMMC → 重建 /lib → usr/lib 符号链接 → 将模块
从 /lib/modules/ 移动到 /usr/lib/modules/。
预防:打包时始终使用 usr/lib/modules/ 路径,并在打包后使用 tar -tvf
检查是否有裸的 lib/ 目录。
2026-07-22(第二弹):tarball 顶层目录所有者导致 / 被改成 suly
教训:打包时未注意顶层目录 ./ 的所有权。sudo tar -xf 会保留包内
记录的所有权信息,导致根目录 / 被改为 suly:suly,所有 pacman 后处理钩子
报 Detected unsafe path transition。
修复方式:
sudo chown root:root /
预防:打包时使用 fakeroot 或显式设置顶层目录为 root:root:
fakeroot tar -czvf pkg.tar.gz -C tmpdir .
# 等价于:
tar --owner=root --group=root -czvf pkg.tar.gz -C tmpdir .
本文由 AI 梨梨协助 chixiaoshu 完成
