Generic BSD installations on ARM64 UEFI: results and first impressions

After turning RockPro64 into a "normal" UEFI PC time to test generic UEFI ARM64 installations of major BSDs, see if any can be used as a desktop OS on this machine. Images used are FreeBSD-14.3-RELEASE-arm64-aarch64-disc1.iso, NetBSD-10.1-evbarm-aarch64.iso and install77.img (OpenBSD). Procedure for each is to prepare a 10 GiB partition on SATA drive for installation, write image on USB stick, reboot and go through installation.

FreeBSD

(TL;DR) Impressions:

  • Excellent UEFI support on ARM64, straightforward installation
  • Easy to get all package build definitions from https://git.freebsd.org/ports.git
  • Looks like no graphics support for Mali T860 GPU on RockPro64
  • Spoofs MAC address without being told to, in my case breaking Internet access
  • Dependency tree is very bloated - installing a small Wayland compositor (Sway) pulled in 2 GiB of dependencies including LLVM. On Arch it's about 16 MiB.

FreeBSD refused to be installed on the prepared partition so I deleted it and created a new one in the partitioning step of the installation. FreeBSD installer recognized EFI partition and created EFI executables there, very nice. Install finished, on reboot U-boot recognized EFI files and offered FreeBSD as a boot option. System loaded fine, did not spoof the MAC address of the builtin ethernet interface (like it did on Cubieboard) maybe because it is written in U-boot configuration on SPI chip, so it got DHCP IP from ISP. To test if I can get a graphical desktop I installed sway and seatd. According to post-install instructions displayed by pkg, started seatd service as root, added regular user to video group, logged off and on, tried to start sway as a regular user - it couldn't find any DRM backends (no GPU devices).

RockPro64 has a Mali T860 GPU, is it not supported on FreeBSD? https:///bsd-hardware.info has no info on the board or GPU, digging in FreeBSD source code I found only this declaration:

sys/contrib/device-tree/Bindings/gpu/arm,mali-midgard.yaml
- items:
  - enum:
      - rockchip,rk3399-mali
  - const: arm,mali-t860

After some online searching it turns out DRM drivers are not part of the base FreeBSD system, they are in ports tree (FreeBSD's name for installable packages). The build definitions are available as a single git repo https://git.freebsd.org/ports.git - very convenient. According to graphics/drm-kmod/pkg-descr, only amdgpu/i915/radeon DRM modules are there. There are references to Panfrost though (Linux name for Mali GPU driver) in graphics/mesa-dri/Makefile. Mesa is userspace, not the kernel module, but I wanted to install it to see if it would pull some dependencies and make it work, unlikely but worth a try. Rebooted to FreeBSD aaand... FreeBSD spoofs the MAC address. No DHCP IP from ISP = no Internet = can't install packages.

Looks like there's no DRM driver for Mali T860 GPU in FreeBSD 14.3, making it not usable as a dekstop OS. If there is one I'll give it another try.

OpenBSD

I had a serial connection to the board at the time, so monitored the messages there - the system loads the kernel, goes through hardware enumeration to something like creating/attaching to wsdisplay0 and hangs there presenting a blank screen (the HDMI output is active though, monitor doesn't turn off). No reactions to keyboard/mouse or input via serial. Can't proceed with installation.

NetBSD

(TL;DR) Impressions:

  • Installer for ARM64 is full of bugs
  • Shell in installation image, which I had to use to work around said bugs, is extremely primitive - no working backspace, no history, no tab completion, not even arrow keys. Even on a physical typewriter you can move back a space, come on NetBSD...
  • Spoofs MAC address without being told to, in my case breaking Internet access

Installer started by replacing firmware-provided MAC address with a generated one (same as FreeBSD, but earlier), leaving machine without internet access - so no fetching sets from mirrors. If choosing "Use already existing GPT partitions" it fails to mount EFI partition. Choosing not to mount EFI partition results in installer silently installing bootloader (EFI executable) to /boot in root partition. By default it doesn't recognize the USB stick it booted off as storage to get the sets from, so I had to manually put device name sd0a (thanks to helpful people on IRC). Choosing "Expert partitioning" (pretty much same functionality, duplicated) resulted in some other errors. While extracting the kernel from USB stick there was a warning ar: pattern ./netbsd.ub not matched but it finished anyway. After that I manually mounted the EFI partition to /tmp/efi, newly installed root partition to /tmp/root and copied /tmp/root/boot/EFI/boot/bootaa64.efi to /tmp/efi/EFI/boot/bootaa64.efi.

Well, it's a mess, ain't it, sheriff? If it ain't, it'll do till the mess gets here. (No Country for Old Men, 2007)

Remove USB stick, reboot - U-boot presented NetBSD EFI option and it booted.

On the MAC address issue people on IRC suggested checking ofctl -p to see if firmware-provided MAC was all zeroes to begin with. No - the value of local-mac-address matches the one in U-boot. So NetBSD reads MAC address of an interface from firmware but sets a new one anyway? The same helpful person on IRC suggested a workaround - create /etc/ifconfig.<ifname> with link <correct_MAC> active line. Sounds like adding a second layer of spoofing, don't like it but tried anyway - the address: line in ifconfig output changed, the link: line didn't. dhcpcd still not getting DHCP lease with this configuration - tries to solicit 2 times, gives up and assigns IP4LL 169.* address. Changing dhcpcd options to ones recommended in dhcpcd.conf for less RFC-conforming DHCP servers didn't help. Is there a way to force NetBSD to simply use MAC provided by firmware?

Turning RockPro64 into a "normal" UEFI PC

Props to Marcin Juszkiewicz for writing post series on boot standards in ARM. The goal is to get rid of the SD card holding U-boot and boot files by flashing UEFI-enabled U-boot to SPI and boot without fiddling with offsets and the like. U-boot should reside in SPI and nowhere else, scan all connected media on boot (USB sticks and SATA drives in my case) and present boot options.

ATF requires arm-none-eabi-gcc to build, it's not in Arch ARM repos, but we can steal packages from Manjaro (find a Manjaro mirror close to you). Building them on Arch ARM from AUR packages is problematic because arm-none-eabi-newlib requires arm-none-eabi-gcc to build and vice versa.

https://mirror.truenetwork.ru/manjaro/arm-stable/extra/aarch64/arm-none-eabi-newlib-4.2.0.20211231-1-any.pkg.tar.zst

https://mirror.truenetwork.ru/manjaro/arm-stable/extra/aarch64/arm-none-eabi-gcc-12.1.0-1-aarch64.pkg.tar.zst

https://mirror.truenetwork.ru/manjaro/arm-stable/extra/aarch64/arm-none-eabi-binutils-2.38-1-aarch64.pkg.tar.zst

git clone --depth=1 -b v2.13.0 https://github.com/ARM-software/arm-trusted-firmware.git
find . -name '*.bin' -exec rm -vf '{}' \;
make -j $(nproc) CC=gcc PLAT=rk3399 bl31

U-boot:

git clone --depth=1 -b v2025.04 https://source.denx.de/u-boot/u-boot.git
export BL31=/path/to/built/bl31.elf
make mrproper && make rockpro64-rk3399_defconfig
make menuconfig

In the menu I changed some things to enable USB before boot (to be able to interrupt autoboot with keyboard), disable all networking (don't need it, smaller U-boot), increase boot delay, present boot menu, add some commands, lower baud rate. Here are the variables in .config, but don't just copy them to your .config - search for them in menuconfig and change there, the menu will take care of dependencies.

CONFIG_BOOTDELAY=20
CONFIG_BOOTCOMMAND="bootflow scan -lbm"
CONFIG_CMD_ERASEENV=y
CONFIG_CMD_NVEDIT_LOAD=y
CONFIG_NO_NET=y
CONFIG_BAUDRATE=115200
CONFIG_USE_PREBOOT=y
CONFIG_PREBOOT="usb start"
CONFIG_PREBOOT_DEFINED=y
make -j $(nproc)

Writing to SD card first to test it (from doc/README.rockchip in U-boot source, "Option 3: Package the image with TPL"):

dd if=idbloader.img of=/dev/sdX seek=64
dd if=u-boot.itb of=/dev/sdX seek=16384
sync

Flashing to SPI used to be a complex process (see Gentoo Wiki link), much simpler nowadays, see doc/board/rockchip/rockchip.rst in U-boot source. Create a partition with FAT filesystem on the same SD card, place u-boot-rockchip-spi.bin on it, reboot, get to U-boot cmdline and:

sf probe
load mmc 1:1 $kernel_addr_r u-boot-rockchip-spi.bin
sf update $fileaddr 0 $filesize

If sf probe returns an error or booting from SPI fails, short pins 23 and 25 on GPIO to temporarily disable SPI. After booting disconnect them again and erase SPI to at least get SD boot working.

U-boot environment section on SPI might be empty, broken or contain wrong variables. To get default environment for installed version of U-boot, remove situational partitions variable and save the environment to SPI, run in U-boot cmdline:

env default -a
env delete partitions
env save

I then created a 1 GiB EFI partition on SATA drive (type ef00, FAT filesystem), moved boot_arch and extlinux directories from SD card to it, removed SD card and rebooted. U-boot enumerates storage devices on boot, looks inside filesystems for various predefined files - EFI executables, boot.scr etc, including extlinux/extlinux.conf file, if present checks and adds them to boot menu. In my case it ran extlinux.conf from SATA same way as when it was on SD card. This is not a EFI boot, files could've been placed on any EXT4/FAT filesystem - U-boot just doesn't care that it's on EFI partition. Next to find BSD/Linux distros that provide UEFI-capable ARM64 images and try to install them with a typical "burn image to USB pen drive -> reboot -> go through installer" procedure.

https://marcin.juszkiewicz.com.pl/2021/03/14/u-boot-and-generic-distro-boot/

https://marcin.juszkiewicz.com.pl/2020/06/17/ebbr-on-rockpro64/

https://wiki.gentoo.org/wiki/PINE64_ROCKPro64/Installing_U-Boot

Mainline U-Boot 2022.04 on RockPro64 - works from SD card

Had to downgrade gcc to version 11 on Arch ARM (otherwise ATF won't build), then (using git repos):

arm-trusted-firmware $ git checkout v2.7.0
arm-trusted-firmware $ find . -name '*.bin' -exec rm -vf '{}' \;
arm-trusted-firmware $ make PLAT=rk3399 bl31
arm-trusted-firmware $ cd ../u-boot
u-boot $ git checkout v2022.04
u-boot $ make mrproper && make rockpro64-rk3399_defconfig
u-boot $ make BL31=../arm-trusted-firmware/build/rk3399/release/bl31/bl31.elf

Create a GPT partition on SD card to hold boot files:

/boot1GiBSD/
├── boot_arch
│   ├── dtbs/
│   ├── Image
│   └── initramfs-linux.img
└─── extlinux
    └── extlinux.conf

Write U-boot image to the same SD card (it will not break GPT) - directions in doc/README.rockchip in U-boot source tree, "Booting from an SD card on RK3399", Option 3. Works fine.

SPI flash doesn't work though, sf probe complains:

unrecognized JEDEC id bytes: ff, ff, ff

RockPro64: booting OS from SATA, installing Arch ARM

If U-boot problems with RockPro64 are fixed then most of this cumbersome setup will become unnecessary and this article will get 5 times shorter. Right now it might be easier to use this fork, author claims SATA support (I didn't try it). I chose upstream U-boot to send bugs to developers.

Currently the master branch of U-boot has 2 problems with RockPro64 - SATA doesn't start and the board is not booting since commit 3ae64582, hangs after loading the kernel. Wrote to developers about the second issue, there seems to be some interest. If it's fixed I'll report SATA issue next. There is a patch with PCIe code from OpenBSD, but the description is not encouraging.

So right now booting upstream U-boot is rather tricky:

  • U-boot is on SD card
  • There is a small flash drive in USB2 port with ext4 filesystem to store /extlinux/extlinux.conf, kernel and initrd
  • Other partitions (/, swap, /home etc) are on SATA devices

Commands below are for Arch or Parabola on amd64. There are cross-compilation packages in Arch repos - let's install them:

# pacman -S arm-none-eabi-gcc aarch64-linux-gnu-gcc dtc

Download latest ATF release (currently arm_cca_v0.3), remove binary files and build:

curl -O https://codeload.github.com/ARM-software/arm-trusted-firmware/tar.gz/refs/tags/arm_cca_v0.3
tar -xzf arm_cca_v0.3
cd arm-trusted-firmware-*
find . -name '*.bin' -exec rm -vf '{}' \;
make realclean
make CROSS_COMPILE=aarch64-linux-gnu- PLAT=rk3399
cd ..

Now we can build U-boot 2020.07, to boot from SD we need u-boot-rockchip.bin:

curl -O https://ftp.denx.de/pub/u-boot/u-boot-2020.07.tar.bz2
tar -xjf u-boot-2020.07.tar.bz2
cd u-boot-2020.07/
export BL31=../arm-trusted-firmware-*/build/rk3399/release/bl31/bl31.elf
export CROSS_COMPILE=aarch64-linux-gnu- CC=aarch64-linux-gnu-gcc
make mrproper
make rockpro64-rk3399_defconfig
make

A convenient distro to get started is Armbian. Download Bullseye from here, write it to SD card (/dev/sde here, needs to be 4Gb or larger):

unxz -dc Armbian_21.08.1_Rockpro64_bullseye_current_5.10.60.img.xz > /dev/sde

Boot RockPro64 with it, download ArchLinux ARM "Generic" root tarball from here. Create a 20Gb partition on SATA device, mount it to /mnt, unpack Arch there:

tar -xzf ArchLinuxARM-aarch64-latest.tar.gz -C /mnt

I wrote the following steps from memory, please contact me if something doesn't work, I'll try to fix it. Mount the filesystem on USB flash drive to /mnt_usb, move boot files from SATA to USB so that U-boot can see them:

mkdir /mnt_usb/boot_arch
cp -r /mnt/boot/* /mnt_usb/boot_arch/
lsblk --fs -oMOUNTPOINTS,UUID|awk '/^\/mnt_usb\s/ {print $2}' # UUID_USB, see below

Add entries to /etc/fstab: 1) to mount the filesystem on USB and 2) to bind the boot_arch directory on USB filesystem to /boot so that pacman uses it during kernel upgrades:

UUID=<UUID_USB> /bootUSB ext4 defaults,noatime 0 1
/bootUSB/boot_arch /boot    none    bind 0 0

Add Arch boot entry to extlinux/extlinux.conf on USB filesystem using PARTUUID of SATA partition (get it with lsblk -o+PARTUUID):

timeout 60
menu title Muh boot options
default Arch_Linux_SSD
label Arch_Linux_SSD
  kernel /boot_arch/Image
  append root=PARTUUID=111111-1111-111 nowatchdog console=ttyS0,115200n8 console=tty1 rootwait rw earlyprintk LANG=en_US.UTF-8
  fdtdir /boot_arch/dtbs
  initrd /boot_arch/initramfs-linux.img

Write U-boot to SD card, if you have a spare one (any size) - use it so you will still have a working one with Armbian. In this example SD card is /dev/sde, check it on your PC with lsblk:

dd if=/dev/zero of=/dev/sde bs=4096 count=2600
dd if=/path/u-boot-git/u-boot-rockchip.bin of=/dev/sde seek=64
sync

Insert it into RockPro64, power on - if all went well you'll see U-boot 2020.07 and Arch will boot from SSD. Add pacman Arch ARM keys as described here.

Download linux-libre-firmware from Parabola repo, install it instead of linux-firmware:

RemoteFileSigLevel = Never # in /etc/pacman.conf
pacman -U https://www.parabola.nu/packages/libre/x86_64/parabola-keyring/download
RemoteFileSigLevel = Required DatabaseOptional # in /etc/pacman.conf
pacman -U linux-libre-firmware-1:1.4-1-any.pkg.tar.xz

I added -mtune=cortex-a72.cortex-a53 to CFLAGS in /etc/makepkg.conf so GCC optimizes makepkg-built packages for this CPU architecture.

What works:

  • Bluetooth (headphones)
  • 1080p video playback
  • WiFi 802.11n Access Point
  • Sharing Internet connection via 1 Gbit/s cable (crossover)
  • Battle for Wesnoth: it's a big project and it's not in Arch ARM repos, so I decided to build it, see if it'll work on aarch64. Works like a charm: asp export wesnoth ; cd wesnoth ; sed -i 's/^arch=.*/arch=(aarch64)/' PKGBUILD ; makepkg, profit. Thanks to Wesnoth developers for portable code. Made a thread about it on Arch ARM, if I have time will make a github PR to add Wesnoth to Community repo, it looks simple enough
  • All the usual software I use on a home PC

Next on the list - linux-libre. I'll have to either build it for Arch/aarch64 or switch to a distro where it's already packaged. This will take some time.

PC on Libre/Free Software: hardware selection, RockPro64

I've been using a libreboot-ed ThinkPad T500, but USB system started to show its age - only one port works, and it's a random one on each boot.

Started to look for a replacement with these requirements:

  • Supported by Libre/Free Software - linux-libre, upstream U-boot without vendor blobs
  • Ability to install OS to SATA/NVMe drive. SD/eMMC/USB are too slow/unreliable
  • Sharing Internet connection via twisted pair and WiFi
  • Working Bluetooth
  • Upstream software: linux-libre, U-boot. I don't want to install forks of outdated versions.

Main candidates:

  • Blackbird, POWER9 CPU. Has a BMC, all firmware sources are available. Firmware for built-in NIC is in development, described here. Drawbacks are price and the fact that you can't order it (logistical problems due to covid, according to Talospace)
  • MNT Reform. ARM64 laptop, no vendor blobs except 16KiB one to initialize RAM. Shipping next year
  • RockPro64 - RK3399 SBC, 4G RAM, good-enough CPU (2xA72 + 4xA53), PCIe x4, 4xUSB, 1xRJ-45 (1 Gbit/s). Gentoo approves
  • Gigabyte GA-G41M-ES2L for libreboot treatment
  • HoneyComb LX2 aarch64, 16 A72 cores, a full-fledged motherboard. More expensive than RockPro64 (it has much more functionality). I couldn't find if it requires vendor blobs, and it's not in U-boot repo (by name)

I chose RockPro64. Pine64 sells a SATA card for it but it uses an ASM chip. Judging by forum threads and my experience ASM chips are not reliable - my mPCIe card on ASM1061 works with HDD but not SSD for some reason. Found this card - 4 ports, Marvell 88SE9215 chip, supported by Linux. Bluetooth and WiFi adapters that do not require vendor blobs were found on H-node (an excellent resource).

Resulting BOM:

  • SATA controller PCE4SAT-M02 (PCIe)
  • Bluetooth controller Asus BT400 (USB)
  • WiFi D-Link DWA-126 (USB) - Access Point mode
  • Ethernet D-Link Dub-E100 100Mbit/s (USB) - for ISP connection
  • 2 USB hubs
  • Chipset cooler Zalman ZM-NB32J - fits, here is an old review
  • Aluminum box 155x85x120 mm
  • Regular PC PSU (I already had one)

DIY power connector Molex -> DC barrel:

power-molex-barrel.jpg

Bolted a small aluminum plate to SATA card to hold the cables:

rockpro64-sata-1


rockpro64-sata-2


More photos:

rockpro64-hw-2


rockpro64-hw-2


rockpro64-hw-2


rockpro64-hw-3


rockpro64-hw-4


rockpro64-hw-5


Next up: installing U-boot and Arch Linux ARM.