
Linux Kernel EFI Boot Stub or "Your own bootloader"

Introduction
After reading a recent article Downloading Linux OS without a bootloader , I realized two things: many are interested in the “novelty” dating back to 2011; the author did not describe the most basic, without which, in fact, nothing will work in some cases. There was also another article, but either it was already outdated, or again there was a lot of unnecessary and unsaid at the same time.
Specifically, the main point was missed - the kernel assembly option CONFIG_EFI_STUB . Since in recent versions of U (lu / ku / edu / * etc *) buntu this option is already enabled by default, the author did not have any suspicions.
As far as I know, it is currently included in distributions of the above versions: Arch Linux, Fedora 17, OpenSUSE 12.2, and Ubuntu 12.10. In the comments, they also mentioned that Debian with the 2.6 kernel can, but this is nothing more than backport from the latest versions. These distributions do not need to be rebuilt at all! But on the other CONFIG_EFI_STUB, most likely, it is either absent altogether, since the option is available only from the kernel version 3.3.0 and higher, or is turned off by default. Accordingly, everything described below is true for a kernel compiled with the CONFIG_EFI_STUB option.
So what exactly is the Linux Kernel EFI Boot Stub?
general information
And nothing more than ... "exe-file"! Yes, "screw" PE / COFF . Well, to be exact, just skid it with minor modifications to please the UEFI bootloader . You can verify this by reading the first 2 bytes of the kernel:
$ od /boot/vmlinuz-linux --address-radix=x --read-bytes=2 -t x1c
0000000 4d 5a
M Z
0000002
It’s familiar, isn't it? At least for those who at least once "for fun" opened the MS-DOS or Windows executable file in notepad, a hex editor or something cool. These are the initials of Mark Zbikowski , who, in fact, developed this file format in MS-DOS. The signature of this stub is still hanging in vestiges in modern Windows executable files, devouring with its header as much as 64 bytes per file!
The DOS header falls on the legacy code, which is executed when the kernel boots as a boot sector, and swears in the manner of MS-DOS when running PE files: “Direct floppy boot is not supported. Use a boot loader program instead. Remove disk and press any key to reboot ... ". Therefore, the information from this header here is garbage, except, in fact, the signature 'MZ' and the offset address of the next header.
Move on.
The PE / COFF specification tells us that at offset 0x3c there is a 32-bit offset of the second header with the signature "PE \ 0 \ 0":
$ od /boot/vmlinuz-linux --address-radix=x --read-bytes=4 --skip-bytes=0x3c -t x4
00003c 000000b8
000040
so, the offset is 0xb8, which is true for the current stable kernel of x86_64 architecture, on x86 it will be 0xa8. We read:
$ od /boot/vmlinuz-linux --address-radix=x --read-bytes=4 --skip-bytes=0xb8 -t x1c
0000b8 50 45 00 00
P E \0 \0
0000bc
And here is the signature of the second header! As you might have guessed, this is an abbreviation for the phrase Portable Executable, with which the payload in the executable files begins.
Even the Windows boot loader did not care about half the fields of this header, and even UEFI did not need them at all, so some of them were registered statically, while the important ones are filled during kernel assembly. A lot of "unnecessary" fields, all kinds of timestamps, control sums, etc. simply remain zeros. Basically, dimensions, offsets, entry point, etc. are filled in. Therefore, we can call this PE file completely valid with a stretch. However, the classic LordPE or PETools utilities are quite content with signatures and tell everything they know about the file:


Structure
In general, everything is as in a regular PE file. Currently, the stable version 3.11.4 of the Arch Linux kernel is from repositories, it contains 3 sections: '.setup', '.reloc' and '.text'.
- The .setup section contains mainly legacy code for initialization in case of loading in compatibility mode. When loading in UEFI mode, all switching modes of the processor, initial initialization is done by the firmware.
- The .reloc section is required by the loader, therefore, when building the kernel, an empty “that is” stub is created.
- The most interesting .code section, in fact, contains EntryPoint and the main code of the rest of the kernel. After the EFI-application is found, the loader executes the LoadImage boot service, thereby loading the entire image into memory. The type of residency depends on the Subsystem field: EFI_APPLICATION will be unloaded when it works. EFI_DRIVER can be Unloadable and will be unloaded only in case of a critical error. Next, control is transferred to the entry point, usually it is the efi_main () function - an analog of main () in C.
In fact, I was a little cunning, at the beginning calling the kernel an exe file. In fact, this is a simple EFI application that uses the PE32 + format.
Primary requirements

If the motherboard has the logo “Windows 8 Ready!”, Then most likely the EFI Boot Mode is already activated by default.
In most cases, to boot the Linux kernel into EFI-mode, you must disable the Secure Boot option.
Disk layout
Many sources indicate that GPT partitioning is required , not MBR , but this is not the case. UEFI is quite capable of MBR. Another thing, for example, Windows forcibly forces a disk to break in a new way to boot into EFI mode and swears at the antiquity of Master Boot Record. And rightly so! Having marked the disk up-to-date, we will not lose anything, but only win.
Firstly, there will be no problems with all kinds of Primary / Logical partitions, “don't go there - go here” and other rudiments.
Secondly, although SolidState disks are moving ahead in large quantities, whose volumes are not very surprising, the size of an ordinary “turntable” of a few terabytes will not surprise anyone now. But under MBR you can mark up a partition with a maximum of about 2TB. GPT seeswell, there’s a lot , you can’t even name a number - disks of such sizes will not appear relatively soon.
Well, plus all sorts of bonuses, such as duplicating a GPT record at the beginning and end of a disk, integrity checksums, etc., add desire without hesitation to mark a disk for GPT.
There are a lot of articles on how to partition a disk using various utilities in GNU / Linux.
Separate section
Section Type
After nn-tat years of development of standards, engineers still decided that hardcode is not good. Now it doesn’t matter where our boot partition is located, the UEFI boot loader does it very simply: it goes through all partitions and disks in a row and looks for one special one. Its peculiarity lies in the fact that in the case of MBR markup, it has a type with the code 0xEF (as you might guess, from EFI). In the case of GPT markup, a section with a GUID equal to C12A7328-F81F-11D2-BA4B-00A0C93EC93B .
There is some implicitness here. All markup utilities, for example, parted, have the ability to set and display the “boot” flag, which applies to the partition. So, in the case of MBR, such a possibility really exists, that is, there is a real byte that indicates to the BIOS that the partition is “bootable”. This flag can be put on any partition whose MBR, we want to feed BIOS for download. But when we are dealing with the GPT, there really is no flag! By this flag, parted means just the GUID equal to the above. That is, in fact GPT boot flag = GPT EFI Partition!
parted
gdisk does not suffer from this:# parted /dev/sda -l
Модель: ATA ST3750330AS (scsi)
Диск /dev/sda: 750GB
Размер сектора (логич./физич.): 512B/512B
Таблица разделов: gpt
Disk Flags:
Номер Начало Конец Размер Файловая система Имя Флаги
1 1049kB 135MB 134MB fat32 EFI System загрузочный
2 135MB 269MB 134MB ext2 Linux filesystem
3 269MB 8859MB 8590MB linux-swap(v1) Linux swap
4 8859MB 30,3GB 21,5GB ext4 Linux filesystem
5 30,3GB 46,4GB 16,1GB ext4 Linux filesystem
6 46,4GB 67,9GB 21,5GB ext4 Linux filesystem
7 67,9GB 750GB 682GB xfs Linux filesystem
gdisk
# gdisk /dev/sda -l
GPT fdisk (gdisk) version 0.8.7
Partition table scan:
MBR: protective
BSD: not present
APM: not present
GPT: present
Found valid GPT with protective MBR; using GPT.
Disk /dev/sda: 1465149168 sectors, 698.6 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 02D11900-D331-4114-A3D7-8493969EF533
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 1465149134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)
Number Start (sector) End (sector) Size Code Name
1 2048 264191 128.0 MiB EF00 EFI System
2 264192 526335 128.0 MiB 8300 Linux filesystem
3 526336 17303551 8.0 GiB 8200 Linux swap
4 17303552 59246591 20.0 GiB 8300 Linux filesystem
5 59246592 90703871 15.0 GiB 8300 Linux filesystem
6 90703872 132646911 20.0 GiB 8300 Linux filesystem
7 132646912 1465149134 635.4 GiB 8300 Linux filesystem
Conclusion: if our EFI partition is on MBR, set the partition type to EFI Partition and boot flag. If the GPT is either an EFI Partition type or a boot flag, since they are the same thing.
There are all sorts of things, such as the GPT legacy boot flag, which is installed in Protective MBR, and so on, but all these are crutches that are used only in compatibility mode. In GPT mode, UEFI Boot should be ignored.
File system
Different sources write differently. Someone says that FAT16 can be used, someone even FAT12 recommends. But is it not better to follow the advice of the official specification? And she says that the system partition must be in FAT32. For removable-media (USB HDD, USB Flash) - also FAT12 / FAT16 in addition to FAT32.
Nothing is said about the size of the partition. However, due to the initial crutch and buggy implementations of boot loaders and firmware, experimentally, people found that in order to avoid various “surprises”, a size of at least 520MiB (546MB) is recommended . Here, with luck, there might not be a problem with a 32-megabyte partition.
Directory structure
After the loader has found its “labeled” partition and made sure that it supports the file system, it starts to perform all actions with paths relative to the root of the partition. In addition, all files in this section should be in the \ EFI \ directory , which, in turn, is the only one in the root of the section. By convention, each vendor is recommended to select a folder with a unique name and place it in \ EFI \, for example: \ EFI \ redhat \ , \ EFI \ microsoft \ , \ EFI \ archlinux \ . The vendor’s directory contains directly executable efi applications. One file per architecture is recommended. Files must have the extension .efi .
For removable devices, the directory is intended\ EFI \ BOOT \ . It also recommends no more than one file for each architecture. In addition to this, the file should be called boot {arch} .efi . For example, \ EFI \ BOOT \ bootx64.efi . Available architectures: ia32, x64, ia64, arm, aa64 .
NVRAM Access
By default, if nothing is written to non-volatile UEFI memory, \ EFI \ BOOT \ bootx64.efi will be loaded . To write the path to the necessary application in NVRAM , you can use the efibootmgr utility. Let's try to display the current entries:
# efibootmgr -v
Some distributions require the included kernel option CONFIG_EFI_VARS to run this utility.
Getting down

There is already a kernel in / boot with support for EFI boot STUB.
Verify
To check if the option was enabled when building the kernel, execute:
CONFIG_EFI_STUB = y means that the option is active.
$ zgrep CONFIG_EFI_STUB /proc/config.gz
or$ zgrep CONFIG_EFI_STUB /boot/config-`uname -r`
CONFIG_EFI_STUB = y means that the option is active.
Next, we have a bunch of scenarios:
- You can mount ESP \ {vendor} \ on / boot via mount --bind by first copying the contents.ExceptionsThis item is only suitable for distributions that do not contain symbolic links in the / boot directory . For example, you won’t be able to mount on openSUSE, because it contains several links there, including the kernel itself.
- Alternatively, when updating the kernel, each time copy it and ram-disk to ESP \ {vendor} \
- You can put the EFI driver to read the / boot file system and boot directly, just adding the extension '.efi' to the kernel (or better hardlink).
Now you need to somehow add the boot point to NVRAM UEFI. Here again, there are many options:
If we are already loaded in EFI mode (efibootmgr -v does not swear) using the boot loaders GRUB2, rEFInd, etc., then everything is fine:
- We use efibootmgr, which can transmit kernel parameters.
- If efibootmgr kicks, you can use the UEFI Shell , which, like our core, is an EFI application. Through his bcfg command, it is possible to edit boot points.
- There may be such an option: efibootmgr swears at adding parameters, which means the firmware does not support writing them (or just a curve, which is more likely). In a previous article, the comments mentioned the efi_no_storage_paranoia kernel parameter , which might help. But you can use it only if you are sure that your firmware is implemented fully in accordance with the specification! The developers warn that if the vendor added crutches and gaps during implementation, there is an unlikely probability of materializing a brick in place of the motherboard.
- You can also boot through the UEFI Shell. A startup.nsh script is created for it , which indicates the kernel boot command with the desired command line. And Shell, in turn, is added as a download point.
- There is another problem: adding an item is possible only for one kernel path, while ram-disk is not visible. Most articles recommend rebuilding the kernel with the built-in initrd. I don’t know for sure if this is a kernel problem, or a bootloader. But at the moment, in 90% of cases, everything is supported and there is no need to rebuild the kernel.Probable cause of errorRecommendations for embedding a ram disk in the kernel went, most likely, due to mass incorrectly specifying the path to it. In early EFI Boot Stub implementations, the kernel did not spit the error about the wrong path to ram-disk, but silently refused to boot. Apparently, therefore, everyone began to massively introduce it into the kernel, deciding that it is not supported. Although support for the initrd parameter has existed since the Boot Stub feature appeared in the kernel.
IMPORTANT: The path to the ram-disk is transferred absolute through backslashes "\" , but not direct! For example, initrd = \ EFI \ archlinux \ initramfs-linux.img.Exceptions for AnarchistsIn fact, kernels, versions higher than 3.8.0-rc5 do not see the difference between the forward and backslashes - any will work. But since the appearance of the Boot Stub feature in version 3.2.0-rc5, the kernel simply did not see the path written through forward slashes and silently refused to boot without errors. Swearing errors about this, it learned in version 3.4.0.
- Download the first live-cd with EFI boot support. And from it already use the efibootmgr command.
- Download UEFI Shell. From it, you can both boot in EFI mode by simply specifying the kernel and ram disk, and edit the boot points.
Dualboot without bootloader
If you have 2 systems installed at the same time, and still do not want to install a third-party bootloader, you can add both to the UEFI boot points and adjust the preferred boot order. The Windows boot loader is usually located in \ EFI \ Microsoft \ BOOT \ bootmgfw.efi .
Total
If everything is done correctly, we reboot, call the Boot Menu, select the item we added and look at an almost instant boot. In the case of SSD, FastBoot, Readahead and Arch Linux - about 3-4 seconds. For a year now, the home server has been loading without any third-party bootloaders using the EFI Boot STUB.
Of course, the gain in speed here is minimal, but, as knowledgeable people like Roderick Smith write , sometimes in the EFI Boot mode there is a “more adequate" initialization of the equipment than in the compatibility modes.
Conclusion
Due to the relative dampness of the UEFI firmware and completely different implementations, I did not give code examples. In each case, there may be a problem. I hope that what I described will help to understand the general principle and apply to my case.
I also recommend flashing the latest version of UEFI from the website of the motherboard manufacturer.
Literature
UEFI Official Specifications
Roderick W. Smith's Web Page is the work of the author of many utilities related to EFI, bootloaders, and disk partitioning.
ArchWiki: UEFI Bootloaders - unchanged and one of the best and most complete GNU / Linux wiki of one of the distributions.
Official PE / COFF Specification