-
Notifications
You must be signed in to change notification settings - Fork 11
Home
UEFI-Boot is a simple project that focused on loading Linux kernel directly from UEFI firmware without need in any bootloader.
Russian Wiki version is available at: http://help.ubuntu.ru/wiki/uefiboot
Fact 1: UEFI provides an ability to load several OS or utilities via number of boot options.
Fact 2: Any Linux kernel that has been built with UEFISTUB option (at the moment it is standard options for many distributions, including Debian, Ubuntu, SUSE and many others) can be loaded directly from UEFI boot option.
Summarize both facts together... Yes, the big question appears - "what for a bootloader is required now?"
Consider additionally the fact that Debian based distributions (and some other) uses Shim for SecureBoot loading of GRUB, and then GRUB (finally) loads the kernel. IMHO this chain of loaders is too long...
You can think that the chain UEFI -> Shim -> GRUB -> Kernel is the only way to boot a computer in SecureBoot mode. But it's not true - kernel itself can be signed to be loaded in SecureBoot mode. And even more: Ubuntu repository (and some other repositories) provides already signed kernels via linux-signed-generic package. Ubuntu kernels are signed by Canonical key (by the same as GRUB signed). And insertion of the Canonical key to the UEFI db keys list can allow loading the signed kernels directly by UEFI in SecureBoot mode.
Let's shorten the booting chain to UEFI -> kernel. There are a few methods which can give us such solution. But, the first of all, we have to understand two thing:
-
How provide initrd to the kernel (standard loaders loads both - the kernel and initrd for it)
-
How can UEFI access kernel and initrd?
The answer on the first question is simple - Linux kernels starting from v.3.3. have parameter 'initrd' which can be used to provide the information about initrd location to the kernel (kernel itself loads the initrd in such case). You can use '/' or '\' in the path - kernel doesn't care which one is used. What you really should care of - how correctly specify the path in the environment where the kernel runs. Kernel runs in the UEFI environment and you should use UEFI environment notations for path definition. Remember that UEFI root location is the root of ESP partition and path delimiter is '\'.
Second question is not such simple as the first one. UEFI has access to ESP partition (certainly) but there are no kernels in the ESP (by default). Kernels are in /boot catalog of root partition or in the separate boot partition. Usually the root (boot) partition has EXT2-4/btrfs or other filesystems, but without additional drivers UEFI knows only FAT32 filesystem. So, the second question splits on several of possible solutions. Lets review all of them:
UEFI can load drivers to access different filesystems (uefi filesystem drivers can be easily googled). Lets load the UEFI shell and perform the following steps:
-
Load the filesystem driver (it was previously copied to EFS volume)
load fs0:\EFI\drivers\ext2_x64.efi
-
Activate (mount) all possible filesystems
map -r
-
Change current path to new filesystem root
fs1:
-
Load kernel
vmlinuz root=UUID=0160863a-1468-422b-8bbb-f80e98e3600d ro queit initrd=initrd.img
Great think that root filesystem has short-named links to last 2 versions of kernels and initrds. Bad thing that we can't load script file from UEFI boot option (UEFI boot option is designed to run only binary files).
UEFI can automatically load drivers (by UEFI specification)... but I found only one tricky hacking method to force the UEFI to make driver remapping (analog of map -r command in UEFI Shell) after driver is loaded. Without driver remapping there is no access to the filesystems that accessible via the loaded driver. I've created PR to efibootmgr utility to make it possible to activate remapping after driver is loaded. It already merged to main branch and the feature can apper in newest version of uefiboot utility.
So, in spite the fact that this solution deeply uses the UEFI flexibility and possibilities, it rather difficult to organize the fully automatic loading of the system in this way.
Copy the last version of kernel to ESP (wich usually is mounted to /boot/efi):
# cp /boot/*-3.19.0-33-generic /boot/efi/
Now you can add the UEFI boot option by efibootmgr utility:
# efibootmgr -cL Ubuntu -l vmlinuz-3.19.0-33-generic -u "root=UUID=0160863a-1468-422b-8bbb-f80e98e3600d ro quiet initrd=initrd.img-3.19.0-33-generic
Now you can reboot the computer and kernel should be loaded directly from ESP partition. Great! ... but... to realize ability to load in different kernel versions you have to copy all required versions to the ESP partition. The worst thing is that you have to copy initrd every time it is updated (it happens rather often) and we need to copy kernel itself when the new version comes with updates. One more reasonable thing to do - to remove old kernels/initrds (to avoid overfull of EFS).
This solution just further development of solution 2. Instead of repetitive copying/removing of kernels and initrds to/from EFS let's force the system to perform all such action over kernels/initrds directly within the ESP partition. Let's make /boot partition on ESP. This idea is developed in the UEFI-Boot project.
The only one small disadvantage of keeping kernels on FAT is that FAT doesn't support links and re-installation of kernel may fail because of this. If you run into such problem just remove kernel and install it back (instead of re-installation).
UEFI-Boot primary designed for Ubuntu distribution. Note that some distribution related things have to be changed to implement this solution on other Linux distributions.
First we need to move all kernel's/ staff to the root of ESP partition.
# mv /boot/*-generic* /boot/efi/
Dismount ESP partition.
# umount /boot/efi
Clear /boot - it will be just a mount point for ESP partition.
# rm -rf /boot/*
Next, we need to change the mount point of ESP partition in /etc/fstab
# sed -i 's/\/boot\/efi/\/boot/' /etc/fstab
Finally, mount ESP into /boot
# mount /boot
Now all updates of kernels and initrds will be performed directly within the ESP file system, and the in same time the UEFI has ability to access kernel and initrd without any additional drivers.
The update utility itself has a rather simple algorithm: first it removes old UEFI boot options that have no corresponding kernels in /boot. Then it addes new kernels that have no UEFI boot options yet.
The utility has configuration file /etc/uefiboot.conf. It provides ability to specify the root partition reference, kernel boot parameters, and kernel suffix (suffix is required for signed kernels in case of SecureBoot activation). When config parameters are not set then utility:
- takes the root reference and subvolume name/id (for btrfs) from the /etc/fstab file,
- set kernel boot parameters to 'ro quiet' and kernel suffix as empty string. Another way to change uefiboot-update behavior - pass necessary parameters via command line options (see readme file for details)
The last question that we have to solve - to update UEFI boot options every time when a new kernel comes with update or when an old kernel is removed. It can be easily organized via kernel triggers (the same way as the GRUB update script is initialized). Triggering of utility is organized via links in /etc/kernel/postinst.d and /etc/kernel/postrm.d.
# ln -s /usr/bin/uefiboot-update /etc/kernel/postinst.d/uefiboot-update
# ln -s /usr/bin/uefiboot-update /etc/kernel/postrm.d/uefiboot-update
As we excluded Shim and GRUB from the booting chain, now we can (and have to) remove them from the system. os-prober (a component of GRUB that searches for other OS installations) is also not required any more.
# apt-get purge grub* shim os-prober
Additionally we have to instruct the system do not install recommended package as kernel has GRUB in recommends.
# echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/zz-no-recommends
Finally, we have to trigger the utility manually to prepare UEFI for start the system next time.
# uefiboot-update
Don't forget to adjust the UEFI boot timeout (the default value can be 5-10 seconds):
# efibootmgr -t0
It is also possible to load signed kernel version in SecureBoot mode of UEFI. You have to organize the kernel singing or use the already signed kernels from Your distribution and add the verification certificate to db keys list of UEFI. You may have to specify kernel suffix for signed versions of kernels via K_SUFFIX variable in utility config file or via command line options.
One special option should be provided to kernel for loading of the root filesystem that is located in the subvolume of BTRFS. You have to specify the subvolume name (or id) in parameter "rootflags=subvol={subvolume name}" (or "rootflags=subvolid={subvolume ID}"). The uefiboot-update script searches for the information about root's subvolume in the /etc/fstab. You can also specify it manually via ROTFLAGS variable in /etc/uefuboot.conf file or via command line options.