PXE booting the Raspberry Pi 4

Today I got around to setting up a kickstart server to allow my Raspberry Pi’s to PXE boot. This has the advantage of reducing reliance (and wear and tear!) on SD cards. (Note, this only works for wired connections, not wifi)

This is fairly straightforwards but does require modification of the bootloader on your PI.

Things you will need:

Raspberry Pi 4 

Powersupply

Micro SD Card (For initial setup)

Ubuntu Server (I run 16.04 in a VM)

It’s also useful to have the Pi hooked up to a screen so you can see the boot process! (I use these HDMI leads)

Preparing the Pi

Use the Raspberry Pi Imager tool to flash a copy of Raspbian to the SD card. if you are running headless, create an empty file called ssh and place it in the boot volume before you boot the pi – this will allow you to SSH into your Pi once it has booted up.

SSH into your Pi (or use the local terminal) and the first thing we will do is update the Pi:

sudo apt-get update && sudo apt-get upgrade

Nest, we need to get the latest BETA firmware image. Go to https://github.com/raspberrypi/rpi-eeprom/raw/master/firmware/beta/ and get the filename of the most recent beta, e.g. “pieeprom-2020-08-31”

Now enter these commands, making sure the first line reflects the most recent beta:

PI_EEPROM_VERSION=pieeprom-2020-08-31
wget https://github.com/raspberrypi/rpi-eeprom/raw/master/firmware/beta/${PI_EEPROM_VERSION}.bin
sudo rpi-eeprom-config ${PI_EEPROM_VERSION}.bin > bootconf.txt
sed -i 's/BOOT_ORDER=.*/BOOT_ORDER=0x21/g' bootconf.txt
sudo rpi-eeprom-config --out ${PI_EEPROM_VERSION}-netboot.bin --config bootconf.txt ${PI_EEPROM_VERSION}.bin
sudo rpi-eeprom-update -d -f ./${PI_EEPROM_VERSION}-netboot.bin
cat /proc/cpuinfo | grep Serial | awk -F ': ' '{print $2}'
ip addr show eth0 | grep ether | awk '{print $2}'

The last two lines will output your serial number and MAC address, we will need to record these for later steps. Note, you can discard the first 1 and zeros from the serial number nad just keep the last 8 or 9 characters. For example, mine looks like “1000000064f7504e” so I would use “64f7504e” as the serial number

Reboot the Pi once, then shut it down and remove the SD card.

Preparing the Kickstart Server

Install an Ubuntu VM, I run mine in Hyper V on my Windows 10 machine but you can use any hypervisor you  like. Note, this will need to be running all the time your PI is on, as the filesystem will be stored on this server. Maybe you have an old laptop lying around that can be repurposed for this – it doesn’t need to be powerful, my VM has 1 core and 512Mb ram and is fine

Once you have ubuntu installed, we get an elevated shell and update and install the required packages:

sudo bash

apt update

apt install -y nfs-kernel-server dnsmasq kpartx unzip

Next, we download the latest Pi image and create the necessary folders:

wget -O raspbian_lite_latest.zip https://downloads.raspberrypi.org/raspbian_lite_latest
unzip raspbian_lite_latest.zip
kpartx -a -v *.img
mkdir {bootmnt,rootmnt}
mount /dev/mapper/loop0p1 bootmnt/
mount /dev/mapper/loop0p2 rootmnt/

Run the following command and replace it with your Raspberry Pi’s serial (last 8 or 9 characters), MAC Address and the IP Address of your Ubuntu VM. This will create the necessary filesystem structure and copy the Raspberry Pi OS from the previous step to be able to boot the Raspberry Pi over the network:

PI_SERIAL=abcdefgh
PI_MAC=dc:a6:ab:cd:ef:gh
KICKSTART_IP=192.168.30.176
mkdir -p /srv/nfs/rpi4-${PI_SERIAL}
mkdir -p /srv/tftpboot/${PI_SERIAL}
cp -a rootmnt/* /srv/nfs/rpi4-${PI_SERIAL}/
cp -a bootmnt/* /srv/nfs/rpi4-${PI_SERIAL}/boot/

Next, we need to replace the default firmware files with the latest version by running the following commands:

rm /srv/nfs/rpi4-${PI_SERIAL}/boot/start4.elf
rm /srv/nfs/rpi4-${PI_SERIAL}/boot/fixup4.dat
wget https://github.com/Hexxeh/rpi-firmware/raw/stable/start4.elf -P /srv/nfs/rpi4-${PI_SERIAL}/boot/
wget https://github.com/Hexxeh/rpi-firmware/raw/stable/fixup4.dat -P /srv/nfs/rpi4-${PI_SERIAL}/boot/

Next we setup the NFS export and dnsmasq configuration to allow the Raspberry Pi to mount the images over the network. If you have an existing DHCP server (And how doesn’t?) and want to use it, specify your network broadcast address and “proxy” keyword as shown in the example below (Most home routers will likely be 192.168.0.255 or  192.168.1.255, this is not the IP of your router, but the broadcast address which is usually the last address in your IP range)

echo "/srv/nfs/rpi4-${PI_SERIAL}/boot /srv/tftpboot/${PI_SERIAL} none defaults,bind 0 0" >> /etc/fstab
echo "/srv/nfs/rpi4-${PI_SERIAL} *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
cat > /etc/dnsmasq.conf << EOF
dhcp-range=192.168.0.255,proxy
log-dhcp
enable-tftp
tftp-root=/srv/tftpboot
pxe-service=0,"Raspberry Pi Boot"
EOF
mount /srv/tftpboot/${PI_SERIAL}/

In addition, to enable SSH and clearing the fstab file so it will not look for filesystem on the SD Card, the cmdline.txt is updated so Raspberry Pi knows how to reach our NFS share

touch /srv/nfs/rpi4-${PI_SERIAL}/boot/ssh
sed -i /UUID/d /srv/nfs/rpi4-${PI_SERIAL}/etc/fstab
echo "console=serial0,115200 console=tty root=/dev/nfs nfsroot=${KICKSTART_IP}:/srv/nfs/rpi4-${PI_SERIAL},vers=3 rw ip=dhcp rootwait elevator=deadline" > /srv/nfs/rpi4-${PI_SERIAL}/boot/cmdline.txt

Now we enable the required services:

systemctl enable dnsmasq
systemctl enable rpcbind
systemctl enable nfs-server
systemctl start dnsmasq
systemctl start rpcbind
systemctl start nfs-server

we are now ready to power on our Raspberry Pi (without the SD card!) and you should see Pi OS boot over the network. You can also confirm things are working by tailing the logs of /var/log/syslog on your Ubuntu system. (tail -f /var/log/syslog)

When the Pi boots, it will load up the PXE boot, connect, and then briefly appear to reset. You will then get the multicoloured start screen and booting should continue.

 

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.