海上的宫殿 - uefi
type: channel
link: https://soha.moe/tag/uefi/
items: 构建 UEFI 启动的 OpenWrt 磁盘映像

构建 UEFI 启动的 OpenWrt 磁盘映像
type: item
date: 2021-02-22T15:09:00+08:00
description: 请注意，本文中的方法已经不再被建议使用。因为在 2021 年 9 月 5 日，随着 OpenWrt 21.02 的释出，官方已经正式开始提供支持 EFI 的镜像，只需要下载带 -efi 后缀的即可，如 generic-ext4-combined-efi.img.gz。 可惜这篇文章只活了半年就结束了它的使命（主要还是我咕，不然早就可以出现了）。 在 Hyper-V 上第一代和第二代虚拟机有着一些差别，最显著的就是旧式 BIOS 启动和 UEFI 启动的区别，但还有其他的例如热插拔硬盘和网卡的区别。我一直以为是 OpenWrt 对 UEFI 启动支持不良，官方才一直没有推出 EFI 的磁盘映像的。因此，之前我的做法都是创建好一个 OpenWrt 虚拟机后都一次性加一堆网卡，以解决临时要加一个口的问题。昨天准备给家里的 OpenWrt 升级版本，就又想到了这个事情，就想试一试如果用 UEFI 启动能不能正常使用。 OpenWrt x86 的启动本身就是 GRUB 了，按理说如果内核支持，那么只要整一个 ESP 分区，然后整个 EFI 版本的 GRUB 进去，grub.cfg 复制过来以后按照老样子启动就可以了。试了之后，确实就这么简单就能启动并正常使用了，暂时也没发现什么问题，就让它冒烟测试吧。因为在调研的时候似乎没看到介绍这么做的教程，我就来水一篇博客了。 为了方便使用 GRUB 的工具，本文所有操作均在 Debian bullseye 上完成。那么接下来我将介绍如何构建同时支持 MBR 启动和 UEFI 启动的 OpenWrt 磁盘映像。 因为我也担心遇到 UEFI 下的问题，为了能够回滚的考虑，就选择了构建一个支持 MBR 和 UEFI 启动的版本，可以在保证旧式 MBR 启动的模式兼容的前提下，支持 UEFI 启动。注意，你同时需要 MBR 和 EFI 两种 GRUB 的文件，Debian 中对应的是 grub-efi-amd64-bin 和 grub-pc-bin 两个包。 首先先从 https://downloads.openwrt.org/ 上下载得到最新的磁盘映像，目前为 openwrt-19.07.7-x86-64-combined-ext4.img.gz，并解压得到磁盘映像。 接着使用 losetup -f，获得一个可以让我们挂载磁盘映像的 loop 设备，并用 losetup -P /dev/loop0 openwrt-19.07.7-x86-64-combined-ext4.img 将磁盘映像关联到这个 loop 设备。 然后我们将 boot 分区（即第一个分区）挂载到一个文件夹，直接把里面的 boot 分区里面的 boot 文件夹复制出来，之后我们会将它塞回新的 ESP 分区。 复制出来以后，先用 umount 卸载，然后使用 fdisk /dev/loop0，将第一个原来的启动分区更改类型为 EFI。先输入“t”（修改分区类型），然后输入“1”（第一个分区），然后输入“uefi”或者“ef”，再使用“w”将改动写入分区表。 ESP 分区本质是一个 FAT12/16/32 的分区，而官方磁盘映像中第一个分区仅 16M，因此我们需要使用 mkfs.fat -n EFI -F16 /dev/loop0p1 将第一个分区格式化为 FAT16。 改造好 ESP 分区后，重新将这个分区挂载上，并将之前复制出来的 boot 文件夹，复制回这个分区，并建立 EFI/BOOT（大小写不敏感）文件夹，还要将 configfile ${cmdpath}/../../boot/grub/grub.cfg 这句话写入 image.cfg 文件中。 这些准备就绪后，就是创建 GRUB 的 EFI 程序了。使用 grub-mkstandalone -d /usr/lib/grub/x86_64-efi/ -O x86_64-efi --locales="" --themes="" --fonts="" --modules="part_msdos" -o esp/EFI/BOOT/BOOTX64.EFI "boot/grub/grub.cfg=image.cfg" 这句话就可以生成 GRUB 的 EFI 文件了。 因为 EFI 版本的 GRUB 中，会先将 GRUB 所用到的文件，例如 modules、locales、themes 之类的文件塞到 EFI 程序里，在运行的时候将其挂载为一个内存盘，所以第一时间使用的 grub.cfg 也来自这个内存盘。这里最后一个参数的意思是将“image.cfg”这个文件作为这个内存盘里面的 grub.cfg 来写入。而在 image.cfg 中，我们使用了“configfile”再次将配置文件指向…