Bianbu 4.0.6 K3 UEFI 镜像在 update-grub 后丢失 DTB,导致无法启动

环境:K3 Pico-ITX,镜像 Bianbu-LXQt-UEFI-K3-v4.0.6-20260819145912,内核 6.18.3-generic #1.0.7.4,GRUB 2.14。

复现步骤

  1. 刷入上述官方 UEFI 镜像,正常启动系统。

  2. 执行 sudo apt install kdump-tools kexec-tools makedumpfile。其中 kdump-tools 安装脚本会调用 update-grub

  3. 重启后,生成的 Bianbu GNU/Linux 启动项无法正常启动。

  4. 在 GRUB 编辑界面的 initrd 行后手工加入以下内容,保留其他参数,即可启动进入系统:

    devicetree /spacemit/6.18.3-generic/k3-pico-itx.dtb
    

已确认原因

原始镜像的 /boot/grub/grub.cfg 预置了带 DTB 的 K3 专用启动项,但检查原始 rootfs.ext4 和板端系统均发现:

  • 缺少 /etc/grub.d/09_bianbu_uefi
  • 通用 /etc/grub.d/10_linux 仍处于可执行状态。
  • update-grub 重新生成的 Linux 启动项只有 linuxinitrd,没有 devicetree;对应 DTB 文件仍存在。

因此,kdump 安装只是触发了配置重生成,问题在于镜像缺失可正确重建启动项的配套脚本。

建议修复

请在镜像中安装并维护 K3 专用 GRUB 生成脚本,为每个内核匹配对应的 initramfs 和 DTB,同时保留用户配置的内核参数;禁用通用 10_linux,并确保包升级不会重新启用。建议将“运行 update-grub 后仍可正常启动”加入镜像发布验证。

目前仅确认上述版本,尚未验证其他版本。

下个版本将修复此问题。

可以先手动创建/etc/grub.d/09_bianbu_uefi,权限755.
已有/etc/grub.d/10_linux调整为644即可禁用

#!/bin/sh
set -e

# Bianbu UEFI boot entries with per-board devicetree selection (efienv product_name).
# Generates entries ahead of 10_linux (which is disabled) so the system
# loads the correct board DTB from /boot/spacemit/<kver>/.
#
# 只为"存在配套 dtb 目录"的内核生成条目(devicetree 为必选项);
# 按版本从新到旧全部生成,第一条为默认,旧内核条目兼作救援入口。
#
# root= 与 search 命令的形态按运行环境自适应(见下方 running_in_chroot 分支):
#   - 构建期 chroot 输出 LABEL 占位符,镜像制作阶段(bianbu-image-create-v2 mke2fs 前
#     尚不知最终 UUID)统一 sed 替换为真实 UUID。
#   - 真机 update-grub 用 grub-probe 探测真实 UUID;探测失败退回 LABEL(文件系统
#     自带这两个 label,始终可引导)。

DIST_LABEL='Bianbu'
VARIANT_LABEL='Minimal'
BOARD='k3'

# 判断是否运行在 chroot 中(借鉴 u-boot-spacemit.postinst 的 running_in_chroot)。
# 返回 0=chroot,1=非 chroot。原理:chroot 只改变当前进程的根目录,不改变 PID 1 的根,
# 故比较两者 device:inode 即可区分;/proc 未挂载时按 chroot 处理(除非自己是 PID 1)。
running_in_chroot() {
    if [ "${SYSTEMD_IGNORE_CHROOT:-0}" = "1" ]; then
        return 1
    fi
    if [ -e "/proc/1/root" ]; then
        root_dev_ino=$(stat -c '%d:%i' / 2>/dev/null) || return 0
        proc1_root_dev_ino=$(stat -L -c '%d:%i' /proc/1/root 2>/dev/null) || return 0
        [ "$root_dev_ino" = "$proc1_root_dev_ino" ] && return 1 || return 0
    fi
    if [ ! -d "/proc" ] || [ ! -r "/proc/version" ]; then
        [ "$$" = "1" ] && return 1 || return 0
    fi
    return 0
}

# root= 与 search 命令按运行环境选择:
#   - 构建期 chroot:无真实块设备,grub-probe 会误读宿主/失败,故写死 LABEL 占位符,
#     由 bianbu-image-create-v2 打包阶段 sed 替换为最终 UUID。
#   - 真机 update-grub:grub-probe 探测当前启动介质的真实 UUID,消除多盘同 label 冲突;
#     探测失败(如 /boot 未挂载)退回 LABEL。
if running_in_chroot; then
    ROOT_PARAM="LABEL=rootfs"
    ROOT_SEARCH="--label bootfs --set=root"
else
    GRUB_PROBE=$(command -v grub-probe 2>/dev/null) || GRUB_PROBE=""
    rootuuid=""
    bootuuid=""
    if [ -n "$GRUB_PROBE" ]; then
        rootuuid=$("$GRUB_PROBE" --target=fs_uuid / 2>/dev/null) || rootuuid=""
        bootuuid=$("$GRUB_PROBE" --target=fs_uuid /boot 2>/dev/null) || bootuuid=""
    fi
    if [ -n "$rootuuid" ] && [ -n "$bootuuid" ]; then
        ROOT_PARAM="UUID=$rootuuid"
        ROOT_SEARCH="--fs-uuid $bootuuid --set=root"
    else
        ROOT_PARAM="LABEL=rootfs"
        ROOT_SEARCH="--label bootfs --set=root"
    fi
fi

first=1
for kernel in $(ls /boot/vmlinuz-* 2>/dev/null | sort -rV); do
    version="${kernel#/boot/vmlinuz-}"
    dtbdir="/boot/spacemit/$version"
    initrd="/boot/initrd.img-$version"
    [ -d "$dtbdir" ] || continue
    ls "$dtbdir"/*.dtb >/dev/null 2>&1 || continue
    [ -f "$initrd" ] || continue

    if [ "$first" = 1 ]; then
        title="$DIST_LABEL $VARIANT_LABEL UEFI"
        first=0
    else
        title="$DIST_LABEL $VARIANT_LABEL UEFI (Linux $version)"
    fi

    cat <<GRUBEOF
menuentry '$title' --class gnu-linux --class gnu --class os 'bianbu-uefi-$BOARD-$version' {
  insmod part_gpt
  insmod ext2
  search --no-floppy $ROOT_SEARCH
  echo 'Loading Linux $version ...'
  linux /vmlinuz-$version root=$ROOT_PARAM rootwait rw plymouth.prefer-fbcon plymouth.ignore-serial-consoles splash
  echo 'Loading initramfs ...'
  initrd /initrd.img-$version
  efienv -g spacemit update product_name
  echo 'Loading device tree blob ...'
  devicetree /spacemit/$version/\${product_name}.dtb
}
GRUBEOF
done

# 10_linux 已被禁用且无回退,若一个 entry 都生成不出来,grub.cfg 将无可引导项。
# 与其静默产出空配置,不如让 update-grub 报错暴露问题(构建/升级时可见)。
if [ "$first" = 1 ]; then
    echo "09_bianbu_uefi: no kernel with dtb/initrd found" >&2
    exit 1
fi