计算机加电后,首先会执行刷在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),完成操作系统的加载。
UEFIUEFI: 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的官网:
https://wiki.syslinux.org/
GRUB的官网:
https://www.gnu.org/software/grub/
实验首先,创建工作目录demoiso:
1mkdir -p demoiso
BIOS的启动,我们使用isolinux。isolinux实现了对ISO9660和EL Torito规范的支持。
从官网下载syslinux并解压:
1wget 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:
12wget --no-check-certificate -O vmlinuz https://mirrors.kernel.org/slackware/slackware64-current/kernels/huge.s/bzImagewget --no-check-certificate -O initrd.img https://mirrors.kernel.org/slackware/slackware64-current/isolinux/initrd.img
在isolinux下创建isolinux.cfg,内容如下:
12345678910111213141516default vesamenu.c32timeout 600MENU clearMENU title DEMO ISOMENU vshift 2MENU rows 10MENU margin 8MENU helpmsgrow 19MENU tabmsgrow 15MENU autoboot Booting default in #sLABEL DEMO menu label ^Slackware linux KERNEL vmlinuz INITRD initrd.img
文件准备完毕,开始制作ISO。在demoiso目录下执行:
1xorriso -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文件的启动信息:
1234567891011121314[root@dev01v ~]# dumpet -i ../demoiso-bios.isoValidation Entry: Header Indicator: 0x01 (Validation Entry) PlatformId: 0x00 (80x86) ID: "" Checksum: 0x55aa Key bytes: 0x55aaBoot Catalog Default Entry: Entry is bootable Boot Media emulation type: no emulation Media load segment: 0x0 (0000:7c00) System type: 0 (0x00) Load Sectors: 4 (0x0004) Load LBA: 34 (0x00000022)
可以看到存在一条启动记录。
在BIOS固件虚拟机上启动界面如图:
启动后成功进入Slackware Linux,如图:
接下来,处理UEFI支持。在demoiso目录下创建目录EFI/BOOT:
1mkdir -p EFI/BOOT/
在EFI/BOOT目录下创建文件grub.cfg,内容如下:
123456789101112131415161718192021set default="0"set timeout=60set root=(cd0)menuentry 'Slackware Linux' { echo 'Loading Slackware kernel ...' linuxefi /isolinux/vmlinuz initrdefi /isolinux/initrd.img}menuentry 'Reboot' { echo 'System rebooting ...' reboot}menuentry 'Halt' { echo 'Halting system ...' halt}
在demoiso目录下执行命令,生成BOOTX64.EFI文件:
1grub2-mkstandalone -o EFI/BOOT/BOOTX64.EFI -O x86_64-efi "boot/grub/grub.cfg=EFI/BOOT/grub.cfg"
在demoiso目录下创建目录images:
1mkdir images
查看EFI目录大小:
12[root@dev03v demoiso]# du -sh EFI/8.4M EFI/
我们制作一个能包含下这个大小的UEFI能够识别的ESP分区镜像, 并将文件BOOTX64.efi复制到镜像中相应位置:
1234dd if=/dev/zero of=images/efiboot.img bs=1M count=12mkfs.vfat images/efiboot.imgmmd -i images/efiboot.img EFI EFI/BOOTmcopy -i images/efiboot.img EFI/BOOT/BOOTX64.EFI ::EFI/BOOT/
接下来生成BIOS和UEFI都支持的ISO文件:
1xorriso -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文件:
1234567891011121314151617181920212223242526[root@dev03v demoiso]# dumpet -i ../demoiso-both.isoValidation Entry: Header Indicator: 0x01 (Validation Entry) PlatformId: 0x00 (80x86) ID: "" Checksum: 0x55aa Key bytes: 0x55aaBoot Catalog Default Entry: Entry is bootable Boot Media emulation type: no emulation Media load segment: 0x0 (0000:7c00) System type: 0 (0x00) Load Sectors: 4 (0x0004) Load LBA: 6178 (0x00001822)Section Header Entry: Header Indicator: 0x91 (Final Section Header Entry) PlatformId: 0xef (EFI) Section Entries: 1 ID: ""Boot Catalog Section Entry: Entry is bootable Boot Media emulation type: no emulation Media load address: 0 (0x0000) System type: 0 (0x00) Load Sectors: 24576 (0x6000) Load LBA: 34 (0x00000022)
可以看到有两个启动条目了。
使用UEFI固件的虚拟机启动后,界面如下:
内核启动后可以从sysfs中读取到UEFI相关信息:
实际上,因为ESP分区是从efiboot.img来生成的,EFI目录可以忽略掉,为了令生成的ISO文件更小,可以排除EFI目录:
1xorriso -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://en.wikipedia.org/wiki/UEFI
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://oofhours.com/2019/09/02/geeking-out-with-uefi/
https://blog.cuicc.com/blog/2011/11/15/mbr-and-boot-loader/
https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/
https://www.zengl.com/a/201406/160.html
https://fossies.org/linux/xorriso/doc/boot_sectors.txt
https://brinnatt.com/primary/%E7%AC%AC-14-%E7%AB%A0-%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8Buefisystemd/
https://communities.vmware.com/t5/VMware-Workstation-Pro/UEFI-workstation-firmware-and-badly-written-by-me-el-torito/td-p/1314187
https://www.makeuseof.com/types-of-syslinux-bootloaders/
https://www.cnblogs.com/aWxvdmVseXc0/p/15553891.html
https://blog.csdn.net/charleyhuman/article/details/628472
https://pdos.csail.mit.edu/6.828/2017/readings/boot-cdrom.pdf
https://www.cnblogs.com/zszmhd/p/3595787.html
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://zhuanlan.zhihu.com/p/47926853
https://github.com/ivandavidov/systemd-boot
https://tinylab.org/create-iso-for-i386-pc-board/
https://edoceo.com/sys/syslinux-vesa
https://wiki.osdev.org/El-Torito
https://wiki.syslinux.org/wiki/index.php?title=Boot_Stages
https://github.com/xcp-ng/xcp/tree/master/iso/7.4
https://access.redhat.com/solutions/4842101
https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/html/anaconda_customization_guide/sect-boot-menu-customization
https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/pdf/anaconda_customization_guide/red_hat_enterprise_linux-7-anaconda_customization_guide-en-us.pdf
https://access.redhat.com/solutions/5711111
https://www.cnblogs.com/lpfuture/p/5703555.html
https://www.cnblogs.com/lpfuture/p/5704416.html
https://fedoraproject.org/wiki/Anaconda/Kickstart/KickstartingFedoraLiveInstallation
https://fedoraproject.org/wiki/LiveOS_image
https://access.redhat.com/solutions/3417041
https://github.com/syzdek/efibootiso
http://www.panticz.de/create-uefi-iso
https://www.ruanyifeng.com/blog/2013/02/booting.html
https://www.ruanyifeng.com/blog/2015/09/0x7c00.html
https://xuanxuanblingbling.github.io/ctf/pwn/2020/03/10/bios/
https://zhuanlan.zhihu.com/p/81960137
https://www.bilibili.com/read/cv20851965/
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/
https://github.com/ivandavidov/systemd-boot
http://x86asm.net/articles/uefi-programming-first-steps/