Ivan Kovmir

UEFI Demystified - Ivan Kovmir

UEFI Demystified

Last edit: 2024-04-10

I have not come across any good explanations of what UEFI is and how it works, even though you have to deal with it almost every time you install an operating system. The following guide provides a comprehensive overview of UEFI and its functioning.

UEFI Is

UEFI is a firmware on the motherboard, it can configure hardware and boot operating systems.

Actually, hardware configuration menu and operating system bootloaders are examples of UEFI applications, which you can develop too. Futher text refers specifically to UEFI bootloaders.

Legacy BIOS

UEFI predecessor.

EFI & UEFI

Nowadays interchangeable terms.

UEFI Structure & Toolchain

UEFI requires its own GPT disk partition of type EFI, formatted with FAT32 file system. People usually follow the old habit from the days of Legacy BIOS and create this partition at the beginning of a disk, not sure whether it is still mandatory for UEFI (but it would not hurt). If you are unsure about the size, make it 512 MiB. EFI partition is where the bootloader resides among other things, a bootloader is a file with .efi extension.

If you have a running Linux, try to locate and observe your existing EFI partition. Open /etc/fstab, or execute lsblk -f and find a partition with FAT filesystem, then look for its mount point. Within /etc/fstab the entry would look similar to the following:

/dev/sda1         /boot/efi        vfat        defaults         0   0

The partition is mounted under /boot/efi, let us take a look at it:

$ tree /boot/efi
/boot/efi
├── EFI
│   └── Slackware
│       ├── elilo.conf
│       ├── elilo.efi
│       ├── initrd.gz
│       ├── vmlinuz-generic
│       └── vmlinuz-huge
└── limine
    ├── limine.cfg
    └── liminex64.efi

4 directories, 8 files

Indeed, it has: two bootloaders elilo.efi, and liminex64.efi; their config files elilo.conf, and limine.cfg; Linux kernel vmlinuz-* and ramdisk initrd.gz used by the bootloader to boot Linux.

So, you have created a UEFI partition, installed a UEFI bootloader, written a config file and provided the kernel and ramdisk. There is one last thing left to do: you have to tell your UEFI system within the motherboard where to find a UEFI bootloader within a UEFI partition. One of the ways is to put a bootloader under /EFI/BOOT/BOOTX64.EFI, which is a path that should be checked by default. If you put it somewhere else (but within a UEFI partition), you have to tell your UEFI system within the motherboard where to find it. Linux has efibootmgr program to help us. Execute efibootmgr -v to see the current state of things:

BootCurrent: 0000
Timeout: 2 seconds
BootOrder: 0000,0001
Boot0000* Limine    HD(1,GPT,578fd4d6-329c-3e42-b9fc-c6f5a32c7bd0,0x800,0xfa000)/File(\limine\liminex64.efi)
      dp: 04 01 2a 00 01 00 00 00 00 08 00 00 00 00 00 00 00 a0 0f 00 00 00 00 00 d6 d4 8f 57 9c 32 42 3e b9 fc c6 f5 a3 2c 7b d0 02 02 / 04 04 30 00 5c 00 6c 00 69 00 6d 00 69 00 6e 00 65 00 5c 00 6c 00 69 00 6d 00 69 00 6e 00 65 00 78 00 36 00 34 00 2e 00 65 00 66 00 69 00 00 00 / 7f ff 04 00
Boot0001* Slackware HD(1,GPT,578fd4d6-329c-3e42-b9fc-c6f5a32c7bd0,0x800,0xfa000)/File(\EFI\Slackware\elilo.efi)
      dp: 04 01 2a 00 01 00 00 00 00 08 00 00 00 00 00 00 00 a0 0f 00 00 00 00 00 d6 d4 8f 57 9c 32 42 3e b9 fc c6 f5 a3 2c 7b d0 02 02 / 04 04 36 00 5c 00 45 00 46 00 49 00 5c 00 53 00 6c 00 61 00 63 00 6b 00 77 00 61 00 72 00 65 00 5c 00 65 00 6c 00 69 00 6c 00 6f 00 2e 00 65 00 66 00 69 00 00 00 / 7f ff 04 00

The output shows the currently existing entries, and the bootloaders they point to. As you can see, I have two of them: ‘Limine’ which points to \limine\liminex64.efi, and ‘Slackware’ which points to EFI\Slackware\elilo.efi. Backslashes (‘\’) are used because FAT is a Microsoft filesystem. It is also worth noting that Microsoft filesystem are, unlike UNIX, case-insensitive, so ‘myFile’ and ‘MYFILE’ are the same name. There is no need to create an entry to point to /EFI/BOOT/BOOTX64.EFI, as it is being checked by default, but it should not hurt if you do. Gentoo wiki does a very good job explaining how to use efibootmgr, read it. To add a boot entry you would have to issue the following command:

efibootmgr -c -d /dev/sda -p 1 -L "EntryName" -l '\path\to\bootloader.efi' # -p to specify partition number.

That is pretty much all you need to know.

Practical UEFI

Let us configure a UEFI bootloader. I hate Grub 2, it is the worst and most complex of bootloaders. Limine is perfect: dead simple, yet highly configurable. It supports FAT file system only, as it should, the rest is the kernel’s job. It supports Linux boot protocol natively, and it can simply run other UEFI bootloaders—that is, chainloading. Figure 1 shows example configuration. It is possible to try out Limine on your existing system without breaking anything, which is what I will do in this guide further down.

Limine's boot menu
Fig. 1: Limine Bootloader Screenshot

You can build it from the sources, or install the binaries provided on their GitHub repository. If you are on Slackware Linux, Limine Slackbuild is also available to you.

To install Limine, copy the .efi file to anywhere within the EFI partition; anywhere under /boot/efi in my case. Then add a boot entry with efibootmgr, follow the example from the previous paragraph. You can now boot into Limine, just write a config file to specify what you want to load. To boot Linux, you need two things: the kernel, and initial ramdisk. A typical Linux Limine entry would look like that:

# Save me as 'limine.cfg' at the root of the EFI partiton.
:WhateverName
    PROTOCOL=linux
    KERNEL_PATH=boot:///path/to/kernel
    MODULE_PATH=boot:///path/to/ramdisk
    CMDLINE=root=/path/to/root/system/partition vga=normal ro

CMDLINE specifies arguments to pass to the kernel. You can copy these from the existing bootloader config, typically you should specify the system’s root partition. boot:/// states Limine should look for the files on the same partition where Limine bootloader resides, read Limine documentation to get more details.

Suppose you have Windows installed as your second OS, and you want to be able to choose which OS Limine should run. Unlike Linux, Limine does not support Windows boot protocol natively, but Limine can do bootloader chainloading. It means you can ask Limine to launch Windows'—just like any other—UEFI bootloader, which will handle Windows' boot process. So add another entry to make your limine.cfg file look like that:

# Save me as 'limine.cfg' at the root of the EFI partiton.
# Forward slashes ('/') should be used to build paths.
:WhateverName
    PROTOCOL=linux
    KERNEL_PATH=boot:///path/to/kernel
    MODULE_PATH=boot:///path/to/ramdisk
    CMDLINE=root=/path/to/root/system/partition vga=normal ro

# Adjust windows bootloader path if needed.
:Windows
    PROTOCOL=chainload
    IMAGE_PATH=boot:///EFI/Microsoft/Boot/bootmgfw.efi

My Limine config looks like that:

TIMEOUT=5           # 5 second timeout.
TERM_FONT_SCALE=2x2 # Bigger font.

:Slackware
    PROTOCOL=linux
    KERNEL_PATH=boot:///EFI/Slackware/vmlinuz-generic
    MODULE_PATH=boot:///EFI/Slackware/initrd.gz
    CMDLINE=root=/path/to/root/system/partition vga=normal ro

:Windows
    PROTOCOL=chainload
    IMAGE_PATH=boot:///EFI/Microsoft/Boot/bootmgfw.efi

Read Limine documentation for more options.

UEFI Caveats

Laptop UEFI implementations are often horrible.

frogbar

© 2024 Ivan Kovmir — CC-BY 4.0 License

Created with swege BEST VIEWED WITH EYES Indexed by Wiby