GRUB2 EFI Support

Most GNU/Linux distributions utilize GNU GRUB (GRand Unified Bootloader) to boot the Linux kernel. GRUB was conceived and initially developed by Erich Boleyn. It became part of the GNU project in 1999 but was never officially released. The last development snapshot was v0.97 which was released in 2005. In spite of the fact that it was never officially released, it is used on millions of computers worldwide.

The v0.97 codebase is now known as GRUB Legacy. Supposedly it is still in maintenance mode but no fixes have occurred in the last few years. As a result, vendors such as Red Hat maintain their own branches of GRUB Legacy. Currently the Fedora Project applies 25 patches to the v0.97 release of which 7 or 8 patches relate to the addition of EFI support. Other forks of GRUB Legacy include the bootloader used by Oracle to boot Solaris on IA32 and X64 platforms.

Many years ago the GRUB developers decided that a complete rewrite was required. The goal was to make a more robust, portable and powerful version of GRUB which has support for a scripting language, internationalization and localization. GRUB 2 is derived from PUPA which was a research project funded by the Information-technology Promotion Agency of Japan to investigate the next generation of GRUB.

For some time, I have been EFI-booting one of my X64 systems which runs Fedora 12 and experimenting with the TianoCore EFI shell and various EFI utilities. The system boots into an EFI shell and I then use Fedora’s EFI-enabled version of GRUB Legacy to boot into various Fedora kernels.

Recently I decided to download the latest version of the GRUB 2 sources (currently v1.98), build GRUB2 as an X64 EFI image and install it in parallel with the GRUB Legacy bootloader on the ESP (EFI System Partition) so that I could experiment with the new facilities available in the GRUB2 bootloader.

Building the EFI version of GRUB2 was not an issue. As usual, the first requirement is to configure the build. Here is a copy of the configuration options I choose:

 $ ./configure --with-platform=efi --enable-grub-fstest=no 
--enable-grub-mkfont=no --disable-nls --enable-efiemu=no

As you can see I disabled EFI emulation, NLS support and various debugging utilities (fstest and mkfont.) The Makefile includes ../conf/ which is where most of the build is specified. The default Makefile has a dependency on Ruby but this is easily worked around. The header ../include/grub/i18n also has a minor error. The fix is to change the syntax of the ENABLE_NLS guard from #if to #ifdef.

After you have successfully run make, you have to generate the grub.efi image using the grub-mkimage utility.

$ ./grub-mkimage -d . -o grub.efi boot linux part_gpt  \ 
   fat ext2 normal sh configfile lspci ls reboot datetime 
   loadenv search lvm help quit

GRUB2 is a very modular bootloader. You can include whichever modules you need into the image using grub-mkimage or you can load them from the GRUB2 shell. The boot module is mandatory, the linux module is the loader for Linux images, the help module displays help information, and the remaining modules, except for quit are explained in Erich Boleyn’s commands summary page.

Here is a list of the modules which were built:

acpi.mod          efi_gop.mod          gcry_whirlpool.mod  mmap.mod             scsi.mod
affs.mod          efi_uga.mod          gettext.mod         msdospart.mod        search_fs_file.mod
afs_be.mod        elf.mod              gfxmenu.mod         multiboot2.mod       search_fs_uuid.mod
afs.mod           ext2.mod             gfxterm.mod         multiboot.mod        search_label.mod
appleldr.mod      extcmd.mod           gptsync.mod         normal.mod           search.mod
ata.mod           fat.mod              gzio.mod            ntfscomp.mod         setjmp.mod
at_keyboard.mod   fixvideo.mod         halt.mod            ntfs.mod             setpci.mod
befs_be.mod       font.mod             handler.mod         part_acorn.mod       sfs.mod
befs.mod          fshelp.mod           hashsum.mod         part_amiga.mod       sh.mod
bitmap.mod        functional_test.mod  help.mod            part_apple.mod       sleep.mod
bitmap_scale.mod  gcry_arcfour.mod     hexdump.mod         part_gpt.mod         tar.mod
blocklist.mod     gcry_blowfish.mod    hfs.mod             part_msdos.mod       terminal.mod
boot.mod          gcry_camellia.mod    hfsplus.mod         part_sun.mod         terminfo.mod
bufio.mod         gcry_cast5.mod       iso9660.mod         parttool.mod         test.mod
cat.mod           gcry_crc.mod         jfs.mod             password.mod         tga.mod
chain.mod         gcry_des.mod         jpeg.mod            password_pbkdf2.mod  trig.mod
charset.mod       gcry_md4.mod         keystatus.mod       pbkdf2.mod           true.mod
cmp.mod           gcry_md5.mod         linux.mod           pci.mod              udf.mod
configfile.mod    gcry_rfc2268.mod     loadbios.mod        png.mod              ufs1.mod
cpio.mod          gcry_rijndael.mod    loadenv.mod         probe.mod            ufs2.mod
cpuid.mod         gcry_rmd160.mod      loopback.mod        quit.mod             vga_text.mod
crc.mod           gcry_seed.mod        ls.mod              raid5rec.mod         video_fb.mod
crypto.mod        gcry_serpent.mod     lspci.mod           raid6rec.mod         video.mod
datehook.mod      gcry_sha1.mod        lvm.mod             raid.mod             videotest.mod
date.mod          gcry_sha256.mod      mdraid.mod          read.mod             xfs.mod
datetime.mod      gcry_sha512.mod      memrw.mod           reboot.mod           xnu.mod
dm_nv.mod         gcry_tiger.mod       minicmd.mod         reiserfs.mod         xnu_uuid.mod
echo.mod          gcry_twofish.mod     minix.mod           relocator.mod

After building the grub.efi image I moved it to its home on the ESP and created a very basic GRUB2 configuration file which must be called grub.cfg. Here is a copy of my initial grub.cfg

#  FPM 3/18/10 manual edit
set color_normal=yellow/blue
set color_highlight=blue/yellow

menuentry "Fedora 12" {
   linux /vmlinuz- ro root=/dev/mapper/vg_ultra-lv_root  LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us rhgb quiet nolapic
   initrd /initramfs-

Stanzas are phrased slightly differently and cannot be copied directly from a GRUB Legacy configuration file. They are pretty close though and no explanation of the required changes should be necessary. Note that the command kernel has been superseded by linux. Note also that GRUB2 partition numbers start at 1 not 0 but drives still start at 0. Thus the second partition of the first drive is hd(0,2) in GRUB2 but (hd0,1) in GRUB Legacy.

When I switched to GRUB2 to experiment using it to EFI-boot Fedora 12, I immediately noticed that the time from when I hit the Enter key until I saw a Plymouth theme went from about 10 second to over 3 minutes. It took me a while to track down the cause of this delay as the GRUB2 codebase is significantly larger and more complex than the GRUB Legacy codebase and EFI-related code is in numerous subdirectories.

After a number of false starts, I finally tracked the issue down to the size of the disk cache. See the caching algorithm in get_disk_read() in the source file ../kern/disk.c. I probably could have bypassed the disk cache altogether, but found that increasing the cache size from 8 sectors to 8192 sectors reduced the time to read the kernel and initrd image files to less than a few seconds. Here are the changes to make in ../include/grub/disk.h.

/* The size of a disk cache in sector units.  */
#define GRUB_DISK_CACHE_SIZE    8192    
#define GRUB_DISK_CACHE_BITS     13     
#define GRUB_DISK_CACHE_SIZE    8       
#define GRUB_DISK_CACHE_BITS    3       

Note that I used the guard GRUB_MACHINE_EFI to guard any EFI-specific code in common source files.

One useful feature of GRUB2 are the reboot and halt modules. You can use these modules to add menu options to either reboot or halt your system or you invoke these commands from the GRUB2 command line. For example. here is a reboot menu option.

menuentry "Reboot" {

Currently, no command is available to quit out of GRUB2 and go back to the EFI shell. For non-EFI platforms this is not a useful option but for EFI-based platforms it provides great flexibility. I therefore decided to develop such a command and used the source code for the reboot command as my template. In case anybody else wants to create such a GRUB2 command, here are the relevant details.

Create a file named quit.c in the ../commands subdirectory of your build tree which contains the following source code:

/*  quit.c - command to return to EFI shell */
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2005,2007,2008  Free Software Foundation, Inc.
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  GNU General Public License for more details.
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <>.

#include <grub/dl.h>
#include <grub/command.h>
#include <grub/misc.h>
#include <grub/i18n.h>
#include <grub/term.h>
#include <grub/efi/efi.h>
#include <grub/efi/api.h>
#include <grub/efi/console.h>

static grub_err_t
grub_cmd_quit (grub_command_t cmd __attribute__ ((unused)),
                 int argc __attribute__ ((unused)),
                 char **args __attribute__ ((unused)))

    grub_efi_simple_text_output_interface_t *o;
    static grub_uint8_t efi_console_stdcolor = 

    o = grub_efi_system_table->con_out;
    efi_call_2 (o->set_attributes, o, efi_console_stdcolor);
    efi_call_1 (o->clear_screen, o);

    grub_exit ();
    return 0;

static grub_command_t cmd;

    cmd = grub_register_command ("quit", grub_cmd_quit,
                      0, N_("Return to EFI shell."));

    grub_unregister_command (cmd);

The contents of this file should be self-explanatory to an experienced programmer. The two efi_calls invoke the appropriate EFI protocol calls to set the screen text-mode foreground and background colors and clear the screen.

Next add the following make directives to ../conf/ which is the EFI-specific Makefile:

# For quit.mod.
quit_mod_SOURCES = commands/quit.c

        rm -f quit.mod mod-quit.o mod-quit.c pre-quit.o quit_mod-commands_quit.o und-quit.lst

CLEAN_MODULE_TARGETS += clean-module-quit.mod.1

        rm -f def-quit.lst

CLEAN_MODULE_TARGETS += clean-module-quit.mod-symbol.1
DEFSYMFILES += def-quit.lst
        rm -f quit_mod-commands_quit.d

MOSTLYCLEAN_MODULE_TARGETS += mostlyclean-module-quit.mod.1
UNDSYMFILES += und-quit.lst

ifneq ($(TARGET_APPLE_CC),1)
quit.mod: pre-quit.o mod-quit.o $(TARGET_OBJ2ELF)
        -rm -f $@
        $(TARGET_CC) $(quit_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ pre-quit.o mod-quit.o
        if test ! -z "$(TARGET_OBJ2ELF)"; then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi
        $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@
quit.mod: pre-quit.o mod-quit.o $(TARGET_OBJ2ELF)
        -rm -f $@
        -rm -f $@.bin
        $(TARGET_CC) $(quit_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@.bin pre-quit.o mod-quit.o
        $(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -wd1106 -nu -nd $@.bin $@
        -rm -f $@.bin

pre-quit.o: $(quit_mod_DEPENDENCIES) quit_mod-commands_quit.o
        -rm -f $@
        $(TARGET_CC) $(quit_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ quit_mod-commands_quit.o

mod-quit.o: mod-quit.c
        $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -c -o $@ $<

mod-quit.c: $(builddir)/moddep.lst $(srcdir)/
        sh $(srcdir)/ 'quit' $< > $@ || (rm -f $@; exit 1)

ifneq ($(TARGET_APPLE_CC),1)
def-quit.lst: pre-quit.o
        $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 quit/' > $@
def-quit.lst: pre-quit.o
        $(NM) -g -P -p $< | grep -E '^[a-zA-Z0-9_]* [TDS]'  | sed 's/^\([^ ]*\).*/\1 quit/' > $@

und-quit.lst: pre-quit.o
        echo 'quit' > $@
        $(NM) -u -P -p $< | cut -f1 -d' ' >> $@

quit_mod-commands_quit.o: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -MD -c -o $@ $<
-include quit_mod-commands_quit.d

        rm -f cmd-quit_mod-commands_quit.lst fs-quit_mod-commands_quit.lst partmap-quit_mod-commands_quit.lst handler-quit_mod-commands_quit.lst parttool-quit_mod-commands_quit.lst video-quit_mod-commands_quit.lst terminal-quit_mod-commands_quit.lst

CLEAN_MODULE_TARGETS += clean-module-quit_mod-commands_quit-extra.1

COMMANDFILES += cmd-quit_mod-commands_quit.lst
FSFILES += fs-quit_mod-commands_quit.lst
PARTTOOLFILES += parttool-quit_mod-commands_quit.lst
PARTMAPFILES += partmap-quit_mod-commands_quit.lst
HANDLERFILES += handler-quit_mod-commands_quit.lst
TERMINALFILES += terminal-quit_mod-commands_quit.lst
VIDEOFILES += video-quit_mod-commands_quit.lst

cmd-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)

fs-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)

parttool-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)

partmap-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)

handler-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)

terminal-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)

video-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES)
        set -e;           $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)  $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $<      | sh $(srcdir)/ quit > $@ || (rm -f $@; exit 1)


You should now be able to build quit.mod by invoking make quit.mod from the root of your GRUB2 build tree. Then use the grub-mkimage utility to incorporate the quit module into your grub.efi image. After placing the newly created grub.efi image in the ESP, you should be able to use the quit command from the GRUB2 shell or via a stanza in grub.cfg.

The current linux loader does not reset the screen text-mode foreground and background colors to their defaults. For EFI shells these are yellow and black respectively. Instead the GRUB2 foreground and background colors propagate onto text outputted by the loader module. Here are the diffs to the loader file ../loader/i386/efi/linux.c to correct this problem and add informative messages similar to those outputted by GRUB Legacy.

> #include <grub/efi/console.h>
> #include <grub/i386/vga_common.h>
>   grub_printf("Trying to allocate %u pages for VMLINUZ\n",
>                 (unsigned) prot_mode_pages);
>   /* set color colors to EFI default */
>   grub_efi_simple_text_output_interface_t *o;
>   static grub_uint8_t efi_console_standard_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_YELLOW, GRUB_EFI_BACKGROUND_BLACK);
>   o = grub_efi_system_table->con_out;
>   efi_call_2 (o->set_attributes, o, efi_console_standard_color);
<   grub_printf ("   [Linux-bzImage, setup=0x%x, size=0x%x]\n",
>   grub_printf ("[Linux-EFI, setup=0x%x, size=0x%x]\n",

Once I had GRUB2 up and running, I decided to look at how to support a full-screen background image. This is one of the areas where GRUB Legacy has major limitations. It only supports a background image with a maximum of 16 colors and a maximum resolution of 640×480.

To test support for a background image in GRUB2, l took a Fedora 12 background image and converted it into TGA format using GIMP. Why TGA? No particular reason. I could have just as easily used JPEG or PNG formats. I placed this image in the ESP GRUB2 subdirectory.

Next I needed to obtain a suitable glyph (font) file for displaying characters when in graphics mode. GRUB2 uses PFF2 bitmap fonts. Rather than going to the trouble of creating my own PFF2 file, I simply extracted the font file unicode.pf2 from the Debian grub-common_1.98 package.

Finally, as shown below, I modified grub.cfg to load and configure the appropriate modules and display the background image.

# FPM 3/19/10
if loadfont /efi/grub2/unicode.pf2
   set gfxmode="1024x768x32"
   set gfxpayload=keep
   insmod gfxterm
   insmod efi_gop
   terminal_output gfxterm
   terminal gfxterm
backgound_image /efi/grub2/fedora.tga

set color_normal=yellow/blue
set color_highlight=blue/yellow

menuentry "Fedora 12" {
   linux /vmlinuz- ro root=/dev/mapper/vg_ultra-lv_root  LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us rhgb quiet nolapic
   initrd /initramfs-

menuentry "Reboot" {

menuentry "Exit to EFI Shell" {

You will notice that GRUB2 supports a rudimentary scripting language. Here I test that the font file /efi/grud2/unicode.pf2 is loaded before the gfxterm and efi_gop modules are loaded and configured. When I rebooted the system and invoked GRUB2 from the EFI shell, the specified Fedora backgound image covered the whole screen without a border and characters were correctly displayed.

Is GRUB2 a viable option for EFI-booting Fedora 12 at this time? Yes, provided that it is modified to minimize the load delay caused by the default small disk caches. Do I like GRUB2? Yes. Actually I love the flexibility and additional functionality it provides. Broadening the question further, is GRUB2 ready for primetime use and should Fedora switch to GRUB2 in the near future? The Ubuntu, Gentoo and Debian distributions of GNU/Linux certainly think so and their latest releases utilize it as the default bootloader. My answer to both questions is probably no. I personally think GRUB2 requires some work to harden the product – especially for EFI-booting. Other reasons include a lack of accurate, up-to-date and complete end-user documentation and an over-complex product. By over-complex product, I mean the large number of modules and other files. Many of the modules, for example minix.mod, are of no use to users of Fedora on an IA32 or X64 platform and should not be distributed as part of a Fedora RPM.

There is a GRUB2 RPM available for Fedora 12. However it contains all the available loadable modules irrespective of whether they are of any use to Fedora users. Worse still, it installs all the modules under /usr/lib/grub2/i386-pc/. Why not just put them under /usr/lib/grub2? What value does the extra i386-pc add? I am on an x86-64 platform; I do not expect to find modules that I need in a directory called i386-pc. Yes, I understand that the GRUB2 boatloader always uses 32-bit modules but that fact does not need to be embedded in a subdirectory name. In addition, two important files are missing from the RPM, i.e. the font files unicode.pf2 and ascii.pf2. Without these files, background images are not possible.

Should Fedora switch to using GRUB2 in the long term? Certainly. The extra features such as loadable modules, scripting and localization support are frankly very compelling.

6 comments to GRUB2 EFI Support

  • Thanks for the information. It’s nice to know I’m doing something right!

  • Nice to read your blog. I have “old” MacBook 2.1 and run smoothly Fedora 12 i386. I used netinst CD to set up the fedora, but netinst amd64 can not boot on the machine. The Fedora 12 boot fast (3 seconds) with built-in grub-efi, my machine is single boot fedora 12.

    On other disk I setup gentoo amd64. all installation went well but I faced anoying “long” booting before Grub “face” appear, about 40 seconds :=))

    Once I have time I will follow your tutorial on your blog. Thanks for appoint good clue


  • Keshav P R

    Nice article. I use EDK DUET UEFI64 (UEFI 2.1 x86_64) firmware booting from 1 GB USB pendrive ( I have both Windows 7 x64 Pro (UEFI-GPT) and Archlinux x86_64 booting using GRUB2 UEFI and GRUB2 BIOS (compile scripts – GRUB2 UEFI and GRUB2 BIOS ).

    You should actually mention all this in grub-devel mailing list ( for grub2 devs to take notice.

    I hve a problem booting Archlinux in GRUB2 UEFI x86_64. Before Archlinux I had Fedora 12 x86_64. When Fedora 13 released, I changed over to Arch for its rolling release system. Arch boots fine in GRUB2 BIOS, but in GRUB2 UEFI I get “VFS cannot open root device” and “append correct root= option” errors and a kernel panic with CAPS LOCK blinking and unresponsive keyboard and touchpad (bug-grub report and Archlinux bug report ).

    It seems like the initramfs is loaded in the memory but not correctly passed to Archlinux kernel. Fedora boots fine because of all required disk controller drivers and FS drivers built-in in the kernel. But in Archlinux, except the very basic requirements, all disk controller and FS support are compiled as external modules. So it is impossible to boot Archlinux without initramfs. Archlinux uses BSD style init framework.

    Do you know what is actually wrong? The culprit might be (grub2-source-dir)/loader/i386/efi/linux.c . Fedora’s grub-legacy grub.efi (extracted using p7zip from x86_64 rpm) boots Archlinux without any errors, so I guess the problem is with grub2. I am not a programmer but I like to tinker a lot. Any help would be appreciated. Thank you.

  • BVK Chaitanya

    Can you send your quit.mod source to grub-devel mailing list, if signing copyright assignment to FSF is okay?

  • thanks for this post….very helpfull…

    going to bookmark your site!!!!!!!