Linux Kernel EFI Boot Stub or "Your own bootloader"

UEFI Tux Logo

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:

PE optional header


imageThe main difference from the "real" executable files in Windows is the Subsystem flag of the optional header, which is set in IMAGE_SUBSYSTEM_EFI_APPLICATION, and not in IMAGE_SUBSYSTEM_WINDOWS_GUI for graphical or IMAGE_SUBSYSTEM_WINDOWS_CUI for symbolic (console) applications.

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


imageFirst of all, you need to activate the EFI-mode boot mode. An item can be called as a vendor thinks, usually located in the Boot Options tab. If you saw there something like Legacy Mode or CSM (Compatibility Support Mode), or just BOIS Mode, change it to something similar: (U) EFI Mode, Enhanced Mode or Advanced Mode.

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
# 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 does not suffer from this:
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


imageSo, we have marked FAT32 EFI System Partition (ESP) size 550MiB . Or, we have Windows with the second system and have already created it ourselves. True, she creates it usually about 100MB in size, but personally, I never had problems.
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:
$ 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.
    Exceptions
    This 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 error
    Recommendations 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 Anarchists
    In 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.

If we just found out about the EFI boot mode and want to switch to it to add boot points, you need to be in this mode already , and then we are still in compatibility mode ... There are two main solutions:
  • 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

Also popular now: