Toward a better GRUB
The scene
GRUB's boot selection menu is an absolute mess I don't know why I care about this, but it drives me insane. Every OS you install dumps its own entries in there, mixing in recovery modes, old kernels, and random tools you never asked for. Instead of a clean, usable bootloader, you end up scrolling through a junk drawer of boot options.
I want:
- One clean entry per OS, loading the normal kernel with standard settings.
- A submenu for advanced options, containing recovery modes and older kernels.
- A separate tools menu for utilities like memtest and firmware updates.
To actually fix this mess, let's first break down the boot process, which consists of:
- UEFI Firmware Phase – The firmware initializes hardware and selects a bootloader.
- GRUB Phase – GRUB loads the system configuration and presents the boot menu.
- Kernel Execution – GRUB hands control over to the Linux kernel.
Step 1: The UEFI Boot Order
UEFI firmware stores boot settings in NVRAM (Non-Volatile RAM), which can be managed in Linux using efibootmgr
. Below is my system's current UEFI boot order:
Checking Boot Configuration
efibootmgr
Output:
BootCurrent: 0000
Timeout: 2 seconds
BootOrder: 0000, 0003, 0002
Boot0000* Ubuntu HD(1,GPT,8f756691-3c00-4eef-99e7-d5b15cadf461,0x800,0x9b4000)/EFI/ubuntu/shimx64.efi
Boot0002* Linux Firmware Updater HD(1,GPT,8f756691-3c00-4eef-99e7-d5b15cadf461,0x800,0x9b4000)/EFI/fedora/fwupdx64.efi
Boot0003* Fedora HD(1,GPT,8f756691-3c00-4eef-99e7-d5b15cadf461,0x800,0x9b4000)/EFI/fedora/shimx64.efi
Key Takeaways
- BootCurrent: The last booted OS (Ubuntu in this case).
- Timeout: The system waits 2 seconds before selecting the first boot entry.
- Boot Order: The system attempts to boot in this sequence:
0000
- Ubuntu0003
- Fedora0002
- Linux Firmware Updater
Boot Number | Entry Name | EFI File Path |
---|---|---|
Boot0000* | Ubuntu | /EFI/ubuntu/shimx64.efi |
Boot0002* | Linux Firmware Updater | /EFI/fedora/fwupdx64.efi |
Boot0003* | Fedora | /EFI/fedora/shimx64.efi |
Step 2: How UEFI Chooses a Bootloader
Each UEFI boot entry points to an .efi
file located on the EFI System Partition (ESP). EFI firmware follows these steps:
- Identifies the boot partition (Partition 1, formatted as FAT32, with PARTUUID
8f756691-3c00-4eef-99e7-d5b15cadf461
). - Executes the
.efi
bootloader file specified in the boot entry. - Loads GRUB (via
shimx64.efi
), which then loadsgrubx64.efi
.
Why Use shimx64.efi
Instead of grubx64.efi
?
shimx64.efi
is signed by Microsoft, allowing it to run even with Secure Boot enabled.- It acts as a thin chainloader, passing control to
grubx64.efi
, which is not directly signed.
Step 3: The GRUB Boot Process
Once grubx64.efi
is executed, it loads a small preliminary GRUB configuration file stored on the ESP, usually just grub.cfg in the same directory as grubx64.efi
. My understanding here is that it is necessary to invoke grub in order to be able to mount the /
or /boot
system in case they are non FAT32 file systems.
Example: Initial GRUB Configuration Files
Ubuntu GRUB Config (/boot/efi/EFI/ubuntu/grub.cfg
)
search.fs_uuid 1eb5e7ee-43ad-40f6-b862-7986c8464659 root
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
Fedora GRUB Config (/boot/efi/EFI/fedora/grub.cfg
)
search --no-floppy --root-dev-only --fs-uuid --set=dev a7cdf71e-2219-423a-ac09-d6a020c22409
set prefix=($dev)/boot/grub2
export $prefix
configfile $prefix/grub.cfg
What's Happening Here?
search.fs_uuid
finds the correct root filesystem via UUID not PART_UUID.set prefix=($root)/boot/grub[2]
defines where to load the full GRUB configuration.configfile $prefix/grub.cfg
loads the full GRUB menu settings from the OS's/boot
partition.
The only meaningful difference here is the different directory they use /boot/grub
for bunts and /boot/grub2
for fez.
I tend not to keep /boot
itself on a separate partition even if in theory it would be more efficient, I just don't trust the different OS' not to trample eachother. Check out the official specification for more (in truth probably waaaay too much) information on how the various boot loaders are supposed to play nice with eachother.
I'm going to go with the Fedora grub entry as the primary loader, so the file we need to change is: /boot/grub2/grub.cfg
There are a set of scrips in /boot/grub.d
which are invoked in priority order by grub2-mkconfig.
of import are 10_linux, loads the current OS' config 30_os_prober - finds other OS partitions and adds entries. 40_custom, where we can add our own stuff.
Our first problem is that fedora uses blscfg to load it's own boot options, these options come from the files in /boot/loader/entries
with one file per boot option. Tbf, this is nice as it allows for each boot option to be delete/added/edited independent of any of the others. But for now it's gotta go.
So first things first, disabable blscfg.
sudo grub2-editenv - unset blscfg
From there we just run the normal grub2-mkconfig to generate the grub.cfg file.
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
This gives us the set of options we need, from here we just re-arrange the entries to match that described above.
Click to view the complete GRUB configuration
# Main OS Entries
menuentry 'Fedora Linux 41 (Workstation Edition)' --class fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-simple-a7cdf71e-2219-423a-ac09-d6a020c22409' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root a7cdf71e-2219-423a-ac09-d6a020c22409
echo 'Loading Linux 6.12.11-200.fc41.x86_64 ...'
linux /boot/vmlinuz-6.12.11-200.fc41.x86_64 root=UUID=a7cdf71e-2219-423a-ac09-d6a020c22409 ro rhgb quiet
echo 'Loading initial ramdisk ...'
initrd /boot/initramfs-6.12.11-200.fc41.x86_64.img
}
menuentry 'Ubuntu 24.04.1 LTS' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-f6be26a7-9f80-4334-8de3-3c80282de363' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root f6be26a7-9f80-4334-8de3-3c80282de363
linux /boot/vmlinuz-6.8.0-51-generic root=UUID=f6be26a7-9f80-4334-8de3-3c80282de363 ro quiet splash $vt_handoff
initrd /boot/initrd.img-6.8.0-51-generic
}
menuentry 'Ubuntu 24.04' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-1eb5e7ee-43ad-40f6-b862-7986c8464659' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 1eb5e7ee-43ad-40f6-b862-7986c8464659
linux /boot/vmlinuz-6.11.0-13-generic root=UUID=1eb5e7ee-43ad-40f6-b862-7986c8464659 ro quiet splash crashkernel=2G-4G:320M,4G-32G:512M,32G-64G:1024M,64G-128G:2048M,128G-:4096M $vt_handoff
initrd /boot/initrd.img-6.11.0-13-generic
}
menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os $menuentry_id_option 'osprober-gnulinux-simple-3d48a87e-0ec8-41e5-9df0-ceec7ec1c44f' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 3d48a87e-0ec8-41e5-9df0-ceec7ec1c44f
linux /boot/vmlinuz-linux root=/dev/nvme0n1p11
initrd /boot/initramfs-linux.img
}
menuentry 'Linux From Scratch' --class gnu-linux --class gnu --class os $menuentry_id_option 'osprober-gnulinux-simple-85204b3d-eeca-4e8f-8cbf-dc51e025bf78' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 85204b3d-eeca-4e8f-8cbf-dc51e025bf78
linux /boot/vmlinuz-6.10.5-lfs-12.2 root=/dev/nvme0n1p10
}
# Advanced Options Submenu
submenu 'Advanced Options' {
# Fedora section
menuentry 'Fedora Linux (6.12.9-200.fc41.x86_64)' --class fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-advanced-a7cdf71e-2219-423a-ac09-d6a020c22409' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root a7cdf71e-2219-423a-ac09-d6a020c22409
echo 'Loading Linux 6.12.9-200.fc41.x86_64 ...'
linux /boot/vmlinuz-6.12.9-200.fc41.x86_64 root=UUID=a7cdf71e-2219-423a-ac09-d6a020c22409 ro rhgb quiet
initrd /boot/initramfs-6.12.9-200.fc41.x86_64.img
}
menuentry 'Fedora Linux (6.12.5-200.fc41.x86_64)' --class fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-advanced-a7cdf71e-2219-423a-ac09-d6a020c22409' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root a7cdf71e-2219-423a-ac09-d6a020c22409
echo 'Loading Linux 6.12.5-200.fc41.x86_64 ...'
linux /boot/vmlinuz-6.12.5-200.fc41.x86_64 root=UUID=a7cdf71e-2219-423a-ac09-d6a020c22409 ro rhgb quiet
initrd /boot/initramfs-6.12.5-200.fc41.x86_64.img
}
menuentry 'Fedora Linux (6.12.4-200.fc41.x86_64)' --class fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-advanced-a7cdf71e-2219-423a-ac09-d6a020c22409' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root a7cdf71e-2219-423a-ac09-d6a020c22409
echo 'Loading Linux 6.12.4-200.fc41.x86_64 ...'
linux /boot/vmlinuz-6.12.4-200.fc41.x86_64 root=UUID=a7cdf71e-2219-423a-ac09-d6a020c22409 ro rhgb quiet
initrd /boot/initramfs-6.12.4-200.fc41.x86_64.img
}
menuentry 'Fedora Linux (Rescue)' --class fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-rescue-a7cdf71e-2219-423a-ac09-d6a020c22409' {
load_video
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root a7cdf71e-2219-423a-ac09-d6a020c22409
echo 'Loading Linux 0-rescue-fd247ce0ebe345f6b569283b6eda528b ...'
linux /boot/vmlinuz-0-rescue-fd247ce0ebe345f6b569283b6eda528b root=UUID=a7cdf71e-2219-423a-ac09-d6a020c22409 ro rhgb quiet
initrd /boot/initramfs-0-rescue-fd247ce0ebe345f6b569283b6eda528b.img
}
# Ubuntu 24.04.1 LTS section
menuentry 'Ubuntu 24.04.1 LTS (6.8.0-51-generic)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-advanced-f6be26a7-9f80-4334-8de3-3c80282de363' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root f6be26a7-9f80-4334-8de3-3c80282de363
linux /boot/vmlinuz-6.8.0-51-generic root=UUID=f6be26a7-9f80-4334-8de3-3c80282de363 ro quiet splash $vt_handoff
initrd /boot/initrd.img-6.8.0-51-generic
}
menuentry 'Ubuntu 24.04.1 LTS (Recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-advanced-recovery-f6be26a7-9f80-4334-8de3-3c80282de363' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root f6be26a7-9f80-4334-8de3-3c80282de363
linux /boot/vmlinuz-6.8.0-51-generic root=UUID=f6be26a7-9f80-4334-8de3-3c80282de363 ro recovery nomodeset dis_ucode_ldr
initrd /boot/initrd.img-6.8.0-51-generic
}
# Ubuntu 24.04 section
menuentry 'Ubuntu 24.04 (6.11.0-13-generic)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-advanced-1eb5e7ee-43ad-40f6-b862-7986c8464659' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 1eb5e7ee-43ad-40f6-b862-7986c8464659
linux /boot/vmlinuz-6.11.0-13-generic root=UUID=1eb5e7ee-43ad-40f6-b862-7986c8464659 ro quiet splash crashkernel=2G-4G:320M,4G-32G:512M,32G-64G:1024M,64G-128G:2048M,128G-:4096M $vt_handoff
initrd /boot/initrd.img-6.11.0-13-generic
}
menuentry 'Ubuntu 24.04 (Recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-advanced-recovery-1eb5e7ee-43ad-40f6-b862-7986c8464659' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 1eb5e7ee-43ad-40f6-b862-7986c8464659
linux /boot/vmlinuz-6.11.0-13-generic root=UUID=1eb5e7ee-43ad-40f6-b862-7986c8464659 ro recovery nomodeset dis_ucode_ldr
initrd /boot/initrd.img-6.11.0-13-generic
}
}
# System Tools Submenu
submenu 'System Tools' {
menuentry 'Memory Test (memtest86+)' --class memtest $menuentry_id_option 'memtest86+' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root f6be26a7-9f80-4334-8de3-3c80282de363
linux /boot/memtest86+x64.efi
}
menuentry 'Memory Test (Serial Console)' --class memtest $menuentry_id_option 'memtest86+-serial' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root f6be26a7-9f80-4334-8de3-3c80282de363
linux /boot/memtest86+x64.efi console=ttyS0,115200
}
menuentry 'UEFI Firmware Settings' $menuentry_id_option 'uefi-firmware' {
fwsetup
}
}
Of course, this is a rather manual process, and it will need to be run whenever the OS is updated, but it'll do for now.
If I find something better, I'll update.