计算机加电后,首先会执行刷在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.bin
syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32
syslinux-6.03/bios/com32/libutil/libutil.c32
syslinux-6.03/bios/com32/lib/libcom32.c32
syslinux-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/