Libreboot on an X60, Part III: Modify the Boot Menu

In the first article of this series, I explained the fundamentals behind the Libreboot free software BIOS project and why you might want to replace your BIOS with it. I followed up by describing how to install Libreboot on a ThinkPad X60. In this final article of the series, I explain how to perform one major task that so far I've left unexplained: how to modify the default GRUB boot menu.

A traditional BIOS provides users with a configuration menu where they can change boot orders and enable and disable devices. Typically there is an assigned key on the keyboard (Esc, F11 and F12 are common), so that you can select your boot device instead of going with the default order. With Libreboot, all of the device settings are set inside the ROM itself, and you use a GRUB menu to select a boot device.

The existing GRUB menu provides a number of common boot options that hopefully should work on your system. The default menu item attempts to boot off the first partition, and after that, there are options to boot removable devices and finally an option to search for and load any local GRUB configuration that might be on a hard drive. Ideally this default menu would be sufficient, but there are some cases (such as booting the Tails USB disk) that might require some tweaks.

On the one hand, if you are familiar with GRUB commands, you can boot more less any device you want on the fly with the right incantation. On the other hand, it can be a pain to type in GRUB commands every time you want to boot something, so if you find yourself tweaking the default menu items to boot a special device, you probably will want to modify the GRUB menu more permanently.

What I suggest is that you experiment with sample GRUB configuration changes directly from the GRUB boot menu, because it allows you to edit the configuration of any menu item directly. This way, you quickly can test any sample changes without having to go through the full process of writing to and flashing a new ROM. Once you know what changes you'd like to make, you are ready to move on to make them permanent.

The Setup

If you have followed the previous two articles in this series, you already should have downloaded and validated the Libreboot binary and installed Libreboot on an X60. Let's pick up from that point by opening a terminal and changing to the libreboot_bin directory that contains all of the Libreboot binaries, ROMs and supporting scripts. You already should have installed the Libreboot build dependencies when setting up Libreboot, but if not, first run either the deps-trisquel or deps-parabola script if you are on a Debian-based or Arch-based distribution, respectively. If you are using another distribution, inspect the packages those scripts install and map them to the package names for your distribution.

The Libreboot ROM actually contains a small filesystem called CBFS, so to edit it, you need to install the cbfstool binary. Within the libreboot_bin directory is a script called builddeps-cbfstool, so run that script, and you should see a cbfstool and rmodtool binary appear under libreboot_bin:


$ ./builddeps-cbfstool

Modify the ROM

Once cbfstool is installed, the next step is to choose the ROM to modify so you can view the files within it and extract a copy of the GRUB configuration. For this example, I'm going to use one of the ROMs provided by Libreboot itself. First, run cbfstool along with the path to the ROM and the print argument. The print argument will then list all of the files within the ROM:


$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb.rom print
libreboot_usqwerty_vesafb.rom: 2048 kB, bootblocksize 1424,
 ↪romsize 2097152, offset 0x0
alignment: 64 bytes, architecture: x86

Name                        Offset     Type         Size
cmos_layout.bin             0x0        cmos_layout  1788
cmos.default                0x740      cmos_default 256
fallback/romstage           0x880      stage        50924
fallback/ramstage           0xcfc0     stage        81695
fallback/payload            0x20f40    payload      541644
etc/ps2-keyboard-spinup     0xa5380    raw          8
config                      0xa53c0    raw          4504
background.jpg              0xa6580    raw          67907
dejavusansmono.pf2          0xb6f00    raw          100513
grub.cfg                    0xcf800    raw          1637
grubtest.cfg                0xcfec0    raw          1629
(empty)                     0xd0580    null         1242264

As you can see, there are two different GRUB config files: grub.cfg and grubtest.cfg. The former is the default GRUB config that is loaded, and the second can be loaded by the first for testing new configs. The fact is, if you make some major mistake in your GRUB config, you potentially could lock yourself out of booting your system (or at least make it very difficult), so it's important to validate your changes in a safe way. The recommended workflow is to modify grubtest.cfg first, update the ROM and flash your BIOS with it, then select the option in the GRUB menu to load grubtest.cfg. Then you can validate that your config works before you copy the same change to grub.cfg.

With that in mind, start by extracting the grubtest.cfg file using cbfstool:


$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb.rom extract
 ↪-n grubtest.cfg -f grubtest.cfg

Here, instead of print, you are passing the extract command to cbfstool along with two new arguments. The -n option specifies the name of the file within the CBFS filesystem to extract, and the -f option specifies what to name the copy of the file on the local filesystem. Since the grub.cfg file references this specific filename, it's best to keep it the same.

The grubtest.cfg will contain a number of GRUB settings at the top of the file, but the more interesting settings will be found down in the menuentry sections:


menuentry 'Load Operating System' {
        set root='ahci0,msdos1'
        linux  /vmlinuz root=/dev/sda1
        initrd /initrd.img
}
menuentry 'Parse ISOLINUX menu (USB)' {
        set root='usb0'
        syslinux_configfile -i (usb0)/isolinux/isolinux.cfg
}
menuentry 'Parse ISOLINUX menu (CD)' {
        set root='ata0'
        syslinux_configfile -i (ata0)/isolinux/isolinux.cfg
}

For instance, the above three sections are for menu items to boot a Linux kernel from the first disk, a USB disk and a CD, respectively. If you find, for example, that your root partition isn't /dev/sda1 but instead /dev/sda2, you would edit the first menuentry section to reflect that. In my case, I noticed that the Tails live USB disks created prior to version 1.3 required a special set of boot options. After some experimentation, I came up with the following addition for GRUB:


menuentry 'Tails (USB)' {
        set root='usb0,gpt1'
        syslinux_configfile -i (usb0,gpt1)/syslinux/live486.cfg
}

A Quick Warning

Once you have made changes, it's time to copy the modified grubtest.cfg to your ROM. If you are using one of the standard Libreboot ROMs, I recommend first making a copy for your changes. This is important, because the default Libreboot ROMs are created with particular sections of the ROM blank to work well with initial flashing. I've personally bricked an X60 by attempting the first flash with one of my custom ROMs, so it's worth keeping the original ROMs intact:


$ cp bin/x60/libreboot_usqwerty_vesafb.rom
bin/x60/libreboot_usqwerty_vesafb-custom.rom

Now remove the old grubtest.cfg from your custom ROM and use the print command to confirm that it no longer exists:


$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom remove
 ↪-n grubtest.cfg
$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom print
libreboot_usqwerty_vesafb-custom.rom: 2048 kB,
 ↪bootblocksize 1424, romsize 2097152, offset 0x0
alignment: 64 bytes, architecture: x86

Name                        Offset     Type         Size
cmos_layout.bin             0x0        cmos_layout  1788
cmos.default                0x740      cmos_default 256
fallback/romstage           0x880      stage        50924
fallback/ramstage           0xcfc0     stage        81695
fallback/payload            0x20f40    payload      541644
etc/ps2-keyboard-spinup     0xa5380    raw          8
config                      0xa53c0    raw          4504
background.jpg              0xa6580    raw          67907
dejavusansmono.pf2          0xb6f00    raw          100513
grub.cfg                    0xcf800    raw          1637
(empty)                     0xcfec0    deleted      1688
(empty)                     0xd0580    null         1242264

Now you are ready to add your custom version and use the print command to confirm it exists:


$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom
 ↪add -n grubtest.cfg -f grubtest.cfg -t raw
$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom print
libreboot_usqwerty_vesafb-custom.rom: 2048 kB, bootblocksize 1424,
 ↪romsize 2097152, offset 0x0
alignment: 64 bytes, architecture: x86

Name                        Offset     Type         Size
cmos_layout.bin             0x0        cmos_layout  1788
cmos.default                0x740      cmos_default 256
fallback/romstage           0x880      stage        50924
fallback/ramstage           0xcfc0     stage        81695
fallback/payload            0x20f40    payload      541644
etc/ps2-keyboard-spinup     0xa5380    raw          8
config                      0xa53c0    raw          4504
background.jpg              0xa6580    raw          67907
dejavusansmono.pf2          0xb6f00    raw          100513
grub.cfg                    0xcf800    raw          1637
grubtest.cfg                0xcfec0    raw          1629
(empty)                     0xd0580    null         1242264

Flash the BIOS
Now you can flash your BIOS with the modified ROM. You can use
the flashrom utility that's included inside your Libreboot binary
directory. If you are running this from the same system you used to
install Libreboot in the first place, you already should have flashrom
built and available. Otherwise, if you are running this from a system
like Tails, or if you haven't yet installed flashrom, first run the
builddeps-flashrom script from the base of the libreboot_bin directory
as root. When you are ready to flash your BIOS, make sure you are in
the libreboot_bin directory and run:


$ sudo ./flashrom/flashrom -p internal -w
bin/x60/libreboot_usqwerty_vesafb-custom.rom
flashrom v0.9.7-unknown on Linux 3.16.0-4-586 (i686)
flashrom is free software, get the source code at
 ↪http://www.flashrom.org

Calibrating delay loop... delay loop is unreliable, trying
 ↪to continue OK.
coreboot table found at 0xcf6bd000.
Found chipset "Intel ICH7M". Enabling flash write... OK.
Found Macronix flash chip "MX25L1605D/MX25L1608D/MX25L1673E"
 ↪(2048 kB, SPI) mapped at physical address 0xffe00000.
Reading old flash chip contents... done.
Erasing and writing flash chip...
Erase/write done.

Of course, replace the above ROM with the full path to your custom ROM. Once the flash succeeds, reboot your machine and at the boot menu, select the menu item that switches you to your custom grubtest.cfg. You then should see whatever changes you made, and you can attempt to boot from them. If everything works as expected, you are ready to make it the default. If not, especially if your changes made GRUB not work at all, just be glad it's the test file. You've been given a second chance to iterate through grubtest.cfg until it does work, and then you can move on.

Warning: make sure before you move on from here that you have completely tested your grubtest.cfg changes and everything works as expected.

Edit the Default Menu

Boot back in to your system and go back to your working directory. Since grubtest.cfg works, the next step is to create a copy of it named grub.cfg that you will use as the default GRUB config. The official Libreboot documentation for the GRUB menu lists this following sed script that will do all of the work of creating a grub.cfg based on your grubtest.cfg, but it will change the menu entries to make sure they still reference grubtest.cfg and grub.cfg where appropriate (be sure to run this in the directory that contains your custom grubtest.cfg):


$ sed -e 's:(cbfsdisk)/grub.cfg:(cbfsdisk)/grubtest.cfg:g'
 ↪-e 's:Switch to grub.cfg:Switch to grubtest.cfg:g'
 ↪< grubtest.cfg > grub.cfg

Now you can repeat the steps you performed to delete and re-add grubtest.cfg from the ROM, only this time with grub.cfg:


$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom
 ↪remove -n grub.cfg
$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom print
$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom add
 ↪-n grub.cfg -f grub.cfg -t raw
$ ./cbfstool bin/x60/libreboot_usqwerty_vesafb-custom.rom print

Confirm that grub.cfg has been added properly to your ROM, and then flash your BIOS with the new custom ROM:


$ sudo ./flashrom/flashrom -p internal -w
bin/x60/libreboot_usqwerty_vesafb-custom.rom

Once you reboot, you should be able to use your new modified GRUB menu. Just be sure to take the extra steps of validating changes with grubtest.cfg first each time you do this—you wouldn't want to get locked out of your system!

Kyle Rankin is a Tech Editor and columnist at Linux Journal and the Chief Security Officer at Purism. He is the author of Linux Hardening in Hostile Networks, DevOps Troubleshooting, The Official Ubuntu Server Book, Knoppix Hacks, Knoppix Pocket Reference, Linux Multimedia Hacks and Ubuntu Hacks, and also a contributor to a number of other O'Reilly books. Rankin speaks frequently on security and open-source software including at BsidesLV, O'Reilly Security Conference, OSCON, SCALE, CactusCon, Linux World Expo and Penguicon. You can follow him at @kylerankin.

Load Disqus comments