Building RPI 4 images from ubuntu 18.04

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 and selected the ArmV8 / Aarch64 server install image. Although using 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

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 ./
sudo rm rpi/*.deb
sudo rm rpi/
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

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: