star ✦ ark ↪ Migrating a Debian install from legacy to EFI boot

I recently migrated my Xen hypervisors from HVM to PVH. I'll write about that later, but since the boot interface changed from legacy (BIOS) to EFI, today I have to migrate my VMs - including the one I'm writing this very post on.

Before doing anything, back up your data!

Boot the VM into a recovery ISO

My go-to is Alpine but anything works. You'll need to get e2fsprogs, dosfstools, gptfdisk and arch-install-scripts packages (which also requires mount and findmnt on Alpine, but they don't get auto-installed).

Convert the MBR partition table to GPT

I installed Debian on these VMs with the automated installer, and my partitions look like this:

localhost:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
xvda    202:0    0  100G  0 disk 
├─xvda1 202:1    0 94.9G  0 part 
├─xvda2 202:2    0    1K  0 part 
└─xvda5 202:5    0  5.1G  0 part

Open the device in gdisk and save changes immediately.

localhost:~# gdisk /dev/xvda
GPT fdisk (gdisk) version 1.0.10
  
Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present
  
  
***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by
typing 'q' if you don't want to convert your MBR partitions
to GPT format!
***************************************************************
  
  
Command (? for help): w
  
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!!
  
Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/xvda.
The operation has completed successfully.
localhost:~# 

The device now looks like this, with the BIOS boot partition removed.

localhost:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
xvda    202:0    0  100G  0 disk 
├─xvda1 202:1    0 94.9G  0 part 
└─xvda5 202:5    0  5.1G  0 part

However, the device files aren't created in /dev:

localhost:~# ls /dev/xvda*
/dev/xvda

If your system has udev or something similar creating device files, skip to the next section!

Create them with mknod:

localhost:~# mknod /dev/xvda1 b 202 1
localhost:~# mknod /dev/xvda5 b 202 5

Mount the new partitions

In my case, partition 1 contains the data, and partition 5 is a swap partition. I don't need the swap so I'll repurpose that as the EFI partition.
If you need to keep the swap partition, just shrink it by 50MB or so and create a new EFI partition.

I'll do this:

localhost:~# mount /dev/xvda1 /mnt
localhost:~# mkdir /mnt/boot/efi
localhost:~# mkfs.fat -F 32 -n boot /dev/xvda5
mkfs.fat 4.2 (2021-01-31)
mkfs.fat: Warning: lowercase labels might not work properly on some systems
localhost:~# mount /dev/xvda5 /mnt/boot/efi

Regenerate the fstab, too.

localhost:~# genfstab -U /mnt > /mnt/etc/fstab

Chroot into the OS and reconfigure grub

Remove grub for BIOS and install grub for EFI.

localhost:~# arch-chroot /mnt
root@localhost:/# apt update && apt remove -y grub-pc && apt install -y grub-efi
[...]
root@localhost:/# grub-install /dev/xvda5
Installing for x86_64-efi platform.
Installation finished. No error reported.

Since this is a PVH domU, I'll also add the appropriate kernel parameters, and regenerate the config.

root@localhost:/# sed -i s/'GRUB_CMDLINE_LINUX="'/'GRUB_CMDLINE_LINUX="console=hvc0 earlyprintk=xen '/ /etc/default/grub
root@localhost:/# update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.12.48+deb13-amd64
Found initrd image: /boot/initrd.img-6.12.48+deb13-amd64
Found linux image: /boot/vmlinuz-6.12.43+deb13-amd64
Found initrd image: /boot/initrd.img-6.12.43+deb13-amd64
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
Adding boot menu entry for UEFI Firmware Settings ...
done

Reboot

If your EFI bootloader is reasonable, this last command will have added an entry for /EFI/debian/grubx64.efi, which the VM will boot into after removing the recovery ISO.

Unfortunately, mine (intentionally) doesn't; EFI variables such as boot entries get reset after a reboot. As EFI looks for a bootloader at /EFI/BOOT/BOOTX64.EFI, we'll just get a No bootable option or device was found error at this point.

emoji: neocat_facepalm

I forgot about that!

Back to the recovery ISO then. We'll mount just the EFI partition and copy the files over:

localhost:~# mount /dev/xvda5 /mnt
localhost:~# cd /mnt/EFI
localhost:/mnt/EFI# cp -r debian BOOT
localhost:/mnt/EFI# mv BOOT/grubx64.efi BOOT/BOOTX64.EFI

There, it boots now. All done!