计算机加电后,首先会执行刷在ROM/NVRAM中的系统固件代码。系统固件(BIOS/UEFI)完成自身的一系列工作(如硬件自检(POST: Power-On Self-Test)后,需要引导操作系统启动。固件可以从NVRAM中读取启动设备列表,按设备顺序尝试进行引导。
BIOS
对于磁盘介质来说,BIOS会尝试读取启动设备的第一个扇区(512字节)。如果这个扇区的最后两个字节是0x55和0xAA, 则表明该设备可以用于引导,否则继续尝试列表中的下个设备。这个扇区叫做主引导记录(MBR: Master Boot Record)。这512字节分为三部分:
- 可执行代码, 446字节
- 分区表,每个表项16字节,共64字节
- Magic number: 0x55和0xAA。
分区表项结构如下:
| BI | Starting CHS | PT | Ending CHS | Starting Sector | Partition Size |
|---|
- BI: Boot Indicator, 1字节,如果是80,标记该分区是否含有操作系统启动代码
- Starting CHS: 3字节,指示分区起始位置,用柱片(Cylinder), 磁头(Head), 扇区(Sector)表示
- Partition Type Descriptor: 1字节,文件系统类型,如,
0x0B表示FAT32,0x07表示NTFS - Ending CHS: 3字节,指示分区结尾位置
- Starting Sectro: 4字节,以
逻辑区块地址(LBA: Logical Block Address)表示的起始扇区 - Partition Size: 4字节,表示分区大小
MBR的代码空间只有446字节,主要完成从分区表中查找可引导分区,并加载分区中的扇区并执行(可以直接加载内核,也可以链式加载分阶段的bootloader),完成操作系统的加载。
UEFI
UEFI: United Extensible Fireware Interface的启动流程, 不依赖BIOS模式下固定的启动扇区(boot sector),而是由实现在固件中的boot manager来寻找相应的启动项。一个设备要想支持从UEFI启动,需要具备ESP: EFI system partition,用来存储UEFI固件需要读取和加载的文件。它支持MBR: Mater Boot Record, GPT: GUID Partition Table和光盘上的El Torito卷。而在ESP上,UEFI支持的文件系统为: FAT。计算机启动时,boot manager从ESP中根据计算机架构在FAT格式文件系统中寻找目录/EFI/BOOT/下的相应的启动文件(不区分大小写)并执行它,文件名如下:
| 架构 | 文件 |
|---|---|
| x86(32位) | bootia32.efi |
| x64(64位) | bootx64.efi |
| ARM(32位) | bootarm.efi |
| ARM(64位) | bootaa64.efi |
简单来说,UEFI固件本身中已经实现文件系统功能,它从支持的文件系统中直接加载相应的启动文件。
EL Torito
对于从CD/DVD等光学介质来说,和磁盘的启动分区结构有所不同。El Torito是ISO 9660文件系统的一个扩展规范,用于支持从CD/DVD等光盘介质引导计算机。BIOS固件会根据El Torito规格,从光盘中的引导扇区寻找引导机器码。UEFI支持ISO 9660文件系统及扩展EL Torito, 会从中寻找ESP: EFI System Partition及文件系统。
了解了相应原理后,下面来从头制作一个支持从BIOS和UEFI两种固件来启动Linux内核的ISO。
因为CentOS的ISO分别使用isolinux和grub两种bootloader,我们也使用它们来进行实验。
isolinux的官网:
GRUB的官网:
实验
首先,创建工作目录demoiso:
1 | mkdir -p demoiso |
BIOS的启动,我们使用isolinux。isolinux实现了对ISO9660和EL Torito规范的支持。
从官网下载syslinux并解压:
1 | wget https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz |
将以下文件copy到isolinux:
syslinux-6.03/bios/core/isolinux.binsyslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32syslinux-6.03/bios/com32/libutil/libutil.c32syslinux-6.03/bios/com32/lib/libcom32.c32syslinux-6.03/bios/com32/menu/vesamenu.c32
我们可以自己编译内核和制作initrd,为了简单,我直接使用Slackware官方编译好的64位内核和initrd。
在isolinux目录下载内核和initrd:
1 | wget --no-check-certificate -O vmlinuz https://mirrors.kernel.org/slackware/slackware64-current/kernels/huge.s/bzImage |
在isolinux下创建isolinux.cfg,内容如下:
1 | default vesamenu.c32 |
文件准备完毕,开始制作ISO。在demoiso目录下执行:
1 | xorriso -as mkisofs -o ../demoiso.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table ./ |
我们可以使用dumpet命令来查看ISO文件的启动信息:
1 | [root@dev01v ~]# dumpet -i ../demoiso-bios.iso |
可以看到存在一条启动记录。
在BIOS固件虚拟机上启动界面如图:
启动后成功进入Slackware Linux,如图:
接下来,处理UEFI支持。在demoiso目录下创建目录EFI/BOOT:
1 | mkdir -p EFI/BOOT/ |
在EFI/BOOT目录下创建文件grub.cfg,内容如下:
1 | set default="0" |
在demoiso目录下执行命令,生成BOOTX64.EFI文件:
1 | grub2-mkstandalone -o EFI/BOOT/BOOTX64.EFI -O x86_64-efi "boot/grub/grub.cfg=EFI/BOOT/grub.cfg" |
在demoiso目录下创建目录images:
1 | mkdir images |
查看EFI目录大小:
1 | [root@dev03v demoiso]# du -sh EFI/ |
我们制作一个能包含下这个大小的UEFI能够识别的ESP分区镜像, 并将文件BOOTX64.efi复制到镜像中相应位置:
1 | dd if=/dev/zero of=images/efiboot.img bs=1M count=12 |
接下来生成BIOS和UEFI都支持的ISO文件:
1 | xorriso -as mkisofs -o ../demoiso-both.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e images/efiboot.img -no-emul-boot ./ |
使用dumpet查看生成的ISO文件:
1 | [root@dev03v demoiso]# dumpet -i ../demoiso-both.iso |
可以看到有两个启动条目了。
使用UEFI固件的虚拟机启动后,界面如下:
内核启动后可以从sysfs中读取到UEFI相关信息:
实际上,因为ESP分区是从efiboot.img来生成的,EFI目录可以忽略掉,为了令生成的ISO文件更小,可以排除EFI目录:
1 | xorriso -as mkisofs -o ../demoiso-both.iso -x EFI/ -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e images/efiboot.img -no-emul-boot ./ |
参考
https://oofhours.com/2021/12/25/how-does-uefi-boot-from-a-cd/
https://uefi.org/specs/UEFI/2.10/13_Protocols_Media_Access.html
https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/
https://pdos.csail.mit.edu/6.828/2017/readings/boot-cdrom.pdf
https://www.askpure.com/course_BJV4RJHV-WUG54YUW-1XTLSH5H-AXDT2X6I.html
https://zdyxry.github.io/2019/12/01/Linux-%E5%BC%95%E5%AF%BC%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF/
https://fedoraproject.org/wiki/Anaconda/Kickstart/KickstartingFedoraLiveInstallation
https://xuanxuanblingbling.github.io/ctf/pwn/2020/03/10/bios/
https://www.techtarget.com/whatis/definition/Unified-Extensible-Firmware-Interface-UEFI
https://www.linuxdashen.com/linux%E7%94%A8%E6%88%B7%E7%9A%84uefi%E5%9B%BA%E4%BB%B6%E6%8C%87%E5%8D%97
https://www.freedesktop.org/wiki/Software/systemd/systemd-boot/