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 # raspberry-pi-nix
NixOS modules that make building images for raspberry-pi products The primary goal of this software is to make it easy to create
easier. Most of the work in this repository is based on work in working NixOS configurations for Raspberry Pi products. Specifically,
[nixos-hardware](https://github.com/NixOS/nixos-hardware) and this repository aims to deliver the following benefits:
[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.
This flake provides nixos modules that correspond to different 1. Configure the kernel, device tree, and boot loader in a way that is
raspberry-pi products. These modules can be included in nixos compatible with the hardware and proprietary firmware.
configurations and aim to deliver the following benefits: 2. Provide a nix interface to Raspberry Pi/device tree configuration
that will be familiar to those who have used Raspberry Pi's
1. Configure the kernel, device tree, and u-boot in a way that is [config.txt based
compatible with the hardware. configuration](https://www.raspberrypi.com/documentation/computers/config_txt.html).
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.
3. Make it easy to build an image suitable for flashing to an sd-card, 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. without a need to first go through an installation media.
The important modules are `overlay/default.nix`, `rpi/default.nix`, The important modules are `overlay/default.nix`, `rpi/default.nix`,
and `rpi/device-tree.nix`. The other modules for i2c, i2s, etc are and `rpi/config.nix`. The other modules for i2c, i2s, etc are mostly
mostly wrappers that set common device tree settings for you. wrappers that set `config.txt` settings and enable required kernel
modules.
## Example ## Example
@ -46,27 +41,12 @@ mostly wrappers that set common device tree settings for you.
useDHCP = false; useDHCP = false;
interfaces = { wlan0.useDHCP = true; }; 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 { in {
nixosConfigurations = { nixosConfigurations = {
rpi-zero-2-w-example = nixosSystem { rpi-example = nixosSystem {
system = "aarch64-linux"; system = "aarch64-linux";
modules = [ raspberry-pi-nix.rpi-zero-2-w basic-config ]; modules = [ raspberry-pi-nix.rpi basic-config ];
};
rpi-4b-example = nixosSystem {
system = "aarch64-linux";
modules = [ raspberry-pi-nix.rpi-4b 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 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 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: 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 The image produced by this package is partitioned in the same way as
installation media from nixpkgs: There is a firmware partition that the aarch64 installation media from nixpkgs: There is a firmware
contains necessary firmware, u-boot, and config.txt. Then there is partition that contains necessary firmware, u-boot, and
another partition that contains everything else. After the sd-image is config.txt. Then there is another partition (labeled `NIXOS_SD`) that
built, nixos system updates will not change anything in the firmware contains everything else. The firmware and `config.txt` file are
partition ever again. New kernels and device tree configurations will managed by NixOS modules defined in this package. Additionally, NixOS
remain on the nixos partition and be booted by u-boot in the firmware 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. partition.
So, while you can control device tree params and overlays through your ## `config.txt` generation
nixos system configuration, if you want to modify other config.txt
variables this must be done manually by mounting the partition and As noted, the `config.txt` file is generated by the NixOS
modifying the config.txt file. 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, ... }: { lib, config, pkgs, ... }:
let let
cfg = config; cfg = config.hardware.raspberry-pi;
render-raspberrypi-config = let render-raspberrypi-config = let
render-options = opts: render-options = opts:
lib.strings.concatStringsSep "\n" (render-dt-kvs opts); lib.strings.concatStringsSep "\n" (render-dt-kvs opts);
@ -22,11 +22,11 @@ let
args = render-dt-kvs v; args = render-dt-kvs v;
}) overlays); }) overlays);
render-config-section = k: render-config-section = k:
{ options, base-dtb-params, dt-overlays }: { options, base-dt-params, dt-overlays }:
let let
all-config = lib.concatStringsSep "\n" (lib.filter (x: x != "") [ all-config = lib.concatStringsSep "\n" (lib.filter (x: x != "") [
(render-options options) (render-options options)
(render-base-dt-params base-dtb-params) (render-base-dt-params base-dt-params)
(render-dt-overlays dt-overlays) (render-dt-overlays dt-overlays)
]); ]);
in '' in ''
@ -38,48 +38,48 @@ let
(lib.attrsets.mapAttrsToList render-config-section conf); (lib.attrsets.mapAttrsToList render-config-section conf);
in { in {
options = { options = {
raspberrypi-config = let hardware.raspberry-pi = {
raspberrypi-config-options = { config = let
options = { raspberry-pi-config-options = {
options = lib.mkOption { options = {
type = with lib.types; attrsOf anything; options = lib.mkOption {
default = { }; type = with lib.types; attrsOf anything;
example = { default = { };
enable_gic = true; example = {
armstub = "armstub8-gic.bin"; enable_gic = true;
arm_boost = true; arm_boost = true;
};
}; };
}; base-dt-params = lib.mkOption {
base-dtb-params = lib.mkOption { type = with lib.types; attrsOf anything;
type = with lib.types; attrsOf anything; default = { };
default = { }; example = {
example = { i2c = "on";
i2c = "on"; audio = "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 { config-output = lib.mkOption {
type = with lib.types; attrsOf (submodule raspberrypi-config-options); type = lib.types.package;
}; default = pkgs.writeTextFile {
raspberrypi-config-output = lib.mkOption { name = "config.txt";
type = lib.types.package; text = ''
default = pkgs.writeTextFile { # This is a generated file. Do not edit!
name = "config.txt"; ${render-raspberrypi-config cfg.config}
text = '' '';
# Auto-generated by nix. Modifications will be overwritten. };
${render-raspberrypi-config cfg.raspberrypi-config}
'';
}; };
}; };
}; };
} }

View file

@ -2,8 +2,10 @@
{ lib, pkgs, config, ... }: { 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 = { system.activationScripts.raspberrypi = {
text = '' text = ''
if ! grep -qs '/boot/firmware ' /proc/mounts; then if ! grep -qs '/boot/firmware ' /proc/mounts; then
@ -11,29 +13,28 @@
fi fi
cp ${pkgs.uboot_rpi_arm64}/u-boot.bin /boot/firmware/u-boot-rpi-arm64.bin 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 -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 = { # Default config.txt on Raspberry Pi OS:
pi4 = { # https://github.com/RPi-Distro/pi-gen/blob/master/stage1/00-boot-files/files/config.txt
options = { hardware.raspberry-pi.config = {
enable_gic = true; cm4 = { options = { otg_mode = true; }; };
armstub = "armstub8-gic.bin"; pi4 = { options = { arm_boost = true; }; };
arm_boost = true;
disable_overscan = true;
};
dt-overlays = { vc4-kms-v3d-pi4 = { cma-512 = null; }; };
};
pi02 = { dt-overlays = { vc4-kms-v3d = { cma-256 = null; }; }; };
all = { all = {
options = { options = {
# The firmware will start our u-boot binary rather than a
# linux kernel.
kernel = "u-boot-rpi-arm64.bin"; kernel = "u-boot-rpi-arm64.bin";
arm_64bit = true;
enable_uart = true; enable_uart = true;
avoid_warnings = 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; 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 = '' populateFirmwareCommands = ''
cp ${pkgs.uboot_rpi_arm64}/u-boot.bin firmware/u-boot-rpi-arm64.bin 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 -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 = '' populateRootCommands = ''
mkdir -p ./files/boot mkdir -p ./files/boot

View file

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