环境:K3 Pico-ITX,镜像 Bianbu-LXQt-UEFI-K3-v4.0.6-20260819145912,内核 6.18.3-generic #1.0.7.4,GRUB 2.14。
复现步骤
-
刷入上述官方 UEFI 镜像,正常启动系统。
-
执行 sudo apt install kdump-tools kexec-tools makedumpfile。其中 kdump-tools 安装脚本会调用 update-grub。
-
重启后,生成的 Bianbu GNU/Linux 启动项无法正常启动。
-
在 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 启动项只有 linux 和 initrd,没有 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