How to install FreeBSD on a Raspberry Pi (or: How to cross compile FreeBSD/arm)

Preface

This howto is based on various snippets I found on the web and spiced with my own experiences. I found Oleksandr Tymoshenko's build-pi-image.sh script very helpful. You can find his blog at http://kernelnomicon.org/.

Requirements

Steps

  1. For the sake of ease log in as user root and start a Bourne compatible shell, e.g. /bin/sh or /usr/local/bin/bash
  2. If your system doesn't have an auditdistd user (if id auditdistd returns no such user), then create it:
    pw useradd auditdistd -u 78 -g 77 -c "Auditdistd unprivileged user" -d /var/empty -s /usr/sbin/nologin
  3. Download the boot loader:
    fetch http://people.freebsd.org/~gonzo/arm/rpi/freebsd-uboot-20121129.tar.gz
  4. Either install subversion as binary package
    pkg_add -r subversion
    or as port:
    make -C /usr/ports/devel/subversion install clean
  5. Fetch the latest FreeBSD source code:
    svn co svn://svn.freebsd.org/base/head
  6. Change to the newly created directory:
    cd head
  7. Determine how much of your Raspberry's memory is available to FreeBSD, e.g. mine has 512 MB. Its integrated graphics card takes 64 MB by default. Thus FreeBSD gets 448 MB or 469762048 bytes of ram. Written in hexadecimal this is 0x1C000000. You can use this shell one-liner to calculate that number:
    printf "%X\n" $(((512-64)*1024*1024))
    Edit sys/boot/fdt/dts/bcm2835-rpi-b.dts sys/boot/fdt/dts/rpi.dts. Lines 503-506 38-42 contain something like this:
    memory {
    	device_type = "memory";
    	reg = <0 0x08000000>; /* 128 MB, Set by VideoCore */
    
    };
    
    Replace that by:
    memory {
    	device_type = "memory";
    	reg = <0 0x1C000000>; /* 448 MB */
    };
    
  8. If you like, you can add support for IPv6 in the kernel although IPv6 doesn't seem to work on FreeBSD/arm (-HEAD, as of January 2013) (Update 2013-02-01: IPv6 is functional on FreeBSD/arm, see this commit message). Edit sys/arm/conf/RPI-B and add the following lines:
    options		INET6			# IPv6 communications protocols
    options		TCP_OFFLOAD		# TCP offload
    options		PROCFS			# Process filesystem (requires PSEUDOFS)
    
    device		vlan			# 802.1Q VLAN support
    
    # libalias library, performing NAT
    options		LIBALIAS
    
    options		IPFIREWALL			#firewall
    options		IPFIREWALL_DEFAULT_TO_ACCEPT	#allow everything by default
    options		IPFIREWALL_NAT			#ipfw kernel nat support
    options		IPDIVERT			#divert sockets
    
  9. As of 2013-01-26, this step doesn't seem to be needed anymore
    Edit Makefile.incl. Around lines 920 and 928 (below stage 2.3: build tools), comment out both lines starting with cd ${KRNLOBJDIR}/${_kernel} and cd ${KERNSRCDIR}/modules/aic7xxx/aicasm, respectively
  10. Set an environmental variable which contains the target's cpu architecture:
    export TARGET_ARCH=armv6
  11. Build the tools (compilers and so) which are required to build FreeBSD (this takes about half an hour depending on your cpu's speed):
    make kernel-toolchain
  12. Build the kernel (this takes also about half an hour):
    make KERNCONF=RPI-B WITH_FDT=yes buildkernel
  13. Compile the userland (this takes really long, up to several hours):
    make MALLOC_PRODUCTION=yes buildworld
  14. Get the build environment variables:
    buildenv=`make buildenvvars | sed 's/MACHINE_ARCH=armv6/MACHINE_ARCH=arm/'`
  15. Change to sys/boot:
    cd sys/boot
  16. Build the boot loader (U-Boot, the Universal Boot Loader):
    eval $buildenv make MK_NAND=no UBLDR_LOADADDR=0x2000000 clean obj all
  17. Change back to /root/head:
    cd -
  18. Create the disk image which will be transferred to the sd card. FreeBSD needs 260 MB at a bare minimum. Everything below 300 MB is insane. 512 MB are ok:
    dd if=/dev/zero of=/root/arm.img bs=1m count=512
  19. Attach that disk image:
    mdfile=`mdconfig -a -t vnode -f /root/arm.img`
  20. Create a MBR partitioning scheme:
    gpart create -s MBR "$mdfile"
  21. Add a small MSDOS partition which will contain the boot loader:
    gpart add -s 32m -t '!12' "$mdfile"
  22. Mark that partition active:
    gpart set -a active -i 1 "$mdfile"
  23. Create the filesystem:
    newfs_msdos -L boot -F 16 /dev/"$mdfile"s1
  24. Mount that filesystem:
    mount -t msdosfs /dev/"$mdfile"s1 /mnt
  25. Extract the boot loader to the boot partition:
    tar -C /mnt -xvzf /root/freebsd-uboot-20121129.tar.gz
  26. Copy the U-Boot loader to the boot partition:
    cp -iv /usr/obj/arm.armv6/root/head/sys/boot/arm/uboot/ubldr /mnt/
  27. Copy the device database to the boot partition:
    cp -iv /usr/obj/arm.armv6/root/head/sys/RPI-B/bcm2835-rpi-b.dtb /mnt/devtree.dat
    cp -iv /usr/obj/arm.armv6/root/head/sys/RPI-B/rpi.dtb /mnt/devtree.dat
  28. Edit /mnt/config.txt and add the following lines:
    device_tree=devtree.dat
    device_tree_address=0x100
    disable_commandline_tags=1
    
    You can assign more memory to the graphics card, e.g. 128 MB instead of 64 MB:
    gpu_mem=128
    Note that you have to recompile the kernel if you increase the graphics card's memory (see step 7)
  29. Unmount the boot partition:
    umount /mnt
  30. Add a FreeBSD partition:
    gpart add -t freebsd "$mdfile"
  31. Create a BSD partitioning scheme inside that partition:
    gpart create -s BSD "$mdfile"s2
  32. Add a UFS partition which becomes the root partition:
    gpart add -t freebsd-ufs "$mdfile"s2
  33. Create the UFS filesystem:
    newfs -U -j /dev/"$mdfile"s2a
  34. Mount the root partition:
    mount /dev/"$mdfile"s2a /mnt
  35. Install the kernel:
    make KERNCONF=RPI-B DESTDIR=/mnt installkernel
  36. Install everything else:
    make DESTDIR=/mnt DB_FROM_SRC=1 installworld distribution
  37. Add the following line to /mnt/boot/loader.rc:
    fdt addr 0x100
  38. FreeBSD has to know where its root filesystem resides. Add this to /mnt/etc/fstab:
    /dev/mmcsd0s2a / ufs rw,noatime 1 1
  39. Add this to /etc/fstab to mount /proc:
    procfs /proc procfs rw 0 0
  40. /mnt/etc/rc.conf should contain something similiar to this:
    hostname="raspberry.your.domain"
    dumpdev="NO"
    ifconfig_ue0="192.168.0.42 netmask 255.255.255.0"
    defaultrouter="192.168.0.1"
    sshd_enable="YES"
    ntpdate_enable="YES"
    ntpd_enable="YES"
    
    Instead of ifconfig_ue0 and defaultrouter you could try dhcp:
    ifconfig_ue0="DHCP"
  41. If you don't use dhcp, then configure at least one nameserver in /mnt/etc/resolv.conf:
    nameserver 192.168.0.23
  42. If your nameserver doesn't resolve the Raspberry's hostname, then add this to /mnt/etc/hosts:
    192.168.0.42	raspberry.your.domain raspberry
  43. Edit /mnt/etc/ssh/sshd_config, go to line 44, and change
    #PermitRootLogin no
    to
    PermitRootLogin yes
  44. Either add your public ssh key to /mnt/root/.ssh/authorized_keys:
    mkdir -m 0700 /mnt/root/.ssh
    cat ~/.ssh/id_rsa.pub >>/mnt/root/.ssh/authorized_keys
    chmod 0600 /mnt/root/.ssh/authorized_keys
    
    Or permit ssh access without being prompted for a password (Insecure! Don't do this in a hostile environment!): Edit /mnt/etc/pam.d/sshd, go to line 12, comment out
    auth	required	pam_unix.so	no_warn try_first_pass
    and add
    auth	required	pam_permit.so
  45. Set a proper timezone:
    cp -iv /mnt/usr/share/zoneinfo/Europe/Berlin /mnt/etc/localtime
  46. Unmount the root partition:
    umount /mnt
  47. Detach the disk image:
    mdconfig -d -u "$mdfile"
  48. Shove the sd card into the card reader and connect it to your FreeBSD system
  49. Use the output of dmesg to check which device node represents your sd card, e.g. da1. That device node should not appear in the output of mount
  50. Write the image to the sd card:
    dd if=/root/arm.img of=/dev/da1 bs=1m
  51. Eject the sd card and insert it into the Raspberry
  52. Optionally, connect a monitor or TV to your Raspberry to watch FreeBSD booting
  53. Power on the Raspberry
  54. Remotely log into your Raspberry, e.g.:
    ssh root@192.168.0.42
  55. Assign a password to the root account:
    passwd
  56. If you've edited /etc/pam.d/sshd (see step 44), undo that now.