Linux + Windows 10 多系统安装 U 盘

Update: Windows 的引导程序似乎有些问题,如果在同一块 U 盘上写入多个 ISO 分区的话,似乎引导会错乱,最终启动的安装程序版本不是引导程序所在分区的版本。所以暂时一个 U 盘还是只能放一个 Windows 版本。垃圾巨硬。

日常折腾中总免不了要用 LiveCD 修理一下系统,或者是重装一下 Windows 之类的。这时候制作一个引导用的 U 盘基本是最方便的选项了。有不少工具都能创建 U 盘引导,比如 ArchLinux 的 ISO 镜像可以直接用dd写入,Windows 的安装盘也能用 Rufus 创建。不过在使用上还是有些不便,比如dd会覆盖整个U盘,在ISO之外不能再存储其他文件。Rufus 只能在 Windows 上运行,而且一只 U 盘也只能放一份 ISO。于是尝试搞明白怎么把 Linux 的 LiveCD 和 Windows 的安装 ISO 写入到同一只 U 盘就很有必要了。

我个人使用的设备都支持 UEFI,所以这里制作的启动盘也只支持 UEFI 启动,需要 MBR 模式启动的读者请往它处寻。当然,Secure Boot 是要关掉的。制作过程我使用 Linux,纯 Windows 用户现在也可以退出了。基本上,我们需要创建一个 EFI 系统分区(EFI System Partition, ESP),其中包含基本的引导程序(Grub2)和 Linux LiveCD 的 ISO 文件。由于 Windows 的安装程序无法以 ISO 形式被引导,因此我们需要给每个 Windows ISO 文件创建一个分区,并将 ISO 中的内容解压进去。但是分区一旦创建不像文件那么好修改,所以创建每个 Windows ISO 分区的时候我都留了一些额外空间,以备以后 ISO 大小变化,这也意味着这些空间就基本浪费了。That's sad but I guess it's how things work.

另外,购买一个优质的 U 盘还是有必要的,不然不管是创建启动盘还是安装系统都会慢得让你痛不欲生。建议用之前先给 U 盘测一下速,什么拷贝速度只有 2MB/s 的金士顿可以直接进垃圾桶了。至于 U 盘大小取决于你要放多少个 ISO 文件和多少个 Windows 分区,一般 Linux 镜像大小在 500MB~3GB 的都有,Windows 10 的分区一般每个需要 5~6GB.

先给 U 盘分区,用fdisk或者别的什么工具都行。我用的 32GB 的 U 盘,分了 6GB 给 ESP,然后是另外两个 6GB 的分区给 Windows 10 的安装程序。剩下空间留着给以后使用。

然后格式化,ESP 需要 FAT32,Windows 分区用 NTFS 即可。记得多次检查盘符,不然格式化错盘就不好玩了。我给 NTFS 设置的卷标和 ISO 的一致,可以用file <img.iso>看到,不过我不确定这是不是必须的。

sudo mkfs.fat -F 32 /dev/sdXY
sudo mkfs.ntfs -f -L CPBA_X64FRE_ZH-CN_DV9 /dev/sdXY

然后把 GRUB2 安装到 ESP 分区上,假设你的 ESP 分区挂载在了$esp:

sudo grub-install --target=x86_64-efi --removable --boot-directory=$esp/boot --efi-directory=$esp

接着复制 ISO 文件到 U 盘。Linux 的 ISO 可以直接放在 ESP 分区里的任意位置,我放在了$esp/boot/iso/。Windows ISO 需要用 7z 之类的工具解压到 NTFS 分区里:

7z x cn_windows_10_business_editions_version_1909_updated_dec_2019_x64_dvd_262ac8af.iso -o'/run/media/recursiveg/CPBA_X64FRE_ZH-CN_DV9'

最后需要手工编写$esp/boot/grub/grub.cfg文件。我从我系统的配置里复制了一部分图形初始化的指令,然后写了用来引导 LiveCD 和 Windows 的菜单项。一般每个发行版的启动命令都会不一样,需要自己查询。Windows 启动项中search--label参数需要和格式化时设置的一样,当然你也可以用 UUID 等别的标识符。我还另外拷贝了一个 UEFI Shell 到 U 盘里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# copied from arch boot config
function load_video {
if [ x$feature_all_video_module = xy ]; then
insmod all_video
else
insmod efi_gop
insmod efi_uga
insmod ieee1275_fb
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
fi
}

if loadfont unicode ; then
set gfxmode=auto
load_video
insmod gfxterm
set locale_dir=$prefix/locale
#set lang=zh_CN
insmod gettext
fi

insmod part_gpt
insmod search_fs_uuid

# path to the partition holding ISO images (using UUID)
probe -u $root --set=rootuuid
set imgdevpath="/dev/disk/by-uuid/$rootuuid"

menuentry 'archlinux-2020.10.01-x86_64.iso' {
set isofile='/boot/iso/archlinux-2020.10.01-x86_64.iso'
echo 'Mounting archlinux-2020.10.01-x86_64.iso ...'
loopback loop $isofile
linux (loop)/arch/boot/x86_64/vmlinuz-linux img_dev=$imgdevpath img_loop=$isofile earlymodules=loop
initrd (loop)/arch/boot/intel-ucode.img (loop)/arch/boot/amd-ucode.img (loop)/arch/boot/x86_64/initramfs-linux.img
}

menuentry "ubuntu-20.04.1-desktop-amd64.iso" {
set isofile="/boot/iso/ubuntu-20.04.1-desktop-amd64.iso"
echo 'Mounting ubuntu-20.04.1-desktop-amd64.iso ...'
loopback loop $isofile
linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject
initrd (loop)/casper/initrd
}

menuentry "Windows 10 Installer (CN Business editions 1909 Dec 2019)" {
insmod ntfs
search --no-floppy --set=root --label CPBA_X64FRE_ZH-CN_DV9
chainloader /efi/boot/bootx64.efi
echo "Booting windows ..."
}

menuentry "Windows 10 Installer (CN Oct 2020)" {
insmod ntfs
search --no-floppy --set=root --label CCCOMA_X64FRE_ZH-CN_DV9
chainloader /efi/boot/bootx64.efi
echo "Booting windows ..."
}

menuentry "shellx64_v2.efi" {
chainloader /boot/shellx64_v2.efi
}

menuentry "Enter BIOS setup" {
fwsetup
}

全部设置好以后可以用虚拟机测试一下是不是所有项目都能正常启动,如果都没有问题就 OK 了。