I’ve been playing around with Grub2 a bit while trying to learn something about UEFI. Long story short: I’m really interested in the current measurement gap between the UEFI environment and the Linux OS. Before I go too far into those details I just wanted to get a quick note up on building Grub2 for a UEFI target, specifcally the x86_64 (aka amd64) target.
WARNING: This post won’t help you get Linux or any other OS booted from Grub. I’m only covering the steps necessary to build Grub for the
efi target and setting up a UEFI boot disk to run it. That’s all.
Doing the actual build is the easy part. First things first: get the source from the GNU project website. Then RTFM in the INSTALL file. For my setup, specifying
--with-platform. The build goes something like this:
./autogen.sh ./configure --target=x86_64 --with-platform=efi make
UEFI bootable USB
UEFI is a huge departure from the legacy BIOS world. To keep this post short we won’t spend any time discussing BIOS but if you’ve ever wrestled with fixing a broken grub-pc install you’ll know what I mean. Installing or fixing a UEFI Grub system by comparison is pretty simple:
- Format a disk (USB in my case) with a GPT using your favorite partitioning tool.
- Create a partition with the appropriate type for UEFI. Unfortunately the name for this type is different depending on which partitioning tool you’re using. I use fdisk and the type is ‘EFI System’. It doesn’t need to be large, just enough to hold the grub executable. I made mine 100M which is extremely large. Make note of the UUID of the partition.
- Format the partition as FAT16 or FAT32.
Create the UEFI Executable
This builds everything but we don’t yet have a UEFI executable. To create one you’ll need to run the
grub-mkimage utility. This is the root of the build directory and it’s responsible for stitching the grub kernel, core modules and a builtin config to bootstrap grub. Generally you’d only build the minimally necessary grub modules into the UEFI executable but since this is for my debug setup I’m going to build every module in. This makes the executable very large but it’s easier.
Before we can do this we need to grok the idea of the builtin config. Grub needs a hint when it comes to setting up the system. For my purposes all I care about is getting the FAT partition mounted so my config just tells Grub how to find the
root partition and what to set as the
( cat < grub-buildin.cfg
--fs-uuid is pretty self explanitory. The UUID in the above code fragment isn’t the actual UUID of my UEFI system partition. If you’re following along at home you can get the UUID for the FS you created above using the
blkid utility. The
prefix tells Grub where to look for the
grub.cfg configuration. We won’t even supply one here so the system will boot straight to the grub shell.
Since we’ve specified the prefix in the builtin config the
-p option below is useless but
grub-mkimage throws an error if it’s not provided. The value we pass is obviously wrong but the image produced will still function.
Finally execute the command:
./grub-mkimage -d ./grub-core -o bootx64.efi -O x86_64-efi -c ./grub-buildin.cfg -p /foobar $(find . -name '*.mod' | tr 'n' ' ' | sed -e 's/.mod//g')
Setup the Boot Disk
By default UEFI firmware will look for the bootloader on a disk at
/EFI/BOOT/bootx64.efi. So all we need to do is mount our EFI system partition, create the
/EFI/BOOT directory and copy the bootx64.efi executable there. In the above command we built in ever module that Grub has so we don’t need to worry about copying over any modules.
And that should do it. Point your system to this USB device, boot up the grub efi executable and you should go straight to a Grub shell. If you want to get an OS up and running on top of this you’ve still got a long way to go. But if you’re like me and just screwing around with grub (more on this later) then you’ve got a pretty good test environment. Now all you need to do is write a quick config to get Grub logging to some serial hardware and crank up the debug output!