PXE booting with QEMU and UEFI
This is a followup to the qemu with PXE boot
post I wrote a few years back. In this post I will cover the creation of a
live filesystem using livemedia-creator, and PXE booting it with qemu
and UEFI.
I am assuming that you have some familiarity with using
Anaconda
to install Fedora, CentOS,
and Red Hat Enterprise Linux. As well as with my previous posts about
livemedia-creator
.
Building the image with livemedia-creator
The creation process is similar to creating live isos but instead of an iso we will create the live root filesystem. This is an ext4 filesystem that is compressed using squashfs, and it is the same as the rootfs used on the boot.iso and live isos.
You will need to have the lorax
package installed, and you should be using a
distribution that is the same as, or as close as possible, to the distribution
you are targeting (eg. Fedora 38, RHEL 9, etc.). For this post I was running on
a Fedora 37 system, and using a Fedora 38 boot.iso. You can use this same
process on RHEL and CentOS, with the only difference being the specific kernel
and initramfs versions.
livemedia-creator
can use a boot.iso and qemu
to do the actual
installation, or use Anaconda running on the host. In these examples I’ll be
using a boot.iso. If you want to run Anaconda directly, install the
lorax-lmc-novirt
package and pass --no-virt
instead of --iso boot.iso
.
The biggest difference is that when running with --no-virt
mode you need to
be on a UEFI system or VM for Anaconda to correctly setup UEFI booting.
Install the lorax-lmc-virt
package to make sure all of the necessary
requirement are installed:
sudo dnf install -y lorax lorax-lmc-virt
Creating the kickstart for livemedia-creator
Overall you can do anything in the kickstart that you would normally do, but since this is a compressed filesystem image there are a few restrictions:
- Partitioning should be
reqpart
and a singlepart / --fstype="ext4" --size=4000
entry. - Make sure the
dracut-live
package is included in the%packages
section - Remove VM specific files in
%post
like/etc/fstab
, etc.rm -f /etc/fstab rm -f /var/lib/systemd/random-seed rm -f /etc/machine-id touch /etc/machine-id
You can start with the minimal.ks
example kickstart that is found in
/usr/share/doc/lorax/
and modify it to fit your needs. And the full
kickstart documentation is here
.
Creating the PXE boot live filesystem
Download the boot.iso for the distribution, eg. the Fedora 38 boot.iso can be found here.
Run livemedia-creator
to build to root filesystem and example PXE menu entry:
sudo livemedia-creator --iso ./boot.iso --ks ./minimal.ks --virt-uefi --make-pxe-live --resultdir /var/tmp/pxe-live/
When this is finished you will have several files in the /var/tmp/pxe-live/
directory:
* A kernel
* An initramfs image
* A compressed root filesystem named live-rootfs.squashfs.img
* A raw filesystem image
* An example PXE config file when booting with isolinux
The kernel, initramfs, and compressed rootfs will be used in the following steps.
If you are an experienced PXE user you can skip the qemu
setup and jump
directly into the PXE boot menu settings.
A qemu UEFI PXE startup script
The qemu
script from the qemu with PXE boot
post was fairly simple. Adding UEFI support complicates things a bit. You
need to setup the UEFI firmware flash, and make a copy of the OVMF_VARS
file.
You can also set it up to use secure boot if you use the OVMF_CODE.secboot.fd
file, but in this example I am using the non-secure boot firmware:
#!/bin/sh
cp /usr/share/OVMF/OVMF_VARS.fd /tmp/qemu-pxe-OVMF_VARS.fd
qemu-kvm -cpu host -accel kvm \
-machine q35,smm=on -global driver=cfi.pflash01,property=secure,value=on \
-drive file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on \
-drive file=/tmp/qemu-pxe-OVMF_VARS.fd,if=pflash,format=raw,unit=1 \
-netdev user,id=net0,net=192.168.88.0/24,tftp=$HOME/configs/tftp/,bootfile=BOOTX64.EFI \
-device virtio-net-pci,netdev=net0 \
-object rng-random,id=virtio-rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=virtio-rng0,id=rng0,bus=pcie.0,addr=0x9 \
-serial stdio -boot n $@
Put this into a file named something like run-qemu-pxe
and set the executable
bit on it. This will boot a qemu
UEFI system using the tftp setup in
$HOME/configs/tftp/
. We will setup that directory next.
Setup the tftp directory for UEFI
When PXE booting UEFI you need a copy of several files, you can get them from
the boot.iso
used to create the root filesystem, or from the distribution
repository’s EFI/BOOT
directory
.
I’ll use the ones from the boot.iso
, mounted on /mnt/iso/
.
mkdir -p $HOME/configs/tftp
sudo mount boot.iso /mnt/iso/
sudo cp /mnt/iso/EFI/BOOT/BOOTX64.EFI $HOME/configs/tftp/
sudo cp /mnt/iso/EFI/BOOT/grubx64.efi $HOME/configs/tftp/
sudo chown $USER $HOME/configs/tftp/*
Make sure the user has permissions on these files. On the boot.iso they are restricted to root, so ownership and/or permissions need to be changed.
Prepare the PXE files
There are several different ways to load the root filesystem. One is to pull it from a webserver over http, another is to use a combined initramfs that has the rootfs appended to the regular initramfs. Both of these methods need to have enough RAM on the system to hold the filesystem, the kernel, and the initramfs in addition to enough RAM for normal system operation.
We will place these files in the $HOME/configs/tftp/live/
directory. Copy the
kernel, initramfs, and live-rootfs.squashfs.img
files from
/var/tmp/pxe-live/
. Then create a combined.img
file. The name of the
initramfs file will vary based on your kernel version and distribution.
mkdir -p $HOME/configs/tftp/live/
cd $HOME/configs/tftp/live/
sudo cp /var/tmp/pxe-live/* .
sudo chown $USER *
echo ./live-rootfs.squashfs.img | cpio -c --quiet -L -o > rootfs.cpio
cat initramfs-6.2.12-300.fc38.x86_64.img rootfs.cpio > combined.img
If you want to use http to fetch the rootfs, setup a server that points to this
directory. It can be as simple as running python3 -m http.server
from the
directory and then using root=live:http://HOST-IP:8000/live-rootfs.squashfs.img
as the
location in grub.cfg
.
Create a grub menu
The GRUB bootloader needs a configuration file in the same directory as
BOOTX64.EFI
, so switch back to $HOME/configs/tftp
and create a grub.cfg
menu file using the following example. This has two entries. The first,
combined-rootfs
will boot the root filesystem using the tftp server. The
second will boot it using http and a webserver that you have setup to point to
the $HOME/configs/tftp/live/
directory.
set timeout=60
menuentry 'combined-rootfs' {
linuxefi /live/vmlinuz-6.2.12-300.fc38.x86_64 root=live:/live-rootfs.squashfs.img rd.live.image console=ttyS0 console=tty1
initrdefi /live/combined.img
}
menuentry 'http-rootfs' {
linuxefi /live/vmlinuz-6.2.12-300.fc38.x86_64 root=live:http://HTTP-SERVER/live-rootfs.squashfs.img rd.live.image console=ttyS0 console=tty1
initrdefi /live/initramfs-6.2.12-300.fc38.x86_64.img
}
Boot!
With that file in place you should now be able to boot using the
run-qemu-uefi
script, remembering to give it enough memory:
run-qemu-uefi -m 4096
Select the menu entry to boot and after a brief pause for it to load the
initramfs you should see kernel messages flying by. If you get an error
mentioning tftp.c
that usually means it could not find the kernel or
initramfs file(s). Usually because the version or release number in the
grub.cfg
file doesn’t match what is available in $HOME/configs/tftp
.