The GRUB Command Line

GRUB is the Linux boot loader, officially the "GRand Unified Bootloader." Its commonest use is as a means to boot Linux (or Linux side-by-side with Windows or FreeBSD or some other OS). Most people don't touch it at all: it gets updated automatically when their package manager installs a new kernel. Even if people do touch it, they think that GRUB consists of editing /etc/default/grub or possibly one of the files in /etc/grub.d/ and then running sudo update-grub afterward. The truth is that this is a massive obfuscation layer applied on a very complex system that has a great deal more utility than is obvious from these few steps.

Several (possibly) important assumptions here:

  • I'm working on x86 (both 32-bit and 64-bit), I don't know if this will work on other platforms
  • I'm not trying to boot kernels (a commoner use for the GRUB command line) - although I do eventually use it as an example. I'm doing this to learn the GRUB DSL ("Domain-Specific Language").
  • I've always used 'vfat' formatted USB sticks: this would probably work with other formats, but I haven't tested

I'm going to show how to set up a USB stick for use as bootable GRUB media so you can play with the GRUB command line. You could of course reboot your Linux computer and just press "c" in GRUB to go to the command line, but since my intent is to learn GRUB to control a bootable USB stick, I'm going to take this extra step.

What's the point? Any command that can be typed at the grub> prompt can also be used in a grub.cfg file, and I'm trying to create more sophisticated grub.cfg files for bootable USB sticks. Extensive but often opaque documentation is available here: https://www.gnu.org/software/grub/manual/grub/html_node/index.html#Top . Aside from that, example code has proven surprisingly hard to find. It's a complex DSL that's got a fair bit of power, and to learn it you need a playground.

As a user:

$ pmount sdb1

Be very sure that the USB stick is in fact /dev/sdb1, and if it's not, substitute the correct value here and in any following commands. If you're not using pmount to mount your removable media, just get that partition mounted however works for you. In my case, the target media is now mounted at /media/sdb1. This probably won't wipe any files (unless you already have GRUB-specific stuff on the stick), but I'd recommend using a new or unimportant stick. As root:

# grub-install --target=i386-pc --boot-directory=/media/sdb1/boot /dev/sdb

Fedora users will need to use grub2-install. Notice that I've named a directory that didn't previously exist: grub-install is willing to create it. And more important, notice that the target device at the end of the line is the ENTIRE DEVICE, not just the partition we're installing the files to. I made the mistake of doing a grub-install to /dev/sdb1 because I was putting the boot directory there: that doesn't work.

This command is often listed on the internet with --force and --recheck. Read the man page if you have problems before using either of these: I didn't need either, and you'd only need them if you're trying to achieve something different from what I'm doing here.

Unmount the USB stick, remove the stick, and put it into a computer that will boot from USB (this won't make any changes to the computer you're using ... unless you manage to issue some complex and nasty GRUB command - the boot process itself won't hurt it). When you boot to the USB stick, you won't see a menu because we haven't installed a grub.cfg file: you'll go immediately to the grub command line:

                        GNU GRUB  version 2.02~beta3-5

    Minimal BASH-like line editing is supported. For the first word, TAB
    lists possible command completions. Anywhere else TAB lists possible
    device or file completions.

grub>

(Keep in mind that screenshots and/or text dumps aren't implemented in GRUB so I'm writing all of this by hand: there could be typos.)

grub> ls
(hd0) (hd0,msdos1) (hd1) (hd1,msdos1)
grub>

What we see here is a list of available devices. In this case, we have hd0 which is the USB stick, and hd1, the hard drive in the laptop I'm using. Both have only one partition: the one on the USB stick is shown as hd0,msdos1. It's important to note that drive numbering is zero-based, and yet partition numbering starts at one. GRUB's Naming Convention

ls can also be used to view files on partitions ... but don't rush to type ls (hd0) yet. While that's a correct command, it appears that you need to inform GRUB about the file system types it will be looking at:

grub> insmod fat
grub> insmod part_msdos
grub> ls (hd0)
Device hd0: No known filesystem detected - Sector size 512B - Total size
15137280KiB
grub> ls (hd0,msdos1)
        Partition hd0,msdos1: Filesystem type fat - Label 'KINGSTON', UUID
79E7-4CCE - Partition start at 31.5KiB - Total size 15137104.5KiB
grub> ls (hd0,msdos1)/
boot/
grub>

One of the things the grub-install command did was install a bunch of modules to /media/sdb1/boot/grub/i386-pc/ (Fedora users will find this under /media/sdb1/boot/grub2/i386-pc/). To see a list of these modules and their sizes, run (at the grub> prompt) ls -l (hd0,msdos1)/boot/grub/i386-pc/. But there's a problem: there are a LOT of modules, and they scroll off screen. To fix this, type pager=1 and enter, then press the up arrow key twice (happily, GRUB supports command line replay and editing). The command output will now pause every page of output until you press a key. You'll find that GRUB's implementations of both ls and the pager are dumb as stumps - not exactly what you're used to from your regular command line environment. But this isn't surprising: this is a boot loader, not a full OS.

Rudimentary help for these commands can be obtained by typing help insmod or whatever command you're interested in.

As it turns out, the laptop I was working on is one of those rare machines that I have LVM installed on:

grub> ls
(hd0) (hd0,msdos1) (hd1) (hd1,msdos1)
grub> ls (hd0,msdos1)/
        Partition hd1,msdos1: No known filesystem detected - Partition start at
1024KiB - Total size 156289024KiB
grub> insmod lvm
grub> ls (hd0,msdos1)/
        Partition hd1,msdos1: No known filesystem detected - Partition start at
1024KiB - Total size 156289024KiB
grub> ls
(hd0) (hd0,msdos1) (hd1) (hd1,msdos1) (lvm/stray-boot3) (lvm/stray-home)
(lvm/stray-swap) (lvm/stray-boot2) (lvm/stray-boot1)
grub> ls (lvm/stray-boot1)
Device lvm/stray-boot1: Filesystem type ext* - Last modification time
... # lots of detail removed because I didn't feel like typing it in ...
grub> ls (lvm/stray-boot1)/
lost+found/ home/ etc/ media/ var/ usr/ lib/ lib64/ tmp/ sys/ sbin/ run/
root/ proc/ dev/ boot/ bin/ mnt/ srv/ opt/ vmlinuz initrd.img
grub>

LVM Logical Volumes are listed by label: I have three partitions meant for an OS on this machine, stray-boot[123]. I'm not sure of any magic formula for you to discover that LVM is in use prior to loading the 'lvm' module, but if GRUB is unable to read the partition after you've loaded the 'ext2' module, this might be the next guess? Unlike me, you may keep better track of your machines and where LVM is in use.

So let's see if we can boot the first bootable partition. After some digging with ls I found and examined the main GRUB configuration file for the partition (remember pager=1 if it's a long file):

grub> cat (lvm/stray-boot1)/boot/grub/grub.cfg
...
linux /boot/vmlinuz-4.13.0-1-amd64 root=/dev/mapper/stray-boot1 ro quiet
initrd /boot/initrd.img-4.13.0-1-amd64
...

This is a great time to remember that tab completion works at the GRUB command line for devices and filenames. Use it!

grub> linux (lvm/stray-boot1)/boot/vmlinuz-4.13.0-1-amd64 root=/dev/mapper/stray-boot1 ro
grub> initrd (lvm/stray-boot1)/boot/initrd.img-4.13.0-1-amd64
grub>

These commands don't show any response, but you now have the two critical components of booting from GRUB in place: a kernel, and a (matching) initial RAM disk. Assuming you've entered correct values for those two lines, there's only one more thing needed:

grub> boot

Which worked for me in this context. Keep in mind that this is a functioning, valid Debian installation that I booted and no fixes were needed for it to work. If your OS boot process is busted, you may have to dig more (ie. look around to discover older, working kernels) before this will work. Take notes as you work, because if you successfully boot your OS you're going to want to know what changes you made to get it working so that you can update your existing GRUB configuration.