In this post I will be running through some of the steps needed to build custom images for Raspberry PI 4 boards. There are a number of steps along the way that took extra research and ended with some head scratching and wondering about how and why things were failing. Hopefully this guide can help someone struggling down the same path. I used my ubuntu workstation though out this process and would recommend using a Linux based OS
First off you will need an image to start from, I used http://cdimage.ubuntu.com/releases/18.04.4/release/ and selected the ArmV8 / Aarch64 server install image. Although using http://cdimage.ubuntu.com/releases/20.04/release/ is fine too Don’t forget to decompress the .xz
file!
xz --decompress <image_file_name>.img.xz
One of the first problems to overcome is that the image contains 2 partitions, the root file system and the (FAT) boot partition. A number of tools will show you where the offsets on the partition table in the image are for the 2 partitions, however, losetup
has a flag that will use a partition table scan inside the image to register loop device paritions
sudo losetup -P /dev/loop99 <image_file_name>.img
Now we will need an area to setup our mount points in to the partitions of the image. We will be mounting the root filesystem in partition 2 and the boot partition in partition 1
mkdir rpi mkdir rpi/boot
Normally we could just create the rpi
directory, however as this is an ubuntu image, we will be mounting the boot partition to rpi/boot/firmware
this is because the boot partition is FAT, which does not support sym links. If you are going to do any kernel installs; flash-kernel
will will encounter a number of issues trying to write the kernel to the rpi/boot
directory. To begin with, flash-kernel
will report “Unsupported platform”, reading through the documentation, you can override the target platform in /etc/flash-kernel/machine:
echo "Raspberry Pi 4 Model B Rev 1.2" > /etc/flash-kernel/machine
Next you will get a failure on missing device tree blobs:
Couldn't find DTB bcm2711-rpi-4-b.dtb on the following paths: /etc/flash-kernel/dtbs /usr/lib/linux-image- /lib/firmware//device-tree/ Installing into /boot/dtbs//./bcm2711-rpi-4-b.dtb /bin/cp: cannot stat '': No such file or directory
You can copy that device tree blob from /usr/lib/linux-image-/broadcom/bcm2711-rpi-4-b.dtb
to /etc/flash-kernel/dtbs
the result of which will be:
/bin/ln: failed to create symbolic link '/boot/dtb-': Operation not permitted
The correct approach is to mount the boot partition to rpi/boot/firmware
sudo mount -o rw /dev/loop99p2 rpi sudo mount -o rw /dev/loop99p1 rpi/boot/firmware
You will need to bind mount some of your running system in to the mounted partitions next. This will enable you to install packages inside the images in later steps
sudo mount --bind /dev rpi/dev/ sudo mount --bind /sys rpi/sys/ sudo mount --bind /proc rpi/proc/ sudo mount --bind /dev/pts rpi/dev/pts sudo mount --bind /run rpi/run
Finally, you will need to install qemu-user-static
.
sudo apt install -y qemu-user-static
The mount points have now been fully prepared and the next step is to create a chroot to access your image and install software packages as though it is your local system.
First make a script to execute inside the chroot and copy it into the directory. I used the following contents
install-packages.sh
apt update dpkg --configure -a apt-get --print-uris --yes install <list of packages> | grep ^\' | cut -d\' -f2 > downloads.list wget --input-file downloads.list dpkg -i *.deb
Every time I tried to get apt
to install the packages, it froze after unpacking. It would quite happily fetch the debs, so the above script will print the packages and all their dependencies URIs to a file called downloads.list
after which wget
and dpkg
take over to do the download and install respectively.
Copy the script in to the rpi
directory and run your package installation with the following commands
sudo chroot rpi ./install-packages.sh sudo rm rpi/*.deb sudo rm rpi/install-packages.sh sudo rm rpi/downloads.list
That is everything, you should now have an image with packages pre-installed. You will need to unmount everything on your local file system then you are ready to flash your image and try it out
sudo umount rpi/dev/pts sudo umount rpi/dev/ sudo umount rpi/sys/ sudo umount rpi/proc/ sudo umount rpi/run sudo umount /dev/loop99p1 sudo umount /dev/loop99p2 -l sudo losetup -d /dev/loop99