I have just finished adding support for psyopsOS to the Raspberry Pi. Along the way I learned a few things about how device trees and the Pi that surprised me.
U-Boot needs its own device tree
The Pi’s firmware has a pretty nice Linux loader, but a feature of psyopsOS is A/B updates, and to support selection between two different runtime operating systems, I needed to use U-Boot1.
U-Boot needs its own device tree.
Device trees come from the Linux kernel,
so U-Boot is tied to the Linux kernel source.
Typically, a U-Boot binary for Raspberry Pi will set a default dtb file at compilation time,
maybe bcm2711-rpi-4-b.dtb
for the Raspberry Pi 4B.
When booting U-Boot on the Raspberry Pi,
the device_tree=...
configuration setting
in the Pi’s config.txt
will override whatever U-Boot has set as its default.
The U-Boot device tree can be different from the kernel device tree
When booting a kernel, you can specify a different device tree than the one used in U-Boot. Which is good for psyopsOS, because the whole point of the psyopsOS A/B system is to enable upgrading the non-booted A/B side, including the kernel, without changing the bootloader.
U-Boot needs a device tree like the one it was compiled with, NOT necessarily like the one used in the kernel that it’s booting.
VideoCore needs device tree overlays (sometimes)
VideoCore is the proprietary Broadcom GPU firmware.
On the Pi, it is loaded first,
before any bootloaders like U-Boot, and before the Linux kernel.
(VideoCore can also load a Linux kernel directly,
which is the usual way to boot a Pi.)
The config.txt
file on a Raspberry Pi boot partition configures VideoCore.
The device tree system has base device trees for a single device,
like bcm2711-rpi-4-b.dtb
for the Raspberry Pi 4B,
and optional overlays
that can be applied on top of those bases.
For psyopsOS, I want to have a console listening on the serial port
so that I can do emergency maintenance on any machine with a laptop and a serial adapter
without a full monitor and keyboard.
I’m using the primary PL011 UART (which Linux sees as /dev/ttyAMA0
),
which is used by the Bluetooth subsystem by default.
I have to use an overlay to disable bluetooth to access this UART.
There is a device tree overlay file called disable-bt.dtbo
that does this.
When booting a Pi the normal way,
where VideoCore loads Linux directly based on config.txt
,
you need to have /overlays/disable-bt.dtbo
on the boot filesystem,
and set this in config.txt
:
overlay=disable-bt
When I had VideoCore load U-Boot, I took this out, because I applied the overlay inside U-Boot. But my kernel could never see the serial port.
It turns out that the disable-bt overlay is needed both by the kernel and by VideoCore.
When it is set in config.txt
,
VideoCore reconfigures the hardware to give the GPIO pins to the UART,
rather than to Bluetooth.
(I think this is called “pin muxing”, but I don’t understand it very well.)
Example with separate dtb(o) files for U-Boot and Linux
So in my boot partition, I have:
u-boot.bin
, the U-Boot binaryu-boot.dtb
, the Raspberry Pi 4B device tree base file that U-Boot was built with.overlays/disable-bt.dtbo
, the device tree overlay that disables Bluetooth and changes the pin muxes so that GPIO 14/152 are serial TX/RX. This was built from the same kernel sources that builtu-boot.dtb
. Note that this path is special. The device tree base file can be set withdevice_tree=...
inconfig.txt
, but theoverlay=...
lines do not take a path, they take a name likedisable-bt
, which VideoCore takes to mean/overlays/disable-bt.dtbo
.- A
config.txt
file that references these:device_tree=u-boot.dtb dtoverlay=disable-bt kernel=u-boot.bin enable_uart=1 uart_2ndstage=1
And in each psyopsOS A/B side, I have:
kernel
, a Linux kernel that U-Boot can load (from the Alpinelinux-rpi
package, although it could be hand built instead)initramfs
, an initramfs that was built alongside the kernel/dtbs/bcm2711-rpi-4-b.dtb
, a device tree base file for the Raspberry Pi 4B that was built with the kernel./dtbs/overlays/disable-bt.dtbo
, a device tree overlay file that disables bluetooth and was built alongside the kernel. This path is not special, because I load an absolute path to the dtbo in the U-Boot script.- … other files not relevant here
The A/B kernel, dtb, and dtbo might be the same version as the ones on the boot partition, but they don’t have to be. Over time, as I upgrade the A and B sides, those versions will change. The boot volume will change much less often, if at all.
-
in the future, UEFI might be a viable option, but at the moment it has a few problems: it can only address 3GB of RAM without changing the configuration, the configuration cannot be changed outside of the UEFI UI at boot time, and updating UEFI wipes out all local changes. I don’t have any Pi 5 devices, but UEFI also doens’t support those. ↩︎
-
The GPIO numbers are not the same as the Pi’s pin numbers. GPIO 14/15 are on Pi pinout pins labeled 8/10. https://pinout.xyz/ is an excellent reference. ↩︎