update default rpi config

This commit is contained in:
Travis Staton 2023-02-24 16:09:45 -05:00
parent 67f257e524
commit 739c404f11
6 changed files with 147 additions and 104 deletions

117
README.md
View file

@ -1,27 +1,22 @@
# raspberry-pi-nix
NixOS modules that make building images for raspberry-pi products
easier. Most of the work in this repository is based on work in
[nixos-hardware](https://github.com/NixOS/nixos-hardware) and
[nixpkgs](https://github.com/NixOS/nixpkgs). Additionally, be aware
that I am no expert and this repo is the product of me fooling around
with some pis.
The primary goal of this software is to make it easy to create
working NixOS configurations for Raspberry Pi products. Specifically,
this repository aims to deliver the following benefits:
This flake provides nixos modules that correspond to different
raspberry-pi products. These modules can be included in nixos
configurations and aim to deliver the following benefits:
1. Configure the kernel, device tree, and u-boot in a way that is
compatible with the hardware.
2. Provide a nix interface to device tree configuration that will be
familiar to those who have used raspberry-pi's config.txt based
configuration.
1. Configure the kernel, device tree, and boot loader in a way that is
compatible with the hardware and proprietary firmware.
2. Provide a nix interface to Raspberry Pi/device tree configuration
that will be familiar to those who have used Raspberry Pi's
[config.txt based
configuration](https://www.raspberrypi.com/documentation/computers/config_txt.html).
3. Make it easy to build an image suitable for flashing to an sd-card,
without a need to first go through an installation media.
The important modules are `overlay/default.nix`, `rpi/default.nix`,
and `rpi/device-tree.nix`. The other modules for i2c, i2s, etc are
mostly wrappers that set common device tree settings for you.
and `rpi/config.nix`. The other modules for i2c, i2s, etc are mostly
wrappers that set `config.txt` settings and enable required kernel
modules.
## Example
@ -46,27 +41,12 @@ mostly wrappers that set common device tree settings for you.
useDHCP = false;
interfaces = { wlan0.useDHCP = true; };
};
hardware.raspberry-pi = {
i2c.enable = true;
audio.enable = true;
fkms-3d.enable = true;
deviceTree = {
dt-overlays = [{
overlay = "imx477"; # add the overlay for the HQ camera
args = [ ];
}];
};
};
};
in {
nixosConfigurations = {
rpi-zero-2-w-example = nixosSystem {
rpi-example = nixosSystem {
system = "aarch64-linux";
modules = [ raspberry-pi-nix.rpi-zero-2-w basic-config ];
};
rpi-4b-example = nixosSystem {
system = "aarch64-linux";
modules = [ raspberry-pi-nix.rpi-4b basic-config ];
modules = [ raspberry-pi-nix.rpi basic-config ];
};
};
};
@ -77,25 +57,64 @@ mostly wrappers that set common device tree settings for you.
An image suitable for flashing to an sd-card can be found at the
attribute `config.system.build.sdImage`. For example, if you wanted to
build an image for `rpi-zero-2-w-example` in the above configuration
build an image for `rpi-example` in the above configuration
example you could run:
```
nix build '.#nixosConfigurations.rpi-zero-2-w-example.config.system.build.sdImage'
nix build '.#nixosConfigurations.rpi-example.config.system.build.sdImage'
```
## Other notes
## The firmware partition
The sd-image built is partitioned in the same way as the aarch64
installation media from nixpkgs: There is a firmware partition that
contains necessary firmware, u-boot, and config.txt. Then there is
another partition that contains everything else. After the sd-image is
built, nixos system updates will not change anything in the firmware
partition ever again. New kernels and device tree configurations will
remain on the nixos partition and be booted by u-boot in the firmware
The image produced by this package is partitioned in the same way as
the aarch64 installation media from nixpkgs: There is a firmware
partition that contains necessary firmware, u-boot, and
config.txt. Then there is another partition (labeled `NIXOS_SD`) that
contains everything else. The firmware and `config.txt` file are
managed by NixOS modules defined in this package. Additionally, NixOS
system activation will update the firmware and `config.txt` in the
firmware partition __in place__. Linux kernels are stored in the
`NIXOS_SD` partition and will be booted by u-boot in the firmware
partition.
So, while you can control device tree params and overlays through your
nixos system configuration, if you want to modify other config.txt
variables this must be done manually by mounting the partition and
modifying the config.txt file.
## `config.txt` generation
As noted, the `config.txt` file is generated by the NixOS
configuration and automatically updated on system activation.
## Firmware partition implementation notes
In Raspberry Pi devices the proprietary firmware manipulates the
device tree in a number of ways before handing it off to the kernel
(or in our case, to u-boot). The transformations that are performed
aren't documented so well (although I have found [this
list](https://forums.raspberrypi.com/viewtopic.php?t=329799#p1974233)
).
This manipulation makes it difficult to use the device tree configured
directly by NixOS as you must know and reproduce the manipulation
performed by the proprietary firmware.
Even if the manipulation was reproduced, some convenience would be
missed out. For example, the firmware can detect hardware during boot
and automatically configure the device tree accordingly, rather than
requiring a NixOS system rebuild with a different device tree for
different hardware. Examples of what I mean by hardware include: the
specific Raspberry Pi device booting the image, connected cameras, and
connected displays.
So, in order to avoid the headaches associated with failing to
reproduce some firmware device tree manipulation, and to reap the
benefits afforded by the firmware device tree configuration, u-boot is
configured to use the device tree that it is given (i.e. the one that
the raspberry pi firmware loads and manipulates). As a consequence,
device tree configuration is controlled via the [config.txt
file](https://www.raspberrypi.com/documentation/computers/config_txt.html).
Additionally, the firmware, device trees, and overlays from the
`raspberrypifw` package populates the firmware partition on system
activation. This package is kept up to date by the overlay applied by
this package, so you don't need configure this. However, if you want
to use different firmware you can override that package to do so.

View file

@ -1,6 +1,6 @@
{ lib, config, pkgs, ... }:
let
cfg = config;
cfg = config.hardware.raspberry-pi;
render-raspberrypi-config = let
render-options = opts:
lib.strings.concatStringsSep "\n" (render-dt-kvs opts);
@ -22,11 +22,11 @@ let
args = render-dt-kvs v;
}) overlays);
render-config-section = k:
{ options, base-dtb-params, dt-overlays }:
{ options, base-dt-params, dt-overlays }:
let
all-config = lib.concatStringsSep "\n" (lib.filter (x: x != "") [
(render-options options)
(render-base-dt-params base-dtb-params)
(render-base-dt-params base-dt-params)
(render-dt-overlays dt-overlays)
]);
in ''
@ -38,48 +38,48 @@ let
(lib.attrsets.mapAttrsToList render-config-section conf);
in {
options = {
raspberrypi-config = let
raspberrypi-config-options = {
options = {
options = lib.mkOption {
type = with lib.types; attrsOf anything;
default = { };
example = {
enable_gic = true;
armstub = "armstub8-gic.bin";
arm_boost = true;
hardware.raspberry-pi = {
config = let
raspberry-pi-config-options = {
options = {
options = lib.mkOption {
type = with lib.types; attrsOf anything;
default = { };
example = {
enable_gic = true;
arm_boost = true;
};
};
};
base-dtb-params = lib.mkOption {
type = with lib.types; attrsOf anything;
default = { };
example = {
i2c = "on";
audio = "on";
base-dt-params = lib.mkOption {
type = with lib.types; attrsOf anything;
default = { };
example = {
i2c = "on";
audio = "on";
};
description = "parameters to pass to the base dtb";
};
dt-overlays = lib.mkOption {
type = with lib.types; attrsOf (attrsOf (nullOr str));
default = { };
example = { vc4-kms-v3d = { cma-256 = null; }; };
description = "dtb overlays to apply";
};
description = "parameters to pass to the base dtb";
};
dt-overlays = lib.mkOption {
type = with lib.types; attrsOf (attrsOf (nullOr str));
default = { };
example = { vc4-kms-v3d = { cma-256 = null; }; };
description = "dtb overlays to apply";
};
};
in lib.mkOption {
type = with lib.types; attrsOf (submodule raspberry-pi-config-options);
};
in lib.mkOption {
type = with lib.types; attrsOf (submodule raspberrypi-config-options);
};
raspberrypi-config-output = lib.mkOption {
type = lib.types.package;
default = pkgs.writeTextFile {
name = "config.txt";
text = ''
# Auto-generated by nix. Modifications will be overwritten.
${render-raspberrypi-config cfg.raspberrypi-config}
'';
config-output = lib.mkOption {
type = lib.types.package;
default = pkgs.writeTextFile {
name = "config.txt";
text = ''
# This is a generated file. Do not edit!
${render-raspberrypi-config cfg.config}
'';
};
};
};
};
}

View file

@ -2,8 +2,10 @@
{ lib, pkgs, config, ... }:
{
imports = [ ../sd-image ./config.nix ];
imports = [ ../sd-image ./config.nix ./i2c.nix ];
# On activation install u-boot, Raspberry Pi firmware, and our
# generated config.txt
system.activationScripts.raspberrypi = {
text = ''
if ! grep -qs '/boot/firmware ' /proc/mounts; then
@ -11,29 +13,28 @@
fi
cp ${pkgs.uboot_rpi_arm64}/u-boot.bin /boot/firmware/u-boot-rpi-arm64.bin
cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/{start*.elf,*.dtb,bootcode.bin,fixup*.dat,overlays} /boot/firmware
cp ${config.raspberrypi-config-output} /boot/firmware/config.txt
cp ${config.hardware.raspberry-pi.config-output} /boot/firmware/config.txt
'';
};
raspberrypi-config = {
pi4 = {
options = {
enable_gic = true;
armstub = "armstub8-gic.bin";
arm_boost = true;
disable_overscan = true;
};
dt-overlays = { vc4-kms-v3d-pi4 = { cma-512 = null; }; };
};
pi02 = { dt-overlays = { vc4-kms-v3d = { cma-256 = null; }; }; };
# Default config.txt on Raspberry Pi OS:
# https://github.com/RPi-Distro/pi-gen/blob/master/stage1/00-boot-files/files/config.txt
hardware.raspberry-pi.config = {
cm4 = { options = { otg_mode = true; }; };
pi4 = { options = { arm_boost = true; }; };
all = {
options = {
# The firmware will start our u-boot binary rather than a
# linux kernel.
kernel = "u-boot-rpi-arm64.bin";
arm_64bit = true;
enable_uart = true;
avoid_warnings = true;
arm_64bit = true;
camera_auto_detect = true;
display_auto_detect = true;
disable_overscan = true;
};
base-dtb-params = { krnbt = "on"; };
dt-overlays = { vc4-kms-v3d = { }; };
};
};
@ -59,4 +60,12 @@
};
};
hardware.enableRedistributableFirmware = true;
services = {
udev.extraRules = ''
SUBSYSTEM=="dma_heap", GROUP="video", MODE="0660"
KERNEL=="gpiomem", GROUP="gpio", MODE="0660"
KERNEL=="gpiochip*", GROUP="gpio", MODE="0660"
'';
};
}

14
rpi/i2c.nix Normal file
View file

@ -0,0 +1,14 @@
{ config, lib, pkgs, ... }:
let cfg = config.hardware.raspberry-pi.i2c;
in {
options.hardware.raspberry-pi.i2c = {
enable = lib.mkEnableOption "configuration for i2c";
};
config = lib.mkIf cfg.enable {
hardware = {
raspberry-pi.config.all.base-dt-params = { i2c = "on"; };
i2c.enable = true;
};
};
}

View file

@ -16,7 +16,7 @@
populateFirmwareCommands = ''
cp ${pkgs.uboot_rpi_arm64}/u-boot.bin firmware/u-boot-rpi-arm64.bin
cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/{start*.elf,*.dtb,bootcode.bin,fixup*.dat,overlays} firmware
cp ${config.raspberrypi-config-output} firmware/config.txt
cp ${config.hardware.raspberry-pi.config-output} firmware/config.txt
'';
populateRootCommands = ''
mkdir -p ./files/boot

View file

@ -162,6 +162,7 @@ in {
"/boot/firmware" = {
device = "/dev/disk/by-label/${config.sdImage.firmwarePartitionName}";
fsType = "vfat";
options = [ "nofail" "noauto" ];
};
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";