Custom Firmware for Raspberry Pi 4
This page explains how to create, package, and run a custom Linux firmware on a virtual Raspberry Pi 4 board. Ubuntu Server and Ubuntu Desktop images are used as examples as provided directly by Ubuntu. The demonstrated flow can be applied for running other Linux distributions as well.
Firmware Package Contents
For the overall information about firmware packaging see Linux Firmware Packages.
A proper firmware package for a virtual Rapberry Pi 4 board contains following files:
Info.plist
- for the meta information in JSON format about firmware version, type, build, unique identifier, and device identifier.nand
- with the preinstalled arm64 image file.devicetree
- hardware components data for the Linux kernel.kernel
- the Linux kernel file.ramdisk.img
(optional) - the initrd root file system image.
Although specifying the ramdisk.img
is generally optional, we need to add a reboot command in this case because of how this virtual device handles the first pass.
Install the Package
The steps below are specific the firmware package with Ubuntu Server, but can be followed in the same way to insall Ubuntu Desktop:
In your local Linux environment run the shell script for creating the Ubuntu Server image as given in Firmware Packaging Scripts. It shall create firmware package file
rpi4b-ubuntu-server.zip
.On the AVH web interface, click CREATE DEVICE.
Click on the Raspberry Pi 4 board.
Upload the custom firmware package.
When the process is complete, click NEXT.
Create the device without enabling advanced boot options.
The virtual board will boot to the Ubuntu Server login screen. Login with the default credentials (
ubuntu/ubuntu
) and change your admin password.
:::Note
The SSH commands displayed for the virtual Raspberry Pi 4 instance under the Connect tab show information for the stock firmware. With the custom package used in this example, you need to use ubuntu
as the username instead of the pi
shown by default.
:::
Firmware Packaging Scripts
The Raspberry Pi supports a variety of Linux distributions.
The following scripts demonstrate how to create custom firmware packages for the Raspberry Pi 4 board using the Raspberry Pi OS, Ubuntu Server, and Ubuntu Desktop packages.
Raspberry Pi OS Desktop image
The following script creates a custom firmware package rpi4b-11.2-desktop.zip
in your working directory using the Raspberry Pi OS Desktop 11.2 image as provided by Raspberry Pi.
#!/bin/bash
set -e
[ -d pi ] || mkdir pi
cd pi
# Grab the raspberry pi firmware
[ -f 2022-01-28-raspios-bullseye-arm64.zip ] || wget https://downloads.raspberrypi.org/raspios_arm64/images/raspios_arm64-2022-01-28/2022-01-28-raspios-bullseye-arm64.zip
rm -rf {nand,devicetree,kernel,Info.plist,boot,rootfs}
unzip 2022-01-28-raspios-bullseye-arm64.zip
mv 2022-01-28-raspios-bullseye-arm64.img nand
# Mount the firmware image and extract the kernel and device tree
LO="$(losetup -f)"
mkdir boot
losetup -P "${LO}" nand
mount "${LO}p1" boot
# Enable ssh
touch boot/ssh
cp boot/bcm2711-rpi-4-b.dtb devicetree
zcat boot/kernel8.img > kernel
umount boot
rm -rf boot
mkdir rootfs
mount "${LO}p2" rootfs
# Don't run dhcpcd on docker interfaces
echo 'denyinterfaces veth*' >> rootfs/etc/dhcpcd.conf
umount rootfs
rm -rf rootfs
losetup -d "${LO}"
# create the Info plist that describes the model image
cat << EOF > Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Type</key>
<string>iot</string>
<key>UniqueIdentifier</key>
<string>Raspberry Pi OS Desktop</string>
<key>DeviceIdentifier</key>
<string>rpi4b</string>
<key>Version</key>
<string>11.2.0</string>
<key>Build</key>
<string>desktop</string>
</dict>
</plist>
EOF
# zip image and its ready for use
zip -r ../rpi4b-11.2-desktop.zip Info.plist nand devicetree kernel ramdisk.img
Ubuntu Server image
The following script creates a custom firmware package rpi4b-ubuntu-server.zip
in your working directory using the Ubuntu Server 22.04.1 image as provided by Ubuntu.
#!/bin/bash
set -e
mkdir rpi_ubuntu_server_firmware
cd rpi_ubuntu_server_firmware
# Download and extract the "Ubuntu 22.04.1 Server for RPi" image
wget https://cdimage.ubuntu.com/releases/22.04.1/release/ubuntu-22.04.1-preinstalled-server-arm64+raspi.img.xz
xz -dv ubuntu-22.04.1-preinstalled-server-arm64+raspi.img.xz
mv ubuntu-22.04.1-preinstalled-server-arm64+raspi.img nand
# Attach the image file
LO="$(losetup -f)"
losetup -P "${LO}" nand
# Mount partition 1 to directory boot
mkdir boot
mount "${LO}p1" boot
# Copy the rpi4b devicetree
cp boot/bcm2711-rpi-4-b.dtb devicetree
# Extract the Linux kernel
zcat boot/vmlinuz > kernel
# Extract initrd
lz4 -d boot/initrd.img initrd.cpio
umount boot
rm -r boot/
losetup -d "${LO}"
mkdir ramdisk
cd ramdisk
# Extract initrd
cat ../initrd.cpio | cpio -idm
rm ../initrd.cpio
# Add reboot conditional to init before matching string
sed -i '/Move virtual filesystems over to the real filesystem/i \
if /bin/grep init_resize /proc/cmdline; then\
/bin/reboot -f\
fi\
' init
# Create ramdisk.img from directory
find . | cpio -o -H newc -R root:root | lz4 -l > ../ramdisk.img
cd ..
rm -r ramdisk/
# Create the Info.plist file
cat << EOF > Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Type</key>
<string>iot</string>
<key>UniqueIdentifier</key>
<string>Ubuntu Server on RPi</string>
<key>DeviceIdentifier</key>
<string>rpi4b</string>
<key>Version</key>
<string>22.04.1</string>
<key>Build</key>
<string>Ubuntu Server</string>
</dict>
</plist>
EOF
zip -rm ../rpi4b-ubuntu-server.zip Info.plist nand devicetree kernel ramdisk.img
cd ..
rm -r rpi_ubuntu_server_firmware/
Ubuntu Desktop image
The following script creates a custom firmware package rpi4b-ubuntu-desktop.zip
in your working directory using the Ubuntu Desktop 22.04.1 image as provided by Ubuntu.
#!/bin/bash
set -e
mkdir rpi_ubuntu_desktop_firmware
cd rpi_ubuntu_desktop_firmware
# Download and extract the "Ubuntu 22.04.1 Desktop for RPi" image
wget https://cdimage.ubuntu.com/releases/22.04.1/release/ubuntu-22.04.1-preinstalled-desktop-arm64+raspi.img.xz
xz -dv ubuntu-22.04.1-preinstalled-desktop-arm64+raspi.img.xz
mv ubuntu-22.04.1-preinstalled-desktop-arm64+raspi.img nand
# Attach the image file
LO="$(losetup -f)"
losetup -P "${LO}" nand
# Mount partition 1 to directory boot
mkdir boot
mount "${LO}p1" boot
# Copy the rpi4b devicetree
cp boot/bcm2711-rpi-4-b.dtb devicetree
# Extract the Linux kernel
zcat boot/vmlinuz > kernel
# Extract initrd
lz4 -d boot/initrd.img initrd.cpio
umount boot
rm -r boot/
mkdir rootfs
# Mount partition 2 to directory rootfs
mount "${LO}p2" rootfs
# Use aarch64 Linux sssd.conf and set permission to 600
cp rootfs/usr/lib/aarch64-linux-gnu/sssd/conf/sssd.conf rootfs/etc/sssd/.
chmod 600 rootfs/etc/sssd/sssd.conf
umount rootfs
rm -r rootfs/
losetup -d "${LO}"
mkdir ramdisk
cd ramdisk
# Extract initrd
cat ../initrd.cpio | cpio -idm
rm ../initrd.cpio
# Add reboot conditional to init before matching string
sed -i '/Move virtual filesystems over to the real filesystem/i \
if /bin/grep init_resize /proc/cmdline; then\
/bin/reboot -f\
fi\
' init
# Create ramdisk.img from directory
find . | cpio -o -H newc -R root:root | lz4 -l > ../ramdisk.img
cd ..
rm -r ramdisk/
# Create the Info.plist file
cat << EOF > Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Type</key>
<string>iot</string>
<key>UniqueIdentifier</key>
<string>Ubuntu Desktop on RPi</string>
<key>DeviceIdentifier</key>
<string>rpi4b</string>
<key>Version</key>
<string>22.04.1</string>
<key>Build</key>
<string>Ubuntu Desktop</string>
</dict>
</plist>
EOF
zip -rm ../rpi4b-ubuntu-desktop.zip Info.plist nand devicetree kernel ramdisk.img
cd ..
rm -r rpi_ubuntu_desktop_firmware/