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.
Build
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 --target
and --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 prefix
.
( cat < grub-buildin.cfg
The --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!
very useful!
How do I build a bootx64.efi for use with shimx64.efi so that it will secure boot?
I can use shim with other grubx64.efi files from ubuntu, etc., but if I build my own, then it will not secure boot. Is there some checksum/hash/verification step missing?
LikeLike
I dunno much about secure boot unfortunately. AFAIK if you’re building your own grub images you’ll have to sign them with a key that’s in the secure boot key database. Teddy Reed over at http://prosauce.org has a few good secure boot writeups. I’d start there first.
LikeLike
There is a way to build Grub2 ‘static linking’ some external modules, like http instead of generating the mod file?
LikeLike
AFAIK grub will always generate the mod files. The command I provide above using grub-mkimage will build in whatever modules you like. My example uses a little shell pipeline to build in all mod files but you can selectively include whatever you like.
LikeLike
Thank you, in fact just after I hit the Post Coment button I undestood the process of “embedding” the mod files. I’m using the command below to generate the efi module to be used on PXE boots with the modules I need:
./grub-mkimage -O x86_64-efi -o grubx64.efi -p /EFI/BOOT -d grub-core all_video boot cat chain configfile echo efifwsetup efinet ext2 fat font gzio halt loadenv loopback lvm minicmd normal part_msdos part_gpt reboot search search_fs_uuid search_fs_file search_label sleep syslinuxcfg test tftp http video xfs backtrace usb linuxefi
I found easier than using grub-buildin.cfg response file for my needs.
Thank you!
LikeLike