How to Install GRUB2 with EFI Support

Purpose

This document outlines a reference procedure for installing the GRUB2 bootloader with UEFI support built by the Desktop factory to a boot device for the target board.

Prerequisitesubuntu 14.04

  • x86 Host Machine
    • Note: For 64-bit targets, you must have a 64-bit host OS.
  • x86 Target Machine
  • Desktop Factory with the following options enabled:
    • bash (needed for chroot)
    • grep (needed for grub-install)
    • ncurses
    • Target Software->Kernel->Include kernel image within the RFS

Procedure

Build GRUB2 with EFI Support

  1. Open your Desktop Factory configuration interface:
    $ make menuconfig
  2. Navigate to "Target Software->Bootloaders->GRUB2..."
  3. Set "grub2 Platform String" to efi.
  4. Save your configuration.
  5. Remove your existing grub2 build:
    $ make grub2-distclean
  6. Rebuild the Factory:
    $ make

Attach volume to host machine

If you are using USB stick, Compact Flash, SD, or hard drive, connect it to the host. Determine the device node using dmesg, and unmount any volumes that auto-mount. For the purposes of this document, we assume /dev/sdX is the name of the disk.

Create a Linux partition on the device for the RFS.

  1. Create a Linux partition for the RFS using fdisk.
    $ sudo fdisk /dev/sdX
    • Type o to create a new partition table.
    • Create the EFI Partition.
      • Type n to create a new partition.
      • Type p to select a primary partition type.
      • Set Partition Number to 1.
      • Accept the default value for First Sector.
      • Set the Last Sector to +50M.
      • Type t to set the partition type.
      • Type c to set the partition type to W95 FAT32 (LBA).
    • Create the RFS Partition.
      • Type n to create a new partition.
      • Type p to select a primary partition type.
      • Set Partition Number to 2.
      • Accept the defaults for First Sector and Last Sector.
    • Type w to write the new partition table.
  2. Format the EFI partition using mkfs.vfat:
    $ sudo mkfs.vfat -n "efi" /dev/sdX1
  3. Format the RFS partition using mkfs.ext4:
    $ sudo mkfs.ext4 -L "rfs" /dev/sdX2

Initialize the RFS contents

  1. Mount the volume:
    $ sudo mount -t ext4 /dev/sdX2 /rfs
  2. Change to the mounted directory:
    $ cd /rfs
  3. As root, extract the RFS output tarball into the mount point:
    $ sudo tar -xf /path/to/factory/build_*/images/rfs/rootfs.tar.gz
  4. Copy the kernel to the volume and rename it to vmlinuz for autodetection:
    $ sudo mkdir -p boot/
    $ sudo cp /path/to/factory/build_*/images/bzImage-<version> boot/vmlinuz-<version>
    

Set up the EFI Partition

We will be doing most of the EFI setup from within our chroot. However, we still need to mount the volume:

$ sudo mount /dev/sdX1 /efi

Use chroot to install grub to the device

  1. Enter the RFS directory:
    $ cd /rfs
  2. Create the EFI directory under boot:
    $ sudo mkdir -p boot/efi
  3. Bind mount the /dev, /sys, and /proc directories. This allows you to access your system from the chroot jail.
    $ sudo mount --bind /dev ./dev
    $ sudo mount --bind /sys ./sys
    $ sudo mount --bind /proc ./proc
    
  4. Bind mount the your EFI mount point directory:
    $ sudo mount --bind /efi ./boot/efi
  5. Enter the chroot jail:
    32-bit Target
    $ sudo linux32 chroot .

    64-bit Target
    $ sudo linux64 chroot .
  6. Install grub to your Linux partition
    32-bit Target
    # grub-install --target=i386-efi --efi-directory=/boot/efi --removable --boot-directory=/boot/efi/EFI --bootloader-id=grub /dev/sdX

    64-bit Target
    # grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --boot-directory=/boot/efi/EFI --bootloader-id=grub /dev/sdX

The --removable flag prevents the efibootmgr application from running on your host machine if it is present in your filesystem. Otherwise, it would modify your host's EFI partition, which is undesirable.

Create a grub.cfg file under /boot/grub

Note: In order for grub-mkconfig to automatically detect the kernel image, it must be named renamed vmlinuz-<version>. Otherwise you can manually add the menuentry pointing to bzImage.

# grub-mkconfig -o /boot/efi/EFI/grub/grub.cfg

Exit chroot and unmount dev, sys, and proc

# exit
$ sudo umount ./dev
$ sudo umount ./sys
$ sudo umount ./proc
$ sudo umount ./boot/efi

Cleanup grub.cfg

At this point you should edit the grub.cfg to set the root device correctly. Due to the bind mounted device tree, the generated grub.cfg will specify the wrong root.

  1. Open the grub.cfg file, found at /efi/EFI/grub/grub.cfg.
  2. Find the line that sets the root device, typically in the menuentry option:
    set root='hd1,msdos2'
  3. Edit the line to correspond with the second partition of the first hard drive:
    set root='hd0,msdos2'
  4. If you are not using an initrd, you should modify your kernel command line options to boot from the proper device. You may also add additional boot arguments to your kernel command line.
    linux   /boot/vmlinuz-<version> root=/dev/sda2 rw video=vesafb vga=0x318 vmalloc=256MB console=ttyS0,115200 net.ifnames=0

(OPTIONAL) Add a manual menu-entry to grub.cfg

If your kernel was not autodetected, or if you simply want to add an additional boot entry, you can manually edit the grub.cfg.

  1. Enter an entry like this between the 10_linux lines:
    ### BEGIN /etc/grub.d/10_linux ###
    menuentry 'Timesys' --class gnu-linux --class gnu --class os {
       linux /bzImage <kernel-arguments>
    }
    ### END /etc/grub.d/10_linux ###
    

Create startup.nsh script

The EFI shell runs a script at boot time called startup.nsh. We should create this file in the root of the /efi partition

  1. Create the file startup.nsh and open it in your editor of choice.
  2. Add the following contents to it:
    For 32-bit targets
    FS0:
    cd \efi\grub
    boot.efi
    

    For 64-bit targets
    FS0:
    cd \efi\grub\x86_64-efi
    grub.efi
    
  3. Copy the file to the root of your /efi partition:
    $ sudo cp startup.nsh /efi/

Clean up

  1. Leave the RFS partition:
    $ cd /
  2. Unmount the RFS partition:
    $ sudo umount /rfs
  3. Unmount the EFI partition:
    $ sudo umount /efi
  4. Remove the hard drive from your system, and attach it to the target system.