Michael's Daemonic Doodles

...blogging bits of BSD

Running FreeBSD on a Raspberry Pi3 using a custom image created with crochet and poudriere

I used to download images from raspbsd.org and install binary packages using pkg on the device. This has some disadvantages:

  • The image there is updated infrequently
  • Installing using pkg is relatively slow
  • Binary packages aren't always in sync when running CURRENT
  • Building things yourself gives you more control

Using crochet and poudriere it's quite easy to create an image with pre-installed packages, which helps a lot when provisioning a large number of devices. Packages are cross-compiled using qemu, which is slow, but still much faster than building on the device itself.

Preconditions

The procedure below was done on a system running FreeBSD 12.0-BETA1 r339435 GENERIC amd64 using binary packages through pkg. The resulting image is 6GB (6 * 10^9 bytes) in size and will expand on first boot using growfs. It uses DHCP on its ethernet port, remote ssh credentials are user raspberry, password raspberry.

The procedure was tested using sh and bash, some constructs (like "EOF") work differently in other shells.

Install basic packages

pkg install git poudriere qemu-user-static rpi-firmware subversion u-boot-rpi3

Checkout crochet

git clone https://github.com/freebsd/crochet
cd crochet

Create list of packages to add to the image

Only a few basic packages to serve as an example:

cat >pkglist <<EOF
editors/joe
ftp/curl
mail/opensmtpd
security/sudo
shells/bash
sysutils/tmux
EOF

Configure poudriere

Assumes a zpool named zroot and enables ccache:

sysrc -f /usr/local/etc/poudriere.conf ZPOOL=zroot
sysrc -f /usr/local/etc/poudriere.conf CCACHE_DIR=/var/cache/ccache
mkdir -p /var/cache/ccache
mkdir -p /usr/ports/distfiles

Configure binary image activator

This is key to automatically invoke qemu-aarch64-static to run ARM binaries:

binmiscctl add aarch64 \
  --interpreter "/usr/local/bin/qemu-aarch64-static" \
  --magic "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00" \
  --mask  "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" \
  --size 20 --set-enabled

Important

This loads the imgact_binmisc kernel module. You need to do this again after each reboot. The remainder of the procedure depends on this.

Create poudriere jail and ports tree

poudriere jail -c -j 12aarch64 -a arm64.aarch64 -v 12.0-BETA1
poudriere ports -c -p rpi3

Fine tune package build make.conf

For this build we're using security/libressl instead of base's openssl (mail/opensmtpd wouldn't build with system openssl at the moment):

cat >/usr/local/etc/poudriere.d/12aarch64-make.conf <<"EOF"
DEFAULT_VERSIONS+=  ssl=libressl
.if ${.CURDIR:M*/ftp/curl}
OPTIONS_FILE_UNSET+=TLS_SRP
.endif
EOF

Build packages

This takes a long time:

poudriere bulk -j 12aarch64 -p rpi3 -f pkglist

Configure crochet

Important

Having qemu-user-static installed and adding it through the configuration below are required to end up with correct packages installed. You also need to make sure the binary image activator is configured correctly like described above, otherwise POST_INSTALL scripts will fail (there will be error messages, but the image build will finish anyway and you'll basically end up with a corrupted package installation).

cat >config.rpi3.sh <<"EOF"
board_setup RaspberryPi3
option ImageSize 6gb
option Growfs
option User raspberry
FREEBSD_SRC=${TOPDIR}/src
option PackageInit http://localhost:8080
option Package $(cat pkglist)
add_qemu ( ) {
  echo "Installing qemu-aarch64-static"
  mkdir -p ${BOARD_FREEBSD_MOUNTPOINT}/usr/local/bin
  cp -av /usr/local/bin/qemu-aarch64-static ${BOARD_FREEBSD_MOUNTPOINT}/usr/local/bin/.
}
remove_qemu ( ) {
  echo "Removing qemu-aarch64-static"
  rm -v ${BOARD_FREEBSD_MOUNTPOINT}/usr/local/bin/qemu-aarch64-static
}
PRIORITY=50 strategy_add $PHASE_FREEBSD_OPTION_INSTALL add_qemu
PRIORITY=150 strategy_add $PHASE_FREEBSD_OPTION_INSTALL remove_qemu
EOF

Check out FreeBSD source tree

svn checkout https://svn.freebsd.org/base/stable/12 src

Activate python web server to serve local packages

This will serve packages created by poudriere over http (binding to 0.0.0.0:8080):

cd /usr/local/poudriere/data/packages/12aarch64-rpi3
python2.7 -m SimpleHTTPServer 8080 &
cd -

Note

Don't forget to stop it again once you're done. You can also create a more proper setup using ssh or https (e.g. nginx) and use that to serve package updates to your devices.

Build the image

This will take a while:

./crochet.sh -c config.rpi3.sh

The resulting image can be written to an SD card using dd:

dd if=work/FreeBSD-aarch64-12-GENERIC-RaspberryPi3.img bs=1m of=/dev/daX

(replace daX with the actual device name of your SD card).