Installing U-Boot, Grub and Minimal Debian Rootfs on a RockPRO64

The RockPRO64 is a low cost single-board computer featuring the powerful Rockchip RK3399 SoC. Although plenty of Linux rootfs images are available to install on the board, most of them are built in an opaque manner, leaving the user unable to determine whether they contain binary blobs or malware. Instead of trusting a binary image downloaded from the internet, why not learn the boot flow and build your own?

This guide was tested using the SD card slot. SPI does not need to be bypassed but this can be done by bridging GPIO pins 23 (CLK) and 25 (GND).

Setting up the serial console

Connecting to the RockPRO64’s serial console will allow you to troubleshoot in case of any mistakes. A board based on the CP2104 chip was used to create this guide, and a CP2102 chip was tested and found to be incompatible with the board’s preferred baud rate of 1500000.

Abbreviated pinout for basic GPIO serial operation:

Getting a cross-compiler, debootstrap and other prerequisites

arm-none-eabi and aarch64-unknown-linux-gnu compilers are required to assemble the boot firmware. If you use an aarch64 system to follow this guide, you already have the latter. If not, packages are available on most distributions and you can use qemu-user-static to run the debootstrap command which comes later.

On Gentoo aarch64:

crossdev -P -v \
         --b 2.33.1-r1 \
         --g 9.3.0 \
         --l 3.3.0-r1 \
         --target arm-none-eabi
emerge -av sys-block/parted \
           sys-fs/dosfstools \
           dev-util/debootstrap \
           app-crypt/debian-archive-keyring \
           sys-boot/grub

Users following this guide on an architecture other than aarch64 may also have trouble getting the grub installer modules compiled. These users may be able to build grub themselves with the aarch64-unknown-linux-gnu compiler.

Compiling the firmware

Andrius Štikonas has written an excellent tutorial for building this firmware.

First, build ATF:

cd /tmp
git clone https://github.com/ARM-software/arm-trusted-firmware.git
cd arm-trusted-firmware
git reset --hard v2.3
make CROSS_COMPILE=aarch64-unknown-linux-gnu- PLAT=rk3399

Check that the bl31.elf firmware file is ready for use by U-Boot:

export BL31=/tmp/arm-trusted-firmware/build/rk3399/release/bl31/bl31.elf
test -f ${BL31} && echo ok || echo WARNING

Then build U-Boot:

cd /tmp
git clone https://gitlab.denx.de/u-boot/u-boot.git
cd u-boot
git reset --hard v2020.07
make CROSS_COMPILE=aarch64-unknown-linux-gnu- rockpro64-rk3399_defconfig
make CROSS_COMPILE=aarch64-unknown-linux-gnu-

Preparing the root and firmware partitions

Carefully identify the SD card in dmesg, or run ls /dev/sd* before and after insertion.

export DRIVE=/dev/sdg

Erase at least the first few megabytes on the card:

dd if=/dev/zero of=${DRIVE} bs=1M count=50

On the Rockchip website we can see the expected partition layout. The detailed sequence description from sigmaris explains what the partitions are for.

Partition the drive, then create and mount the filesystems:

parted --script "${DRIVE}" \
    mklabel gpt \
    mkpart 1 ext2 8MiB 12MiB \
    mkpart 2 fat32 12MiB 140MiB \
    mkpart 3 ext2 140MiB 100% \
    set 2 esp on
dd if=/tmp/u-boot/idbloader.img of=${DRIVE} seek=64
dd if=/tmp/u-boot/u-boot.itb of=${DRIVE}p1
mkfs.ext4 -L ROCKROOT ${DRIVE}p3
mkfs.vfat -F 32 -n ROCKBOOT ${DRIVE}p2
mkdir -pv /mnt/rockpro
mount -v ${DRIVE}p3 /mnt/rockpro
mkdir -pv /mnt/rockpro/boot/efi
mount -v -t vfat -oiocharset=iso8859-1 ${DRIVE}p2 /mnt/rockpro/boot/efi

Installing the rootfs

If you are running through these steps on an aarch64 system:

debootstrap --arch=arm64 \
            --components=main \
            --variant=minbase \
            --include=linux-image-arm64,systemd-sysv \
            unstable \
            /mnt/rockpro \
            http://cdn-fastly.deb.debian.org/debian

If you are on a different architecture, add the flag --foreign.

Configuring the bootloader

If a UEFI image is located on the first FAT formatted partition, U-Boot will load that. Standalone grub can be our UEFI image, we just have to configure it.

cat <<EOF > /tmp/grub.cfg
set timeout=5
set default=0
menuentry Debian {
    insmod gzio
    insmod part_gpt
    insmod ext2
    set root=hd0,gpt3
    echo Loading kernel...
    linux /boot/vmlinuz-5.7.0-1-arm64 root=LABEL=ROCKROOT console=ttyS2,1500000
    echo Loading ramdisk...
    initrd /boot/initrd.img-5.7.0-1-arm64
}
EOF

According to osdev wiki, “you have to specify what file to include at what path in the memdisk”.

mkdir -pv /mnt/rockpro/boot/efi/EFI/BOOT/
grub-mkstandalone -O arm64-efi \
                  -o /mnt/rockpro/boot/efi/EFI/BOOT/BOOTAA64.EFI \
                  "boot/grub/grub.cfg=/tmp/grub.cfg"

Include the device tree file provided by Debian:

mkdir -pv /mnt/rockpro/boot/efi/dtb/rockchip
cp -v /mnt/rockpro/usr/lib/linux-image-5.7.0-1-arm64/rockchip/rk3399-rockpro64.dtb \
      /mnt/rockpro/boot/efi/dtb/rockchip

Continuing in qemu if needed

If you ran debootstrap on an aarch64 system (without the --foreign flag as described above), you must skip this section.

If you don’t have a qemu-user-static in your distribution, this Gentoo guide has some hints for compiling your own and configuring your kernel to detect aarch64 binaries (binfmt_misc).

chroot /mnt/rockpro /debootstrap/debootstrap --second-stage

Configure the system before booting

Set a root password:

chroot /mnt/rockpro passwd root

Configure the fstab. Some kernels issue a warning if the charset is not specified.

cat <<EOF > /mnt/rockpro/etc/fstab
LABEL=ROCKROOT / ext4 noatime 0 1
LABEL=ROCKBOOT /boot/efi vfat iocharset=iso8859-1 0 2
EOF

Powering on

You are ready to boot. Remove any serial cable connected to pin 10 and apply power with the barrel connector. You should see U-Boot, UEFI and finally the kernel print messages to the serial console. The tutorial is complete, but the following opinionated notes are provided as hints for further configuration.

Graphics

The RockPRO64 has what most hackers would call an HDMI port, but Rockchip and Pine64 have chosen not to license some proprietary aspects of the protocol. In documentation it is referred to as “4K Digital Video Out”.

Alyssa Rosenzweig has put considerable effort into producing a free software driver for the RK3399’s GPU. At the time of writing, this driver, called Panfrost, is available in the Mesa packages for Debian unstable.

Wifi

Linux 5.7 supports many USB wifi dongles. A device from TP-Link called Archer T1U was tested to work with this board. It uses the MediaTek MT7610U chipset and requires a firmware file to be located at /lib/firmware/mediatek/mt7610u.bin. This file can be installed from the standard Linux firmware repo or the Debian firmware-linux package.

The card will operate in a conservative mode until it learns which country’s RF rules to follow. The country can be configured in /etc/default/crda if using crda, or in wpa_supplicant-wlan0.conf with country=xx.