Setting up PXE boot with qemu

I was expecting to spend the whole afternoon getting a tftp server setup so I could PXE boot some qemu virtual machines for testing. I wanted to make sure it didn’t interfere with anything else on the LAN so it would have to be limited to the user mode network I use with my vms. Typically when I try to do something like this I end up trying a bunch of different things that don’t quite work right. Today was the exception. I started out by reading the qemu manpage, which is very verbose but full of great information.

I was surprised to find that qemu has a built in tftp server! So to make sure I don’t forget about this, and to help out anyone else in the same situation, here’s what I did:

Create a local directory to hold the tftp and PXE related files, and copy over the syslinux files used by the PXE server.

mkdir -p ~/configs/tftp/pxeboot.cfg
cp /usr/share/syslinux/*c32 ~/configs/tftp/
cp /usr/share/syslinux/pxelinux.0 ~/configs/tftp/

Create a basic menu that will boot the local harddrive, use this to test the setup and see if it is working. Put the following into ~/configs/tftp/pxeboot.cfg/default:

default menu.c32
prompt 0
timeout 120

menu title PXE Menu

label local
  menu label Boot from ^local drive
  localboot 0xffff

I use a script to startup my qemu vm’s with serial console, and random number generator setup. Put the following into a file and set it to executable:

#!/bin/sh
qemu-kvm -cpu host -accel kvm \
-netdev user,id=net0,net=192.168.88.0/24,tftp=$HOME/configs/tftp/,bootfile=/pxelinux.0 \
-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=pci.0,addr=0x9 \
-serial stdio -boot n $@

When you run it with run-qemu -m 4096 -hda /path/to/disk you should see the PXE menu show up, and if you select the option to local boot it will try to boot the -hda that you have passed to it.

To boot a kernel+initrd put the vmlinuz and initrd.img files into a directory under ~/configs/tftp/ like ~/configs/tftp/fedora32/, and add the following section to the default menu file in pxeboot.cfg/:

label fedora32
  kernel /fedora32/vmlinuz
  append initrd=/fedora32/initrd.img console=ttyS0```

If you are using the initrd.img from a Fedora/RHEL/CentOS boot.iso you can boot the installer image by also appending ip=dhcp inst.repo=http://HTTP-SERVER-IP-ADDRESS/fedora32/os/, but make sure you boot the VM with more than enough ram for the image and the install since all if it will be running from RAM – typically 4GB is enough.