Installing and updating FreeBSD 11.0 release on a Raspberry Pi

FreeBSD runs great on a Raspberry Pi, but the official images are all stable or current branches and there is no support for binary updates, i.e. freebsd-update. So I tried to figure out how to install the latest release version and keep it updated as well, without starting from scratch with a new image every time.

While it is possible to build a kernel and world on the Raspberry Pi itself, it takes about a week to finish (trust me, I tried) and you need a much larger /tmp on a disk or share for it to work in the first place. Since FreeBSD can also be cross-compiled, I decided to use my Xeon box to do the heavy lifting and then just install it on the Raspberry Pi.

So let’s get started with getting the source and building it with armv6 as a target architecture on an amd64 system. I assume that /usr/src is empty, so first get the source. We want the Release Engineering (RelEng) branch, which is the latest release version, including security updates:

# cd /usr/src
# svn checkout https://svn.freebsd.org/base/releng/11.0

After getting the code, we can keep it updated with svn update. I recommend subscribing to the FreeBSD-Announce mailing list, to get a notification every time there is an update.

Now we can build the world and kernel, but with armv6 for the Raspberry Pi as the target architecture:

# make TARGET_ARCH=armv6 UBLDR_LOADADDR=0x2000000 buildworld
# make TARGET_ARCH=armv6 KERNCONF=RPI-B buildkernel

Please note that the UBLDR_LOADADDR variable has to be set, otherwise the kernel won’t boot. Also, the kernel configuration is for the first Raspberry Pi, for the newer Raspberry Pi 2 use KERNCONF=RPI2. This takes a while, but it can be sped up by using the -j<n> parameter with make, where n equals the number of CPU cores + 1, e.g. -j5 for a quad core. My Xeon with an SSD needs about 30 minutes to build the world and kernel.

There are basically several ways to install everything on the Raspberry Pi, but I could only figure out to get one working. The most convenient would be to mount /usr/src and /usr/obj via NFS on the Raspberry Pi and install everything locally. That didn’t work for me. The compiled files weren’t executable, probably because it’s a cross-compiled and not a native build. Another method is to mount / of the Raspberry Pi on the build box via NFS (yay security) and install from the build box. That did work for the kernel, but not for world. The problem is that most files are in use and therefore can’t be overwritten. That’s why the FreeBSD handbook advises to use single user mode, but then there is no networking. Also my USB keyboard wasn’t recognized early enough in the boot process to enter single user mode. So in the end, I just pulled the SD card and put it in a card reader plugged into the build box. This also turned out to be much faster than via network. Mounting the SD card locally is as simple as it can be:

# mount /dev/da0s2a /mnt

Then install kernel and install world can be done like in the handbook, but with a few added variables and parameters:

# make TARGET_ARCH=armv6 KERNCONF=RPI-B DESTDIR=/mnt installkernel
# mergemaster -p -A armv6 -D /mnt
# make TARGET_ARCH=armv6 DESTDIR=/mnt installworld
# mergemaster -iF -A armv6 -D /mnt
# make TARGET_ARCH=armv6 DESTDIR=/mnt delete-old
# make TARGET_ARCH=armv6 DESTDIR=/mnt delete-old-libs

That’s it! Now unmount the SD card, plug it into the Raspberry Pi again, and be greeted with the current release version with the lastest patch:

# uname -a
FreeBSD raspberrypi 11.0-RELEASE-p8 FreeBSD 11.0-RELEASE-p8 #3 r315180: Sun
Mar 12 23:15:58 CET 2017     solence@raspberrypi:/usr/obj/arm.armv6/usr/
src/sys/RPI-B  arm

# freebsd-version
11.0-RELEASE-p8