Given the traction our previous post got, we thought we should jot down the steps to build a 64-bit bootable image for a RPi4. The distro we’re most familiar with is Debian, so we’ll go with a debian-like distro like Ubuntu. If you don’t feel like playing with kernel compilation and FS images, just grab the binary and dd it to an SD card!

First step, download the 64-bit ubuntu server distro for the RPi3:

http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/ubuntu-18.04.2-preinstalled-server-arm64+raspi3.img.xz

Then make sure you follow the instructions from these posts which help us build the kernel and update the boot firmware. The steps from these posts are summarized below:

Build the toolchain Link to heading

 1mkdir -p toolchains/aarch64
 2cd toolchains/aarch64
 3export TOOLCHAIN=`pwd` # Used later to reference the toolchain location
 4
 5cd "$TOOLCHAIN"
 6wget https://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.bz2
 7tar -xf binutils-2.32.tar.bz2
 8mkdir binutils-2.32-build
 9cd binutils-2.32-build
10../binutils-2.32/configure --prefix="$TOOLCHAIN" --target=aarch64-linux-gnu --disable-nls
11make -j4
12make install
1cd "$TOOLCHAIN"
2wget https://ftp.gnu.org/gnu/gcc/gcc-9.1.0/gcc-9.1.0.tar.gz
3tar -xf gcc-9.1.0.tar.gz
4mkdir gcc-9.1.0-build
5cd gcc-9.1.0-build
1../gcc-9.1.0/configure --prefix="$TOOLCHAIN" --target=aarch64-linux-gnu --with-newlib --without-headers --disable-nls --disable-shared --disable-threads --disable-libssp --disable-decimal-float --disable-libquadmath --disable-libvtv --disable-libgomp --disable-libatomic --enable-languages=c
2make all-gcc -j4
3make install-gcc

Build the Raspberry Pi kernel Link to heading

1apt-get install bison flex
2git clone https://github.com/raspberrypi/linux.git rpi-linux
3cd rpi-linux
4git checkout origin/rpi-4.19.y # change the branch name for newer versions
5mkdir kernel-build
6PATH=$PATH:$TOOLCHAIN/bin make O=./kernel-build/ ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-  bcm2711_defconfig
7PATH=$PATH:$TOOLCHAIN/bin make -j4 O=./kernel-build/ ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
8export KERNEL_VERSION=`cat ./kernel-build/include/generated/utsrelease.h | sed -e 's/.*"\(.*\)".*/\1/'` 
9make -j4 O=./kernel-build/ DEPMOD=echo MODLIB=./kernel-install/lib/modules/${KERNEL_VERSION} INSTALL_FW_PATH=./kernel-install/lib/firmware modules_install
1git clone https://github.com/raspberrypi/tools.git rpi-tools
2cd rpi-tools/armstubs
3git checkout 7f4a937e1bacbc111a22552169bc890b4bb26a94
4PATH=$PATH:$TOOLCHAIN/bin make armstub8-gic.bin
1echo "armstub=armstub8-gic.bin" >> config-extra.txt 
2echo "enable_gic=1" >> config-extra.txt 
3echo "arm_64bit=1" >> config-extra.txt
4echo "total_mem=1024" >> config-extra.txt

We now have all the necessary files to create the boot partition and boot into the ubuntu-preinstalled image. Specifically:

Kernel: rpi-linux/kernel-build/arch/arm64/boot/Image

Bootstub: rpi-tools/armstubs/armstub8-gic.bin

Modules: rpi-linux/kernel-build/kernel-install/lib/modules/${KERNEL_VERSION}

Firmware: https://github.com/RPi-Distro/firmware-nonfree

So, first thing to do after we have finished building the above is to de-compress and loop mount the ubuntu-preinstalled image we downloaded:

1xzcat ubuntu-18.04.2-preinstalled-server-arm64+raspi3.img.xz > ubuntu-18.04.2-preinstalled-server-arm64+raspi4.img
2kpartx -av ubuntu-18.04.2-preinstalled-server-arm64+raspi4.img

You should end up with 2 device files:

1/dev/mapper/loop0p1
2/dev/mapper/loop0p2

Mount them under /mnt like this:

1mount /dev/mapper/loop0p2 /mnt
2mount /dev/mapper/loop0p1 /mnt/boot/firmware

Then copy in the kernel/stub, modules and firmware:

1cp rpi-linux/kernel-build/arch/arm64/boot/Image /mnt/boot/firmware/kernel8.img
2cp rpi-tools/armstubs/armstub8-gic.bin /mnt/boot/firmware/armstub8-gic.bin
3cp -avf rpi-linux/kernel-build/kernel-install/lib/modules/${KERNEL_VERSION} /mnt/lib/modules/
4git clone https://github.com/RPi-Distro/firmware-nonfree firmware-nonfree
5cp -avf firmware-nonfree/* /mnt/lib/firmware

Append config-extra.txt to config.txt:

1cat config-extra.txt >> /mnt/boot/firmware/config.txt

and we’re done! Unmount / detach the loop device, dd it to an sdcard, plug it into a RPi4 and party!

1umount /mnt/boot/firmware
2umount /mnt
3kpartx -dv ubuntu-18.04.2-preinstalled-server-arm64+raspi4.img
4losetup -d /dev/loop0
5dd if=ubuntu-18.04.2-preinstalled-server-arm64+raspi4.img of=/dev/sdXX

If you’re too lazy to do the above, feel free to grab our image built using the above steps:

https://cloudkernels.net/ubuntu-18.04.2-preinstalled-server-arm64+raspi4+kvm.img.xz

(sha1sum: 0b1d8b72ea5410fb7928925fd76dd0218b4f7a94)

UPDATE

Links to the previous images were removed, so here’s the new ones: http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/ubuntu-18.04.3-preinstalled-server-arm64+raspi3.img.xz https://cloudkernels.net/ubuntu-18.04.3-preinstalled-server-arm64+raspi4+kvm.img.xz