UEFI and MBR bootable

I took the external drive, where I had installed opensuse 13.1 in UEFI mode, and decided to try to make it also bootable in MBR mode.  In principle, that would allow the drive to be bootable on both newer UEFI boxes and older MBR based boxes.

This post describes what I did and how it turned out.

I’ll note that this is mostly an exercise to see what is possible.  I don’t have any actual need for it.

An overview

My basic plan was to use Yast bootloader settings to switch from “grub2-efi” (used on UEFI systems), to plain “grub2” (used with legacy MBR based booting).  There is not a lot of overlap in the files used by these boot loaders, so this plan should work provided that switching to “grub2” does not delete the files needed for “grub2-efi” to work.  Most of the files for “grub2-efi” are stored in either the EFI partition (mounted as “/boot/efi”), or are in the directory “/boot/grub2/x86_64-efi”.  The files for plain “grub2” are in the directory “/boot/grub2/i386-pc”, with some additional code in either the MBR of the disk or in the partition boot record.  Additionally, “grub2” might install some code in the space between the MBR and the first partition.

For those not familiar with the term, the MBR (or main boot record) is the first physical sector on the hard drive, and contains the traditional partition table.  For use on UEFI, it is normal to instead use GPT partitioning, which leaves a dummy MBR available, and known as the protective MBR.

Booting the external drive

I chose to boot the external drive on an older MBR box.  I already had opensuse installed on the internal drive, so I modified the boot menu of that to boot the external drive.  I could not directly boot, since at this stage the external drive was set for UEFI booting only.

There are two alternatives to this.  I could have just booted it on my UEFI box.  If I had told Yast to switch to grub2, it probably would have complained that grub2 was not appropriate.  But it would have gone ahead and made the switch anyway.  Alternatively, I could have gone to rescue mode, booting a live CD and mounting the partitions from the external drive on “/mnt”.  And then I could have installed grub2 in rescue mode (see my post on rescuing).

Where to install grub2?

Before attempting to install, I had to decide where to install.  I could either install on the MBR or on one of the partitions.  However, both of those have problems.  Traditional MBR booting assumes a traditional MBR partitioned disk.  But this disk had GPT partitioning.  To install in a partition, I would need a hybrid partitioning scheme where one of the partitions is listed in both the GPT partition table and the protective MBR.  The “gdisk” command can setup such hybrid partitioning.  You need only identify which partition is to be listed in both partition tables.

Installing grub2 in the MBR raises a different issue.  In a typical MBR partitioning scheme, there is unassigned and unusable space between the MBR and the first partition.  And grub2 puts some code there.  On a GPT partitioned disk, that space is usable.  The grub2 software can cope with that, by using a special boot partition, called a BIOS boot partition.  The special partition can fit in the space before the first standard partition, usually starting at sector 34.

I chose to go with installing in the MBR.  So I created that BIOS boot partition, using the “gdisk” command.  Here’s the resulting partition table:

# gdisk -l /dev/sdb
GPT fdisk (gdisk) version 0.8.7

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdb: 156301488 sectors, 74.5 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 50D37558-962E-42F6-A846-9414ECC0F510
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 156301454
Partitions will be aligned on 2-sector boundaries
Total free space is 143 sectors (71.5 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          321535   156.0 MiB   EF00  primary
   2          321536         4530175   2.0 GiB     8200  primary
   3         4530176        46475263   20.0 GiB    8300  primary
   4        46475264       156301311   52.4 GiB    8300  primary
   5              34            2047   1007.0 KiB  EF02  BIOS boot partition

With that in place, I was ready to proceed with the grub2 install.

Installing grub2

Next, I started Yast –> System –> Boot Loader

On the first screen, I made sure that “Boot from Master Boot Record” was the only box checked.

Next, I clicked on “Boot Loader Installation Details” toward the bottom of the screen.  That listed two disks.  I made sure that the external drive came first in that list.  That is necessary, since that’s the MBR on which I wanted grub2 to be installed.

Finally, I clicked the OK button, and grub2 was installed.  A quick check showed that the important files for grub2-efi were still there.

Testing

The next step was to attempt to boot the external drive in MBR mode, and then in UEFI mode.  I first tested MBR mode on my older non-UEFI box.  And that worked fine.

At this stage, I did not test on my UEFI box.  I expect that it would have worked, but only if I had disabled secure-boot.

Configuring for secure-boot

To be used with secure-boot, the “grub.cfg” needs to use a “linuxefi” command instead of a “linux” command, and it needs to append an “initrdefi” command rather than an “initrd” command.  However, that would be incompatible with MBR booting requirements.

This is fairly easy to fix.  I simply created an alternative “grub.cfg” file, which I named “grub.altcfg”.  I edited that to use “linuxefi” and “initrdefi” on the appropriate lines.

Next, I moved to the directory “/boot/efi/EFI/Boot” which is where the UEFI booting starts out.  There’s a small “grub.cfg” file there.  It contained the line:

configfile $prefix/grub.cfg

which I promptly changed to read

configfile $prefix/grub.altcfg

I was now set for testing a UEFI boot.  And that worked as expected.

Maintenance

Whenever “grub.cfg” (in the directory “/boot/grub2”) is updated, I will now need to regenerate “grub2.altcfg”.  I wrote a small “sed” script for that purpose.

/^[     ]*linux[        ]/s/linux/linuxefi/
/^[     ]*initrd[       ]/s/initrd/initrdefi/

Note that those brackets contain a space followed by a tab character (the tab does not show on the blog).

So now, I just run

# cd /boot/grub2
# sed -f /usr/local/lib/grubefi.sed grub.cfg > grub.altcfg

whenever “grub.cfg” needs updating.  And it will need updating after any kernel update.

Advertisements

Tags: ,

About Neil Rickert

Mathematician and computer scientist who dabbles in cognitive science.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: