Find out exact location of GRUB / GRUB not working after copying disk image

I prepared an Ubuntu server image with VirtualBox. To transfer the image to the server's SSD I first dded the MBR (512 bytes) and then the LVM partition (PV containing the root partition shrunk to 3GB). The server failed to boot because some parts of GRUB were missing. The grub rescue prompt was showing up.

As I learned from GRUB's documentation, this makes sense, because parts of it are (usually) stored in the disk space between the MBR and the first partition.

But how do I found out where exactly?

I am aware that I could just copy the whole space in front of the first partition, but I am curious if there is some command that shows the exact locations of the various parts of GRUB.

Clarification: There is no separate boot partition. There is only the LVM physical volume containing only the root partition (with the /boot folder). The boot issue can also be fixed by chrooting into the copied image and executing grub-install.


Solution 1:

In the GRUB2-created MBR, starting at offset 0x5c, there is a little-endian 64-bit value indicating the number of the next disk block to be loaded. This is often block 0x00000000 00000001, i.e. the next block after the MBR.

(By ancient DOS convention, the first partition will start at the beginning of cylinder #1, so there is usually a number of unused blocks available on cylinder #0 after the MBR. On modern systems, the first partition is typically aligned to start from block #2048, i.e. exactly 1 MB from the beginning of the disk. This makes for even more free blocks before the start of the first partition.)

This second block contains some more GRUB kernel code, and a blocklist that specifies the blocks to load the rest of the GRUB core image from. The blocklist is located at the very end of this block. Each blocklist entry is 12 bytes long, and structured like this:

struct __attribute__ ((packed)) g2_blist_entry {
  uint64_t start_lba;  # the LBA block number of the first block covered by this entry
  uint16_t num_blocks; # number of blocks to read
  uint16_t startseg;   # segment address in memory to write them to
};

Typically the blocklist has just one entry that covers all the remaining blocks to read, starting from block 0x00000000 00000002.

The length of the GRUB core image varies according to GRUB version and the number of modules appended onto the GRUB kernel.

For example, on my Debian 9 system that uses legacy MBR boot, GRUB encompasses a total of 103 blocks after the MBR. On a RHEL 7.4 VM, the total length is 107 blocks after the MBR.