1. 介绍 / Intro
最近有项工作需要构建一个启动镜像,虽然 BIOS 引导不是必须的,但是为了更舒服的兼容性,了解了一下如何实现 BIOS/UEFI 双重启动,并且还要能够支持 UEFI secure boot 安全启动。
实际上各个 Linux 发行版的安装介质早已经实现了类似的功能,甚至一些也已经支持了 UEFI secure boot。简单搜索得知,大概思路是建立一个 hybrid 格式的磁盘镜像,也就是 MBR 和 GPT 混合格式的分区,在 MBR 空间内安装传统的 grub-pc, 而在 GPT 的 esp 分区内放置 grub-efi 启动文件。
下面我们分步骤介绍在 ubuntu 系统下构建这样一个启动镜像的步骤,完整的脚本文件见文末参考资料部分。
2. 操作步骤 / Operations
2.1 安装依赖软件
安装 shim 和签名过的 grub 二进制,以便支持 UEFI secure boot 安全启动;另外,busybox-static 将用于提供 rootfs 中的各种命令。
1 |
apt install shim-signed grub-efi grub-efi-amd64-signed grub-pc-bin busybox-static |
2.2 解压 initramfs & 安装 busybox
使用系统中已有的 initramfs, 并重新安装 busybox 工具链接。之后再对 initramfs 进行精简,移除各类非必要的软件、驱动和 firmware 等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# build initramfs cd /tmp/work/initrd unmkinitramfs /boot/initrd.img . for i in $(main/bin/busybox --list); do rm main/bin/$i; done busybox --install main/bin # delete useless drivers and tools cd /tmp/work/initrd/main rm -rf bin/plymouth usr/share/{fonts,plymouth} lib/x86_64-linux-gnu/libply* rm -rf lib/i386-linux-gnu lib/x86_64-linux-gnu/plymouth lib/firmware/{netronome,amdgpu,radeon,mellanox,liquidio,cxgb4,nvidia,phanfw.bin} cd /tmp/work/initrd/main/lib/modules/ for i in `ls`;do cd /tmp/work/initrd/main/lib/modules/"$i"/kernel/drivers rm -rf {../net,../sound,../fs,../../updates,net,gpu,regulator,thunderbolt,firewire,fpga,infiniband} done |
2.3 修改 init 启动脚本 & 构建 initrd 镜像
修改 initramfs 中的 init 启动脚本,使其在加载完必要的内核模块之后,就进入交互式 shell,而不是尝试挂载磁盘并执行真实文件系统上的 /sbin/init
1 2 3 4 5 6 7 8 9 10 11 12 |
# hack init sed -i '/load_modules/,$d' main/init cat >> main/init << 'EOF' load_modules clear && echo [ -d "/sys/firmware/efi" ] && echo "[boot mode]: UEFI" || echo "[boot mode]: BIOS" echo -n "[Secure Boot]: " && dmesg | grep -o "secureboot.*" | head -1 && echo sh EOF # build the initrd cd /tmp/work/initrd/main find . | cpio -o -Hnewc | gzip > /tmp/work/initrd/initrd.gz |
2.4 安装并配置 shim, grub, 内核和 initrd
这一步主要是放置 shim 和 grub 引导的 efi 文件(ubuntu shim/grub 具有特殊的目录结构),并准备好配置文件 grub.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# install shim, grub, kernel and initrd cd /tmp/work/rootfs cp -avH /usr/lib/shim/shimx64.efi.signed efi/boot/bootx64.efi cp -avH /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed efi/boot/grubx64.efi cp -avH $KERNEL_FILE efi/ubuntu/vmlinuz cp -avH /tmp/work/initrd/initrd.gz efi/ubuntu/initrd # setup grub.cfg cat > efi/ubuntu/grub.cfg << 'EOF' echo 'Loading kernel...' linux /efi/ubuntu/vmlinuz ro quiet echo 'Loading initramfs...' initrd /efi/ubuntu/initrd echo 'Booting...' boot EOF |
2.5 构建磁盘镜像
创建 hybrid 格式的磁盘镜像,建立好分区和文件系统,最后安装 grub-pc 并拷贝 grub-efi 文件即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# build img cd /tmp/work IMG_SIZE=$(du -hs rootfs|awk '{print $1}') fallocate -o 8M -l "$IMG_SIZE" boot.img # UEFI boot img #echo -ne 'g\n n\n\n\n\n t\n C12A7328-F81F-11D2-BA4B-00A0C93EC93B\n w\n' | fdisk boot.img # BIOS/UEFI (MBR/GPT) hybrid boot img echo -ne 'g\n n\n\n\n+1M\n t\n 21686148-6449-6E6F-744E-656564454649\n n\n\n\n\n t\n\n C12A7328-F81F-11D2-BA4B-00A0C93EC93B\n w\n' | fdisk boot.img losetup -P loop7788 boot.img mkfs.fat -F32 /dev/loop7788p2 mkdir loop mount /dev/loop7788p2 /tmp/work/loop # grub, i386-pc grub-install --target=i386-pc --root-directory=/tmp/work/loop /dev/loop7788 cp -avH rootfs/efi/ubuntu/grub.cfg loop/boot/grub/ # grub, x86_64-efi cp -avrH rootfs/* loop/ && sync && umount loop && losetup -d /dev/loop7788 |
2.6 qemu 启动测试
1 2 3 4 |
# BIOS: qemu-system-x86_64 -enable-kvm -smp 2 -m 1024M -nic user,model=virtio -hda boot.img # UEFI: qemu-system-x86_64 -enable-kvm -smp 2 -m 1024M -nic user,model=virtio -bios /usr/share/qemu/OVMF.fd -hda boot.img |