玩客云内核升级与 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-toolsmkimage —— 将内核/initramfs 打包为 uImage/uInitrd
arch-install-scriptsarch-chroot、pacstrap
archlinux-keyringpacman 签名验证
ca-certificatesHTTPS 下载所必需
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=yrootfs 文件系统
CONFIG_STMMAC_ETH=y网卡 dwmac 驱动
CONFIG_DWMAC_MESON=yMeson 平台网卡适配
CONFIG_REALTEK_PHY=yRTL8211F 千兆 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
参数值
LOADADDR0x00008000
交叉编译器arm-linux-gnueabihf-gcc
uImageuboot-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 已验证可启动的方案:

分区格式大小起始扇区
p1vfat256M32768
p2ext41.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。

故障排查

现象排查方向
上电后没有 IPDTB 文件名不匹配,内核没完整启动
有 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

fastfetch截图和ZRAM详细

错误履历

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 完成