22 KiB
UEFI - Unified EFI
- Overview
- Identification
- Partition Layout
- GRUB Layout
- GRUB Installation
- Boot Process
- Rescue Mode
- EFI Applications
- Troubleshooting
- References
Overview
From Intel on the origins and how the acronym UEFI and EFI are related, as they tend to be used interchangeably on many websites and in a lot of documentation (only a select portion of this web page reproduced here):
The Unified EFI (UEFI) Specification (previously known as the EFI Specification) defines an interface between an operating system and platform firmware. Intel's original version of this specification was publicly named EFI, ending with the EFI 1.10 version.
In 2005, The Unified EFI Forum was formed as an industry-wide organization to promote adoption and continue the development of the EFI Specification. Using the EFI 1.10 Specification as the starting point, this industry group released the following specifications, renamed Unified EFI.
For the Linux tech, this boils down to three major items on a server:
- The GPT partition table is used, MBR is not allowed
- Because GPT is used, > 2T boot disks can be supported
- A special vfat partition
/boot/efiexists on the system for GRUB, the kernel and others to use
This article will cover the operational aspects of working with UEFI Linux systems.
Identification
The One True Way to know if the Linux system is booted into EFI mode is the presence of this virtual directory, only when booted to EFI mode:
/sys/firmware/efi/
This is a defined standard in the Linux kernel and can be used reliably to determine if the server is currently booted to EFI mode or traditional BIOS. Within this directory will be a handful of interfaces which can manipulate the EFI infrastructure itself. When booted in traditional BIOS mode, this directory will be completely missing.
Partition Layout
In general, the design is such that the first partition is the special EFI code, the second is the traditional boot, and the third the rest of the system. Remember, as this is a native GPT partition table there is no special primary/extended problem as there is with MBR, it's just flat partitions all the way. Visually it may feel odd, as the first partition is mounted underneath the second one (so, it appears backwards in mounting from how it appears in the partition table).
Partition Table
# parted /dev/sda unit s print
...
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 2048s 520192s 518145s fat32 EFI boot
2 522240s 1546240s 1024001s ext4 boot boot
3 1548288s 936640478s 935092191s pv0 lvm
Filesystem Mounts
# df -h | grep -v tmpfs
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vglocal20171117-root00 439G 3.5G 435G 1% /
/dev/sda2 477M 219M 254M 47% /boot
/dev/sda1 252M 9.7M 242M 4% /boot/efi
/dev/mapper/vglocal20171117-tmp00 2.0G 3.2M 2.0G 1% /tmp
GRUB Layout
The majority of changes about dealing with EFI happen at the GRUB layer; most of the work is performed at install time however upon occasion it's required to repair or manually manipulate the GRUB configuration. The implementation differs from vendor to vendor, and even within RHEL and CentOS there are minor differences.
The GRUB configuration maker (
grub2-mkconfig/grub-mkconfig) does not support writing both an EFI and BIOS version of the grub.cfg file. When the script is run, it looks for the/sys/firmware/efidirectory, and if found it will create all GRUB menu entries in EFI mode. There is currently no way to override this other than hacking the script to change this behaviour. Once you build GRUB in EFI mode, do not attempt to change the server to BIOS mode and boot it unless you have manually created a non-EFI config file.
RHEL / CentOS
At it's core, three additional packages are added to the OS on top of all the other traditional (non-EFI) GRUB packages:
grub2-efi-x64 efibootmgr shim
The installation of these three packages changes the system to implement a few fundamental designs which must be taken into account:
- The GRUB environment boot block – which stores the named kernel (version) to boot - is moved and symlinked upon package install.
Before:
# ls -lG /boot/grub2/grubenv
-rw-r--r--. 1 root 1024 Sep 26 11:23 /boot/grub2/grubenv
After:
# ls -lG /boot/grub2/grubenv
lrwxrwxrwx. 1 root 28 Nov 17 10:12 /boot/grub2/grubenv -> /boot/efi/EFI/redhat/grubenv
The installation of grub2-efi-x64 moves the boot block to an EFI only area, and it does not work if you boot the server in non-EFI mode with the grub EFI package installed! When booting non-EFI, GRUB will simply not find any boot block (the symlink doesn't work to GRUB) and boot the first kernel found. (Editor's note: Fedora changed this to be relative not absolute, a Red Hat case was opened asking them to backport this to RHEL7 so that the grub2-efi-x64 managed symlink will work in if it's installed on a non-EFI booting system. https://access.redhat.com/support/cases/#/case/01907436 )
- There are two GRUB config files - one for EFI, one for non-EFI. Red Hat handles this externally via symlinks, like so:
# ls -lG /etc/grub*.cfg
lrwxrwxrwx. 1 root 22 Nov 17 10:12 /etc/grub2.cfg -> ../boot/grub2/grub.cfg
lrwxrwxrwx. 1 root 31 Nov 17 10:12 /etc/grub2-efi.cfg -> ../boot/efi/EFI/redhat/grub.cfg
When manually updating grub.cfg (using grub2-mkconfig, vim, etc.) be sure to edit the correct EFI config file! The kernel RPM upgrades call the shell script /sbin/new-kernel-pkg which updates both config files for us, making it seem transparent. The 'redhat' subdirectory will be 'centos' on a CentOS system (most likely to avoid the trademark issues), see below.
- GRUB sees it's boot area differently - when making changes to grub.cfg, make the backup file in the same directory as the original – in
/boot/grub2for non-EFI, in/boot/efi/EFI/{redhat,centos}/for the EFI version. This way if you need to boot grub manually with the backup it's within view at the GRUB console as expected.
Debian / Ubuntu
At it's core, three additional packages are added to the OS on top of all the other traditional (non-EFI) GRUB packages:
grub-efi-amd64 efibootmgr shim
Unlike the Red Hat design, Ubuntu integrates the EFI work into the existing GRUB configuration using an 'include' style architecture.
- The GRUB environment block - grubenv - is unchanged and lives in
/boot/grub/ - The GRUB config file - grub.cfg - is unchanged and lives in
/boot/grub/ - The EFI GRUB
/boot/efi/EFI/ubuntu/grub.cfgis a small include file that generally looks like this:
If /boot is on it's own unique partition (/dev/sda2, e.g.):
search.fs_uuid <UUID to the /boot sda2 partition> root hd0,gpt2
set prefix=($root)'/grub'
configfile $prefix/grub.cfg
If /boot is just a subdirectory of the root / partition (one filesystem):
search.fs_uuid <UUID to the root partition> root
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
The value of $prefix is carried into the /boot/grub/grub.cfg file, which is how it finds it's grubenv file in the same way:
if [ -s $prefix/grubenv]; then
set have_grubenv=true
...
Using this design, the EFI files simply include the main configuration files in their traditional location. This would imply compatibility with all existing tooling and the need to generate only one config file when making changes.
GRUB Installation
The process to prepare a system for EFI use with the GRUB packages differs; besides running a similar boot block install command, a secondary command must be run to update the EFI Bootmanager data to know how to boot the system. Note: in most (if not all) cases after setting up GRUB the commands must be set for the Boot Order, see the section further down with instructions on this process - boot order is for EFI, not GRUB in this case. (GRUB only handles the actual kernels to boot after EFI has booted into it)
RHEL / CentOS
The process is almost identical, except for the name change to avoid trademark infringement by CentOS (standard design change).
## Required packages done during install, mock install for reference here:
yum install grub2-efi efibootmgr shim
## Install the boot block and tell it where EFI data is
grub2-install --target=x86_64-efi \
--efi-directory=/boot/efi \
--bootloader-id=grub \
--debug
## RHEL system - notice the directory name and label
efibootmgr --create --gpt \
--disk /dev/sda --part 1 --write-signature \
--label "Red Hat Enterprise Linux" \
--loader /EFI/redhat/shim.efi
## CentOS system - notice the directory name and label
efibootmgr --create --gpt \
--disk /dev/sda --part 1 --write-signature \
--label "CentOS Linux" \
--loader /EFI/centos/shim.efi
# Build the config from /etc/default/grub into the EFI area
grub2-mkconfig > /boot/efi/EFI/redhat/grub.cfg
As mentioned above, there is a non-EFI grub config (not being updated here) and the EFI grub config file as shown above. They are not symlinked together – when GRUB boots in non-EFI vs. EFI mode it sees the partitions differently.
Debian / Ubuntu
The process is generally the same, this platform provides a nicer way to update GRUB2 but is otherwise the same work:
## Required packages done during install, mock install for reference here:
apt-get update && apt-get install grub-efi-amd64 grub-efi-amd64-bin \
grub-efi-amd64-signed efibootmgr shim shim-signed
## Install the boot block and tell it where EFI data is
grub2-install --target=x86_64-efi \
--efi-directory=/boot/efi \
--bootloader-id=grub \
--debug
## Create the EFI bootloader entry
efibootmgr --create --gpt \
--disk /dev/sda --part 1 --write-signature \
--label "Ubuntu Linux" \
--loader /EFI/ubuntu/shimx64.efi
## Update GRUB
update-grub
EFI Boot to GRUB
This is normally handled automatically by the efibootmgr command when adding the new entry; if needed, see the Troubleshooting section below Setting EFI Boot Order to have EFI hand off the boot to GRUB appropriately.
Boot Process
Based on the above layout, the boot process can now be examined - the EFI firmware has an understanding of partition types and tables for many platforms and expects there to be EFI applications on the vfat (fat32, "sda1") partition at the start of the disk as well as custom settings such as the EFI Bootloader menu. See the EFI Applications section for a full rundown of the contents of this disk area.
RHEL / CentOS
This is documented very succinctly by Red Hat, reproduced here:
- The UEFI-based platform reads the partition table on the system storage and mounts the EFI System Partition (ESP), a VFAT partition labeled with a particular globally unique identifier (GUID). The ESP contains EFI applications such as bootloaders and utility software, stored in directories specific to software vendors. Viewed from within the Red Hat Enterprise Linux 6.9 file system, the ESP is /boot/efi/, and EFI software provided by Red Hat is stored in
/boot/efi/EFI/redhat/. - The
/boot/efi/EFI/redhat/directory containsgrub.efi, a version of GRUB compiled for the EFI firmware architecture as an EFI application. In the simplest case, the EFI boot manager selects grub.efi as the default bootloader and reads it into memory. If the ESP contains other EFI applications, the EFI boot manager might prompt you to select an application to run, rather than load grub.efi automatically. - GRUB determines which operating system or kernel to start, loads it into memory, and transfers control of the machine to that operating system.
The last step is what reads grub.cfg and the environment boot block (grubenv) to know which kernel to boot with the desired options. The above is not 100% true in RHEL7, as the system launches shim.efi which then launches grub.efi - shim.efi is signed, grub.efi is not so if a system is set to SecureBoot it needs to launch a shim.
Debian / Ubuntu
The same as the above just with minor details changed unique to the vendor. Upstream: https://help.ubuntu.com/community/UEFI
Rescue Mode
UEFI Shell
When the UEFI firmware fails to boot GRUB (due to any random reason - if you're here something has definitely gone wrong), it will drop you into the UEFI Shell. It's a daunting looking interface, and depends on the exact vendor to determine what it looks like. In general, remember these tips:
- It acts like Windows - the paths have
\chars, 'ls' shows Windows-like directory lists, etc. FS0:is the magic EFI-ESP boot area, simply typingFS0: <enter>like on Windows C:\ or D:\- Once in
FS0:\\, find theshimx64.efifile and run it like a Windows command - it will load GRUB for you
The shim file should transparently boot you into the GRUB binary; however, it's possible to just boot the grubx64.efi file directly as well. Each EFI file is a binary which can be run (mmx64.efi is MOK Manager, generally useless in daily life - shimx64.efi and grubx64.efi are what you're after to run).
Generally speaking, if you're been dumped into the UEFI Shell it indicates that one of two things has gone wrong:
- The GRUB boot block has not been installed properly to /dev/sda,
grub-installis needed to repair - The EFI bootloader does not have a config to launch GRUB,
efibootmgris needed to repair
Running the grub-install process may run the efibootmgr process automatically as one of it's finishing steps, confirmed on at least Ubuntu 16 that it does. A broken Ubuntu 16 install was repaired in one shot using this command as an example - this is only an example:
# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader=grub --debug
Results will vary - by definition what is broken depends on the exact situation at hand, not all things break in the same way. The above example fixes the most basic problem - the GRUB boot block and/or EFI entry was not properly installed and needed a quick repair.
EFI Applications
What are all these files in /boot/efi? The EFI design is such that it can run any applications written with the EFI SDK - basically, 32bit Windows (PE-executable) files. While the normal usage doesn't require touching any of these, it's handy to understand what they are for.
RHEL / CentOS
Replace the vendor name (redhat -> centos) below, otherwise the same.
| Filename | Purpose |
|---|---|
/boot/efi/EFI/BOOT/BOOTX64.EFI |
The standard, unsigned, GNU EFI Bootloader application - used by bootable USB drives for example |
/boot/efi/EFI/BOOT/fbx64.efi |
The fallback EFI application if the GNU EFI Bootloader fails to find any options or bootloader order (scans on the fly) |
/boot/efi/EFI/redhat/mmx64.efi |
MOK Manager app - the EFI application to sign apps with keys |
/boot/efi/EFI/redhat/shim.efi |
The signed - by Microsoft - EFI Bootloader (aka SecureBoot) |
/boot/efi/EFI/redhat/shimx64.efi |
A copy of shim.efi for compatibility, exact same file as the above |
/boot/efi/EFI/redhat/shimx64-redhat.efi |
The Red Hat key signed version of the GNU EFI Bootloader application |
/boot/efi/EFI/redhat/grubx64.efi |
The unsigned GRUB binary application, it is transparently launched by the signed shim.efi / shimx86.efi |
/boot/efi/EFI/Dell/BootOptionCache/BootOptionCache.dat |
Dell firmware settings for EFI boot options |
/boot/efi/EFI/redhat/grubenv |
GRUB environment boot block, kernel name to boot stored here |
/boot/efi/EFI/redhat/grub.cfg |
GRUB EFI configuration file |
/boot/efi/EFI/redhat/fonts/unicode.pf2 |
Fonts for use with GRUB / other EFI apps |
/boot/efi/EFI/redhat/BOOT.CSV / BOOTX64.CSV |
The EFI Bootloader application options managed by efibootmgr (loads shim.efi, basically) |
Ubuntu / Debian
The Ubuntu system keeps an identical copy of files in /boot/efi/EFI/grub and /boot/efi/EFI/ubuntu - the contents are copied in from outside dynamically, unlike the RHEL/CentOS packages which install the files directly into the /boot/efi area.
| Filename | Purpose |
|---|---|
/boot/efi/EFI/{grub,ubuntu}/grub.cfg |
GRUB include file which sources /boot/grub/grub.cfg on the fly using the UUID of the disk |
/boot/efi/EFI/{grub,ubuntu}/grubx64.efi |
The unsigned GRUB binary application, it is transparently launched by the signed shimx86.efi |
/boot/efi/EFI/{grub,ubuntu}/mmx64.efi |
MOK Manager app - the EFI application to sign apps with keys |
/boot/efi/EFI/{grub,ubuntu}/shimx64.efi |
The signed - by Microsoft - EFI Bootloader (aka SecureBoot) |
These are sourced from the other packages:
| Package | Filename | Purpose |
|---|---|---|
shim |
/usr/lib/shim/fbx64.efi.signed |
The fallback EFI application if the GNU EFI Bootloader fails to find any options or bootloader order |
shim |
/usr/lib/shim/mmx64.efi.signed |
The signed MOK Manager EFI application copied to /boot/efi/EFI/{grub,ubuntu}/mmx64.efi |
shim |
/usr/lib/shim/shimx64.efi |
The signed - by Microsoft - EFI Bootloader (aka SecureBoot) copied to /boot/efi/EFI/{grub,ubuntu}/shimx64.efi |
grub-efi-amd64-signed |
/usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed |
The grubx64.efi EFI application copied to /boot/efi/EFI/{grub,ubuntu}/grubx64.efi |
Troubleshooting
Setting EFI Boot Order
The efibootmgr command can be used to display and set the boot order based on what's configured as possible boot options. Every option is given a numerical designation, setting the boot order is just indicating their logical order to try. First, display the current boot order by passing no options to the command:
# efibootmgr
BootCurrent: 0002
BootOrder: 0003,0000,0001,0002
Boot0000* EFI DVD/CDROM
Boot0001* EFI Hard Drive
Boot0002* EFI Internal Shell
Boot0003* Ubuntu
Notice in the above example, the boot order is set to the EFI internal shell and that was what we booted into (notice BootCurrent); our desire is to reset this boot order to try the OS (Ubuntu) first, then the generic hard drive, then the DVD, then finally the shell last. We specify those options to the command in order desired, and it will display the result automatically:
# efibootmgr --bootorder 0003,0001,0000,0002
BootCurrent: 0002
BootOrder: 0003,0001,0000,0002
Boot0000* EFI DVD/CDROM
Boot0001* EFI Hard Drive
Boot0002* EFI Internal Shell
Boot0003* Ubuntu
After rebooting the device it should have gone straight to option 0003 (Ubuntu), and BootCurrent should reflect that choice:
# efibootmgr
BootCurrent: 0003
BootOrder: 0003,0001,0000,0002
Boot0000* EFI DVD/CDROM
Boot0001* EFI Hard Drive
Boot0002* EFI Internal Shell
Boot0003* Ubuntu
See man efibootmgr for additional options such as activating and deactivating choices.
Add EFI Boot Option
If the device seems to boot to the EFI shell or disk incorrectly, it's possible the OS is missing an entry in the EFI variables to boot from to the OS. In shit case, first examine the bootable options to observe that there's no entry for the OS (GRUB, mainly) present:
# efibootmgr
BootCurrent: 0002
BootOrder: 0001,0000,0002
Boot0000* EFI DVD/CDROM
Boot0001* EFI Hard Drive
Boot0002* EFI Internal Shell
There should be an additional entry "Ubuntu" or "Red Hat" or "CentOS" (etc.) pointing directly at the shim EFI. To add / correct the server, add the entry using efibootmgr – the method is almost identical for all the distros, just varies by the vendor's name in the path.
## RHEL
efibootmgr --create --gpt \
--disk /dev/sda --part 1 --write-signature \
--label "Red Hat Enterprise Linux" \
--loader /EFI/redhat/shim.efi
## CentOS
efibootmgr --create --gpt \
--disk /dev/sda --part 1 --write-signature \
--label "CentOS Linux" \
--loader /EFI/centos/shim.efi
## Ubuntu
efibootmgr --create --gpt \
--disk /dev/sda --part 1 --write-signature \
--label "Ubuntu Linux" \
--loader /EFI/ubuntu/shimx64.efi
After adding the new entry, you must set the boot order for the new entry to boot first. Follow the Setting EFI Boot Order section above to now choose your OS as the default first choice to boot.
References
- https://www.intel.com/content/www/us/en/architecture-and-technology/unified-extensible-firmware-interface/efi-homepage-general-technology.html
- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/installation_guide/s2-grub-whatis-booting-uefi
- https://access.redhat.com/support/cases/#/case/01907436
- https://help.ubuntu.com/community/UEFI