Guix System On A Raspberry Pi 3b (February, 2024)

I've been running my trusty Raspberry Pi Single-Board Computer as a DNS, DHCP and Gitolite-Server at home since around 2016. It's been running on a minimal Raspbian and later on NixOS image, but I always wanted to switch it over to Guix System, to be able to do most of the configuration of my small server in Guile using Guixes tooling for maintenance and systems administration. I'll briefly describe what I did to succesfully boot Guix System in the following. I won't talk about the actual usage of Guix as a DNS, DHCP and Gitolite-Server on a Raspberry Pi for now, but eventually will cover these aspects later.

Firmware Issues

First things first, the FSF lists VideoCore IV based SBCs such as the Raspberry Pi as Single-board computers with fatal flaws. To quote them^1 directly:

Boards based on the Broadcom VideoCore 4 family, such as the Raspberry Pi, require non-free software to startup, although signature checks are not enforced. A free proof-of-concept replacement firmware has been developed, but it is not in a usable state, and development has halted. Until the non-free startup program is fully freed, these boards are useless in the free world.

What's interesting about the Raspberry Pi 3b here, is that it's primary processor, is the Broadcom VPU. When it gets powered on, the ARM core is off, and only gets enabled when the first stage boot ROM is able to read the proprietary second-stage bootloader binary blob (bootcode.bin) from SD-Card.

The second-stage bootloader then looks for a third-stage bootloader in a provided config.txt, which will be u-boot in our case, which then takes care of the rest of the boot process.

There have been efforts to write a libre VPU firmware, but for now we have to rely on a proprietary bootcode.bin.

This being said, the Raspberry Pi is far from being my preferred ARM SBC, as it's pretty reliant on proprietary firmware; but as I already own such a device and its technically working fine, I see no reason replacing it with something else as replacing working hardware isn't really sustainable.

Going The Non-Free Path

Guix System follows the GNU FSDG^2, which means that there won't be and will never be any upstream support for the non-free components required to boot-up the Raspberry Pi.

So to get the best possible support in terms of hardware, I wanted to be as close to the raspberry pi upstream as possible and use:

Luckily I stumbled upon a repository a few month ago where someone already made a packaging attempt for these two things, it can be found here: PantherX/raspberry. The image (as of 2024-02-27) doesn't build out of the box and was written for a later revision of Rasperry Pi. So I'm going to use this as a base with minor modifications.

Building A First System Image

As said before building the unmodified pantherx/raspberry image with:

guix system --target=aarch64-linux-gnu image raspberry-pi.scm --skip-checks --verbosity=3 -K

did fail because of two things:

I applied the following changes to the rpi-kernel.scm:

diff --git a/rpi-kernel.scm b/../guix-mono/projects/raspberrypi/rpi-kernel.scm
index 426cb25..359c88c 100644
--- a/rpi-kernel.scm
+++ b/../guix-mono/projects/raspberrypi/rpi-kernel.scm
@@ -114,22 +114,11 @@
       (format #t "`ARCH' set to `~a'~%" (getenv "ARCH"))
       (when target
-        (setenv "C_INCLUDE_PATH" (string-join
-                                  (cdr (string-split (getenv "C_INCLUDE_PATH") #\:))
-                                  ":"))
-        (setenv "CPLUS_INCLUDE_PATH" (string-join
-                                      (cdr (string-split (getenv "CPLUS_INCLUDE_PATH") #\:))
-                                      ":"))
-        (setenv "LIBRARY_PATH" (string-join
-                                (cdr (string-split (getenv "LIBRARY_PATH") #\:))
-                                ":"))
         (setenv "CROSS_COMPILE" (string-append target "-"))
         (format #t "`CROSS_COMPILE' set to `~a'~%"
                 (getenv "CROSS_COMPILE"))))
     (setenv "KERNEL" "kernel8")
-    (invoke "make" "bcm2711_defconfig")
+    (invoke "make" "bcmrpi3_defconfig")
     (let ((port (open-file ".config" "a"))
           (extra-configuration #$(config->string %default-extra-linux-options)))
       (display extra-configuration port) 

added nss-certs to %base-packages to be able to have applications talk TLS on the raspberry pi in the raspberry-pi.scm that contains the operating-system declaration used by our prospective image:

(packages (append (list nss-certs) %base-packages))

and build the image with:

guix system --no-grafts --target=aarch64-linux-gnu image raspberry-pi.scm --skip-checks --verbosity=3 -K

Finally, I used dd to write it to a microSD card, and afterwards resized the second partition (root partition) on said microSD:

echo ", +" | sfdisk -N 2 /dev/mmcblk0
e2fsck -f /dev/mmcblk0p2 
resize2fs /dev/mmcblk0p2 

to full-size and successfully booted Guix on my Raspberry Pi 3b.

Next Steps

Now being able to boot Guix System on my Raspberry Pi 3b, I'll probably:

I may consider sharing my notes on these topics on here as well.