让UEFI支持btrfs
装系统的时候如果要efi启动的话,启动程序文件要放在ESP分区里,通常是FAT格式的(当然大部分电脑也支持NTFS格式的)所以如果系统分区不是这个格式的话还要专门划一个esp分区来存放EFI启动文件。显然我们Linux玩家就必须要再开一个esp分区,那么有没有办法把我们Linux的系统分区也直接作为ESP分区,一个分区搞定呢?
本文弄的是Btrfs,其它格式同理。
本文在Qemu虚拟机中折腾,使用Edk2 OVMF UEFI固件。对于真正电脑,只要胆子够大也行(手持烧录器我不怕)
我搜了一下,UEFI类似于一个操作系统,也有文件系统驱动,也能操作文件,并且Linux的引导程序对于UEFI来说就是“可执行程序”。只要UEFI能识别的都能作为ESP分区
而rEFInd就有好多文件系统的驱动,去sourceforge下载refind-bin的那个就行了,找到drivers_x64文件夹里面就是文件系统驱动了
那么以下是研究过程
qemu 启动
#!/bin/env zsh@() { __+=" $@" ; }@ qemu-system-x86_64@ -m 16G -object memory-backend-file,id=mem,size=16G,mem-path=/dev/shm,share=on -numa node,memdev=mem@ -smp cpus=8,sockets=1,cores=8,threads=1@ -bios /usr/share/edk2/x64/OVMF.4m.fd@ -cpu host@ -machine q35@ -enable-kvm@ -netdev tap,id=netwan,ifname=qemu-tap-test,script=no,downscript=no -device virtio-net,netdev=netwan,mac=52:55:57:00:00:00@ -drive file=.qcow2,format=qcow2@ -boot menu=on@ -serial stdio# 为了方便测试,这里把这个virtiofs文件夹通过virtiofs共享给虚拟机当ESPmkdir -p virtiofs/usr/lib/virtiofsd --socket-path=virtiofs.socket -o source=virtiofs -o cache=always &@ -chardev socket,id=char1,path=virtiofs.socket -device vhost-user-fs-pci,queue-size=1024,chardev=char1,tag=virtiofsexec eval $__ $@
那么就先加载它的驱动试试能不能用呗
方法一:rEFInd的使命:加载UEFI驱动,然后把vmlinuz当EFI启动文件带参数启动。然后就是vmlinuz自己加载initrd自己启动了。
方法二:手动用EFI Internal Shell加载驱动
windows有cmd,linux下有bash,zsh,UEFI下也有个EFI Internal Shell的程序

UEFI Interactive Shell v2.2EDK IIUEFI v2.70 (EDK II, 0x00010000)Mapping table FS0: Alias(s):F1: PciRoot(0x0)/Pci(0x3,0x0) BLK0: Alias(s): PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0) BLK1: Alias(s): PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)Press ESC in 1 seconds to skip startup.nsh or any other key to continue.Shell>mode调整分辨率
Shell> modeAvailable modes for console output device. Col 80 Row 25 Col 100 Row 31 *Shell> mode 100 31map看盘符(不过这盘符不是C盘D盘而是FS0)
Shell> mapMapping table FS0: Alias(s):F1: PciRoot(0x0)/Pci(0x3,0x0) BLK0: Alias(s): PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0) BLK1: Alias(s): PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)dir或ls命令能看到里面的文件
Shell> ls fs0:Directory of: fs0:\01/01/1970 00:00 <DIR> r 0 .01/01/1970 00:00 62,840 btrfs_x64.efi01/01/1970 00:00 12,477 NvVars 2 File(s) 75,317 bytes 1 Dir(s)load加载驱动
Shell> load fs0:\btrfs_x64.efiImage 'FS0:\btrfs_x64.efi' loaded at 7D7F1000 - Successdrivers查看加载的驱动列表
Shell>
drivers
DRV VERSION TYPE CFG DING #D #C DRIVER NAME IMAGE NAME 5E 0000000A B - - 1 7 PCI Bus Driver PciBusDxe 60 00000010 ? - - - - Virtio PCI Driver VirtioPciDeviceDxe 61 00000010 D - - 1 - Virtio 1.0 PCI Driver Virtio10 62 00000010 ? - - - - Virtio Block Driver VirtioBlkDxe 63 00000010 ? - - - - Virtio SCSI Host Driver VirtioScsiDxe 64 00000010 ? - - - - Virtio Serial Driver VirtioSerialDxe 65 0000000A D - - 2 - Platform Console Management Driver ConPlatformDxe 66 0000000A D - - 2 - Platform Console Management Driver ConPlatformDxe 67 0000000A B - - 2 2 Console Splitter Driver ConSplitterDxe 68 0000000A ? - - - - Console Splitter Driver ConSplitterDxe 69 0000000A ? - - - - Console Splitter Driver ConSplitterDxe 6A 0000000A B - - 2 2 Console Splitter Driver ConSplitterDxe 6B 0000000A B - - 1 1 Console Splitter Driver ConSplitterDxe 6F 0000000A D - - 1 - Graphics Console Driver GraphicsConsoleDxe 70 0000000A B - - 1 1 Serial Terminal Driver TerminalDxe 71 0000000A D - - 2 - Generic Disk I/O Driver DiskIoDxe 72 0000000B ? - - - - Partition Driver(MBR/GPT/El Torito) PartitionDxe 75 0000000A B - - 1 1 SCSI Bus Driver ScsiBus 76 0000000A D - - 1 - Scsi Disk Driver ScsiDisk 77 0000000A D - - 1 - Sata Controller Init Driver SataController 78 00000010 D - - 1 - AtaAtapiPassThru Driver AtaAtapiPassThruDxe 79 00000010 B - - 1 1 ATA Bus Driver AtaBusDxe 7A 00000010 ? - - - - NVM Express Driver NvmExpressDxe 7B 00000010 B - - 1 3 OVMF Sio Bus Driver SioBusDxe 7C 0000000A B - - 1 1 PCI SIO Serial Driver PciSioSerialDxe 7D 0000000A D - - 1 - PS/2 Keyboard Driver Ps2KeyboardDxe 80 0000000A ? - - - - FAT File System Driver Fat 81 00000010 ? - - - - UDF File System Driver UdfDxe 82 00000010 D - - 1 - Virtio Filesystem Driver VirtioFsDxe 85 0000000A ? - - - - Simple Network Protocol Driver SnpDxe 86 0000000A B - - 1 1 VLAN Configuration Driver VlanConfigDxe 87 0000000A B - - 1 3 MNP Network Service Driver MnpDxe 88 0000000A B - - 1 1 ARP Network Service Driver ArpDxe 89 0000000A B - - 1 2 DHCP Protocol Driver Dhcp4Dxe 8A 0000000A B - - 2 12 IP4 Network Service Driver Ip4Dxe 8B 0000000A B - - 7 12 UDP Network Service Driver Udp4Dxe 8C 0000000A B - - 2 2 MTFTP4 Network Service Mtftp4Dxe 8D 0000000A B - - 1 2 DHCP6 Protocol Driver Dhcp6Dxe 8E 0000000A B - - 2 12 IP6 Network Service Driver Ip6Dxe 8F 0000000A B - - 6 10 UDP6 Network Service Driver Udp6Dxe 90 0000000A B - - 1 1 MTFTP6 Network Service Driver Mtftp6Dxe 91 0000000A B - - 8 1 UEFI PXE Base Code Driver UefiPxeBcDxe 92 0000000A B - - 7 1 UEFI PXE Base Code Driver UefiPxeBcDxe 95 00000000 D - - 1 - DNS Network Service Driver DnsDxe 96 00000000 D - - 1 - DNS Network Service Driver DnsDxe 97 0000000A D - - 1 - HttpDxe HttpDxe 98 0000000A D - - 1 - HttpDxe HttpDxe 99 0000000A B - - 2 1 UEFI HTTP Boot Driver HttpBootDxe 9A 0000000A B - - 3 1 UEFI HTTP Boot Driver HttpBootDxe 9B 0000000A D - - 1 - iSCSI Driver IScsiDxe 9C 0000000A D - - 1 - iSCSI Driver IScsiDxe 9E 00000010 ? - - - - Virtio Network Driver VirtioNetDxe 9F 00000020 ? - - - - Usb Uhci Driver UhciDxe A0 00000030 ? - - - - Usb Ehci Driver EhciDxe A1 00000030 ? - - - - Usb Xhci Driver XhciDxe A2 0000000A ? - - - - Usb Bus Driver UsbBusDxe A3 0000000A ? - - - - Usb Keyboard Driver UsbKbDxe A4 00000011 ? - - - - Usb Mass Storage Driver UsbMassStorageDxe A5 00000010 B - - 1 1 QEMU Video Driver QemuVideoDxe A6 00000010 ? - - - - Virtio GPU Driver VirtioGpuDxe A7 00000010 ? - - - - Virtio Random Number Generator Driv VirtioRngDxe A8 0000000A B - - 3 4 TCP Network Service Driver TcpDxe A9 0000000A B - - 3 4 TCP Network Service Driver TcpDxe B5 017D5C56 B - - 2 2 1af41000.efidrv Offset(0x10E00,0x273FF) FF 00000010 D - - 1 - rEFInd 0.14.2 btrfs File System Dri \btrfs_x64.efi
map -r重新分配盘符(不过注意是重新分配:假如原来FS0是一个分区,map -r之后FS0可能是另一个分区)
Shell> map -rMapping table FS1: Alias(s):F1: PciRoot(0x0)/Pci(0x3,0x0) FS0: Alias(s):F0a65535a:;BLK0: PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0) BLK1: Alias(s): PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF可以看到文件系统被识别了 (当然盘符也重新分配了)
那么启动ArchLinux的vmlinuz试一试呢 (是的你没看错,vmlinuz-linux也是一个EFI启动文件,它可以自己加载initrd,不过要传入参数。)
Shell> fs0:FS0:\> boot\vmlinuz-linux initrd=boot\initramfs-linux-fallback.img root=UUID=59004200-5900-3100-3300-300036000000 rw splash启动成功了

方法三:edk2 ovmf的设置里可以设置自动加载驱动哦

既然开机会自动识别btrfs文件系统,那么把btrfs上的EFI启动程序添加到启动列表呢
可以从设置里添加启动项

当然也可以进linux后运行 efibootmgr 添加启动项
efibootmgr -c -d /dev/sda -L 'Arch Linux' -l '/boot/vmlinuz-linux' --unicode "initrd=/boot/initramfs-linux-fallback.img root=UUID=59004200-5900-3100-3300-300036000000 rw splash" # -p 1
-c代表创建-d后面是硬盘-p是分区数,如果没有分区就没有p参数-L名称-l文件路径(对于分区而言),会自动转换斜杠--unicode给EFI启动程序的参数
嘿嘿,发现这么弄确实可以启动诶~

现在可以把启动文件放Btrfs上了,但是前提是要把Btrfs驱动放ESP分区上,那不还是要ESP分区吗
那么为什么FAT文件系统就可以直接识别呢?应该就是它固件里内置了FAT驱动。那么想办法把Btrfs驱动也给内置一下。
把驱动内置
以前我看过别人的教程改过开机LOGO,所以我用过用UEFITool,所以现在自然想起UEFITool啦~
对了,GitHub LongSoft UEFITool Releases里面的Axx的版本好像只能看不能改,请下载0.28.0版
用UEFITool打开固件,Ctrl+F果然发现内嵌了FAT的驱动

那么怎么把btrfs驱动也内嵌进入呢?
先研究一会,然后我发现大概是这个结构(实在不知道该怎么说这个)
一个大东西.ffs{ 类型:驱动, XXX:XXX, body:[ 备注驱动程序名.sct{size:XXX,body:[name.text.hex]}, 备注驱动版本名.sct{size:XXX,body:[version.text.hex]}, 驱动程序.sct{size:XXX,body:[DXE.efi]}, 其他的一些小东西.sct{size:XXX,body:[xxx.bin]}, ]}
- Type显示为File的就是大东西
- Type显示为Section为小东西

其中这些是搞这一整个“东西”的
- Extract as is… Ctrl+E
- Insert into… Ctrl+l
- Insert before… Ctrl+Alt+|
- Insert after… Ctrl+Shift+|
- Replace as is… Ctrl+R
- Remove Ctrl+Del
其中这些是操作”body”
- Extract body… Ctrl+Shift+E
- Replace body… Ctrl+Shift+R
那就把btrfs驱动也嵌入呗
-
先把一整个“大东西”导出,用16进制编辑器改个GUID(前面的字节),再找个地方把编辑后的“大东西”导入进来

-
把 PE32 image section 这“小东西”的body替换成btrfs驱动(btrfs_x64.efi)

-
User interface section对应右边的Text,还有Version section,好像是给自己看的(嗯好像drivers也会显示,不过那也是给自己看的嘛~),好像改不改都行,(甚至直接删掉也行),也可以导出body用16进制编辑器修改再导入
- 改备注驱动程序名

- 改备注驱动版本名

-
如果导入时导入了DXE dependency section的话要移除它,否则好像不会自动加载这个驱动了,具体这个是干嘛用的我也还没去了解
-
然后保存并使用修改的固件就好啦。
现在可以把btrfs就能当esp分区啦! 作为选择困难强迫症的我终于不用再纠结于系统分区的起始扇区位置哩(btrfs创建后无法移动分区起始位置), 嘻嘻我直接不分区了哈哈!
注:
-
这个btrfs驱动是只读的,不像FAT驱动那样是可读写的。
-
如果加载多个同一种格式的文件系统驱动也只会有一个驱动接管分区。 rEFInd的btrfs驱动与quibble暂不兼容,会导致quibble无法启动btrfs上的windows。 quibble的btrfs驱动与Linux内核的UKI暂不兼容,会导致Linux内核的UKI功能无法按照initrd参数找到initrd。 所以假如两个都要用的话就别内置这个驱动了。
我在华硕和技嘉的电脑上试了,无法通过校验,无法正常刷入,需要通过BIOS FlashBack 或者用编程器刷入。
我在华硕的电脑上试了,直接开启安全启动后也能识别到btrfs分区。