Compare commits

..

No commits in common. "master" and "v0.3.0" have entirely different histories.

14 changed files with 387 additions and 539 deletions

View file

@ -1,23 +0,0 @@
name: update-flake-lock
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0'
permissions:
contents: write
pull-requests: write
jobs:
lockfile:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install nix
uses: DeterminateSystems/nix-installer-action@e50d5f73bfe71c2dd0aa4218de8f4afa59f8f81d # v16
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@a2bbe0274e3a0c4194390a1e445f734c597ebc37 # v24

173
README.md
View file

@ -17,29 +17,96 @@ The important modules are `overlay/default.nix`, `rpi/default.nix`,
and `rpi/config.nix`. The other modules are mostly wrappers that set
`config.txt` settings and enable required kernel modules.
## Stability note
`master` is the development branch -- if you want to avoid breaking changes, you
should pin your flake to a specific release and refer to the release notes when
upgrading.
## Example
See the `rpi-example` config in this flake for an example config built by CI.
See [the example
repo](https://github.com/tstat/raspberry-pi-nix-example) for a
complete example.
```nix
{
description = "raspberry-pi-nix example";
nixConfig = {
extra-substituters = [ "https://raspberry-pi-nix.cachix.org" ];
extra-trusted-public-keys = [
"raspberry-pi-nix.cachix.org-1:WmV2rdSangxW0rZjY/tBvBDSaNFQ3DyEQsVw8EvHn9o="
];
};
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11";
raspberry-pi-nix.url = "github:tstat/raspberry-pi-nix";
};
outputs = { self, nixpkgs, raspberry-pi-nix }:
let
inherit (nixpkgs.lib) nixosSystem;
basic-config = { pkgs, lib, ... }: {
time.timeZone = "America/New_York";
users.users.root.initialPassword = "root";
networking = {
hostName = "basic-example";
useDHCP = false;
interfaces = { wlan0.useDHCP = true; };
};
environment.systemPackages = with pkgs; [ bluez bluez-tools ];
hardware = {
bluetooth.enable = true;
raspberry-pi = {
config = {
all = {
base-dt-params = {
# enable autoprobing of bluetooth driver
# https://github.com/raspberrypi/linux/blob/c8c99191e1419062ac8b668956d19e788865912a/arch/arm/boot/dts/overlays/README#L222-L224
krnbt = {
enable = true;
value = "on";
};
};
};
};
};
};
};
in {
nixosConfigurations = {
rpi-example = nixosSystem {
system = "aarch64-linux";
modules = [ raspberry-pi-nix.nixosModules.raspberry-pi basic-config ];
};
};
};
}
```
## Using the provided cache to avoid compiling linux
This repo uses the raspberry pi linux kernel fork, and compiling linux takes a
while. CI pushes kernel builds to the nix-community cachix cache that you may
use to avoid compiling linux yourself. The cache can be found at
https://nix-community.cachix.org, and you can follow the instructions there
to use this cache.
This repo uses the raspberry pi linux kernel fork, and compiling linux
takes a while. I do push my kernel builds to a cachix cache that you
may use to avoid compiling linux yourself. The cache can be found
at https://raspberry-pi-nix.cachix.org, and you can follow the
instructions there to use this cache.
You don't need the cachix binary to use the cachix cache though, you
just need to add the relevant
[`substituters`](https://nixos.org/manual/nix/stable/command-ref/conf-file.html?highlight=nix.conf#conf-substituters)
and
[`trusted-public-keys`](https://nixos.org/manual/nix/stable/command-ref/conf-file.html?highlight=nix.conf#conf-trusted-public-keys)
settings settings to your `nix.conf`. You can do this directly by
modifying your `/etc/nix/nix.conf`, or in the flake definition. In the
above example flake these `nix.conf` settings are added by the
`nixConfig` attribute ([doc
link](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html?highlight=flake#flake-format)).
Note that this will only work if the user running `nix build` is in
[`trusted-users`](https://nixos.org/manual/nix/stable/command-ref/conf-file.html?highlight=nix.conf#conf-trusted-users)
or the substituter is in
[`trusted-substituters`](https://nixos.org/manual/nix/stable/command-ref/conf-file.html?highlight=nix.conf#conf-trusted-substituters).
## Building an sd-card image
Include the provided `sd-image` nixos module this flake provides, then 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-example` in the above configuration example you could run:
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-example` in the above configuration
example you could run:
```
nix build '.#nixosConfigurations.rpi-example.config.system.build.sdImage'
@ -47,15 +114,16 @@ nix build '.#nixosConfigurations.rpi-example.config.system.build.sdImage'
## The firmware partition
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, the kernel or 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, a systemd service will update the firmware and
`config.txt` in the firmware partition __in place__. If uboot is enabled then
linux kernels are stored in the `NIXOS_SD` partition and will be booted by
u-boot in the firmware partition.
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, a
systemd service 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.
## `config.txt` generation
@ -190,36 +258,37 @@ nix build '.#nixosConfigurations.rpi-example.config.hardware.raspberry-pi.config
## 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) ).
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 the proprietary firmware's manipulation must be known and
reproduced.
This manipulation makes it difficult to use the device tree configured
directly by NixOS as the proprietary firmware's manipulation must be
known and reproduced.
Even if the manipulation were successfully reproduced, some benefits would be
lost. For example, the firmware can detect connected hardware during boot and
automatically configure the device tree accordingly before passing it onto the
kernel. If this firmware device tree is ignored then a NixOS system rebuild with
a different device tree would be required when swapping connected
hardware. Examples of what I mean by hardware include: the specific Raspberry Pi
device booting the image, connected cameras, and connected displays.
Even if the manipulation were successfully reproduced, some benefits
would be lost. For example, the firmware can detect connected hardware
during boot and automatically configure the device tree accordingly
before passing it onto the kernel. If this firmware device tree is
ignored then a NixOS system rebuild with a different device tree would
be required when swapping connected 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, the bootloader 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
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 populate the firmware partition. 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.
Additionally, the firmware, device trees, and overlays from the
`raspberrypifw` package populate the firmware partition. 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.
## What's not working?
- [ ] Pi 5 u-boot devices other than sd-cards (i.e. usb, nvme).

View file

@ -1,44 +0,0 @@
{ pkgs, lib, ... }: {
time.timeZone = "America/New_York";
users.users.root.initialPassword = "root";
networking = {
hostName = "example";
useDHCP = false;
interfaces = {
wlan0.useDHCP = true;
eth0.useDHCP = true;
};
};
raspberry-pi-nix.board = "bcm2711";
hardware = {
raspberry-pi = {
config = {
all = {
base-dt-params = {
BOOT_UART = {
value = 1;
enable = true;
};
uart_2ndstage = {
value = 1;
enable = true;
};
};
dt-overlays = {
disable-bt = {
enable = true;
params = { };
};
};
};
};
};
};
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
}

134
flake.lock generated
View file

@ -1,186 +1,164 @@
{
"nodes": {
"libcamera-apps-src": {
"flake": false,
"locked": {
"lastModified": 1713431793,
"narHash": "sha256-uoewZMGf3vsBoRDfRz8KBKl+J6st/J44SHvNRMBdaUI=",
"owner": "raspberrypi",
"repo": "libcamera-apps",
"rev": "414a7383464b98f21f5e5381a16cc73ae0350ba6",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "v1.4.4",
"repo": "libcamera-apps",
"type": "github"
}
},
"libcamera-src": {
"flake": false,
"locked": {
"lastModified": 1725630279,
"narHash": "sha256-KH30jmHfxXq4j2CL7kv18DYECJRp9ECuWNPnqPZajPA=",
"lastModified": 1713446223,
"narHash": "sha256-p0/inkHPRUkxSIsTmj7VI7sIaX7OXdqjMGZ31W7cnt4=",
"owner": "raspberrypi",
"repo": "libcamera",
"rev": "69a894c4adad524d3063dd027f5c4774485cf9db",
"rev": "eb00c13d7c9f937732305d47af5b8ccf895e700f",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"repo": "libcamera",
"rev": "69a894c4adad524d3063dd027f5c4774485cf9db",
"rev": "eb00c13d7c9f937732305d47af5b8ccf895e700f",
"type": "github"
}
},
"libpisp-src": {
"flake": false,
"locked": {
"lastModified": 1724944683,
"narHash": "sha256-Fo2UJmQHS855YSSKKmGrsQnJzXog1cdpkIOO72yYAM4=",
"lastModified": 1713362873,
"narHash": "sha256-CHd44CH5dBcZuK+5fZtONZ8HE/lwGKwK5U0BYUK8gG4=",
"owner": "raspberrypi",
"repo": "libpisp",
"rev": "28196ed6edcfeda88d23cc5f213d51aa6fa17bb3",
"rev": "999da5acb4f40cb8e93d22ec16e28edd55ec9414",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "v1.0.7",
"ref": "v1.0.5",
"repo": "libpisp",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1736061677,
"narHash": "sha256-DjkQPnkAfd7eB522PwnkGhOMuT9QVCZspDpJJYyOj60=",
"lastModified": 1715218190,
"narHash": "sha256-R98WOBHkk8wIi103JUVQF3ei3oui4HvoZcz9tYOAwlk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cbd8ec4de4469333c82ff40d057350c30e9f7d36",
"rev": "9a9960b98418f8c385f52de3b09a63f9c561427a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"rev": "9a9960b98418f8c385f52de3b09a63f9c561427a",
"type": "github"
}
},
"root": {
"inputs": {
"libcamera-apps-src": "libcamera-apps-src",
"libcamera-src": "libcamera-src",
"libpisp-src": "libpisp-src",
"nixpkgs": "nixpkgs",
"rpi-bluez-firmware-src": "rpi-bluez-firmware-src",
"rpi-firmware-nonfree-src": "rpi-firmware-nonfree-src",
"rpi-firmware-src": "rpi-firmware-src",
"rpi-linux-6_12_17-src": "rpi-linux-6_12_17-src",
"rpi-linux-6_6_78-src": "rpi-linux-6_6_78-src",
"rpi-linux-stable-src": "rpi-linux-stable-src",
"rpicam-apps-src": "rpicam-apps-src"
"rpi-linux-6_6-src": "rpi-linux-6_6-src",
"u-boot-src": "u-boot-src"
}
},
"rpi-bluez-firmware-src": {
"flake": false,
"locked": {
"lastModified": 1708969706,
"narHash": "sha256-KakKnOBeWxh0exu44beZ7cbr5ni4RA9vkWYb9sGMb8Q=",
"lastModified": 1698157837,
"narHash": "sha256-CjbZ3t3TW/iJ3+t9QKEtM9NdQU7SwcUCDYuTmFEwvhU=",
"owner": "RPi-Distro",
"repo": "bluez-firmware",
"rev": "78d6a07730e2d20c035899521ab67726dc028e1c",
"rev": "d9d4741caba7314d6500f588b1eaa5ab387a4ff5",
"type": "github"
},
"original": {
"owner": "RPi-Distro",
"ref": "bookworm",
"repo": "bluez-firmware",
"rev": "d9d4741caba7314d6500f588b1eaa5ab387a4ff5",
"type": "github"
}
},
"rpi-firmware-nonfree-src": {
"flake": false,
"locked": {
"lastModified": 1723266537,
"narHash": "sha256-T7eTKXqY9cxEMdab8Snda4CEOrEihy5uOhA6Fy+Mhnw=",
"lastModified": 1700058854,
"narHash": "sha256-Yynww79LPPkau4YDSLI6IMOjH64nMpHUdGjnCfIR2+M=",
"owner": "RPi-Distro",
"repo": "firmware-nonfree",
"rev": "4b356e134e8333d073bd3802d767a825adec3807",
"rev": "88aa085bfa1a4650e1ccd88896f8343c22a24055",
"type": "github"
},
"original": {
"owner": "RPi-Distro",
"ref": "bookworm",
"repo": "firmware-nonfree",
"rev": "88aa085bfa1a4650e1ccd88896f8343c22a24055",
"type": "github"
}
},
"rpi-firmware-src": {
"flake": false,
"locked": {
"lastModified": 1728405098,
"narHash": "sha256-4gnK0KbqFnjBmWia9Jt2gveVWftmHrprpwBqYVqE/k0=",
"lastModified": 1713970515,
"narHash": "sha256-X5OinkLh/+mx34DM8mCk4tqOGuJdYxkvygv3gA77NJI=",
"owner": "raspberrypi",
"repo": "firmware",
"rev": "7bbb5f80d20a2335066a8781459c9f33e5eebc64",
"rev": "969420b4121b522ab33c5001074cc4c2547dafaf",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "1.20241008",
"ref": "1.20240424",
"repo": "firmware",
"type": "github"
}
},
"rpi-linux-6_12_17-src": {
"rpi-linux-6_6-src": {
"flake": false,
"locked": {
"lastModified": 1740765145,
"narHash": "sha256-hoCsGc4+RC/2LmxDtswLBL5ZhWlw4vSiL4Vkl39r2MU=",
"lastModified": 1713516936,
"narHash": "sha256-mlsDuVczu0e57BlD/iq7IEEluOIgqbZ+W4Ju30E/zhw=",
"owner": "raspberrypi",
"repo": "linux",
"rev": "5985ce32e511f4e8279a841a1b06a8c7d972b386",
"rev": "0c341f47adc3578cd5f817aa20ee2b7f9ae6b23e",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "rpi-6.12.y",
"ref": "stable_20240423",
"repo": "linux",
"type": "github"
}
},
"rpi-linux-6_6_78-src": {
"u-boot-src": {
"flake": false,
"locked": {
"lastModified": 1740503700,
"narHash": "sha256-Y8+ot4Yi3UKwlZK3ap15rZZ16VZDvmeFkD46+6Ku7bE=",
"owner": "raspberrypi",
"repo": "linux",
"rev": "2e071057fded90e789c0101498e45a1778be93fe",
"type": "github"
"lastModified": 1712055538,
"narHash": "sha256-IlaDdjKq/Pq2orzcU959h93WXRZfvKBGDO/MFw9mZMg=",
"type": "tarball",
"url": "https://ftp.denx.de/pub/u-boot/u-boot-2024.04.tar.bz2"
},
"original": {
"owner": "raspberrypi",
"ref": "rpi-6.6.y",
"repo": "linux",
"type": "github"
}
},
"rpi-linux-stable-src": {
"flake": false,
"locked": {
"lastModified": 1728403745,
"narHash": "sha256-phCxkuO+jUGZkfzSrBq6yErQeO2Td+inIGHxctXbD5U=",
"owner": "raspberrypi",
"repo": "linux",
"rev": "5aeecea9f4a45248bcf564dec924965e066a7bfd",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "stable_20241008",
"repo": "linux",
"type": "github"
}
},
"rpicam-apps-src": {
"flake": false,
"locked": {
"lastModified": 1727515047,
"narHash": "sha256-qCYGrcibOeGztxf+sd44lD6VAOGoUNwRqZDdAmcTa/U=",
"owner": "raspberrypi",
"repo": "rpicam-apps",
"rev": "a8ccf9f3cd9df49875dfb834a2b490d41d226031",
"type": "github"
},
"original": {
"owner": "raspberrypi",
"ref": "v1.5.2",
"repo": "rpicam-apps",
"type": "github"
"type": "tarball",
"url": "https://ftp.denx.de/pub/u-boot/u-boot-2024.04.tar.bz2"
}
}
},

View file

@ -2,42 +2,38 @@
description = "raspberry-pi nixos configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
rpi-linux-stable-src = {
nixpkgs.url = "github:NixOS/nixpkgs/9a9960b98418f8c385f52de3b09a63f9c561427a";
u-boot-src = {
flake = false;
url = "github:raspberrypi/linux/stable_20241008";
url = "https://ftp.denx.de/pub/u-boot/u-boot-2024.04.tar.bz2";
};
rpi-linux-6_6_78-src = {
rpi-linux-6_6-src = {
flake = false;
url = "github:raspberrypi/linux/rpi-6.6.y";
};
rpi-linux-6_12_17-src = {
flake = false;
url = "github:raspberrypi/linux/rpi-6.12.y";
url = "github:raspberrypi/linux/stable_20240423";
};
rpi-firmware-src = {
flake = false;
url = "github:raspberrypi/firmware/1.20241008";
url = "github:raspberrypi/firmware/1.20240424";
};
rpi-firmware-nonfree-src = {
flake = false;
url = "github:RPi-Distro/firmware-nonfree/bookworm";
url = "github:RPi-Distro/firmware-nonfree/88aa085bfa1a4650e1ccd88896f8343c22a24055";
};
rpi-bluez-firmware-src = {
flake = false;
url = "github:RPi-Distro/bluez-firmware/bookworm";
url = "github:RPi-Distro/bluez-firmware/d9d4741caba7314d6500f588b1eaa5ab387a4ff5";
};
rpicam-apps-src = {
libcamera-apps-src = {
flake = false;
url = "github:raspberrypi/rpicam-apps/v1.5.2";
url = "github:raspberrypi/libcamera-apps/v1.4.4";
};
libcamera-src = {
flake = false;
url = "github:raspberrypi/libcamera/69a894c4adad524d3063dd027f5c4774485cf9db"; # v0.3.1+rpt20240906
url = "github:raspberrypi/libcamera/eb00c13d7c9f937732305d47af5b8ccf895e700f"; # v0.2.0+rpt20240418
};
libpisp-src = {
flake = false;
url = "github:raspberrypi/libpisp/v1.0.7";
url = "github:raspberrypi/libpisp/v1.0.5";
};
};
@ -53,39 +49,13 @@
core = import ./overlays (builtins.removeAttrs srcs [ "self" ]);
libcamera = import ./overlays/libcamera.nix (builtins.removeAttrs srcs [ "self" ]);
};
nixosModules = {
raspberry-pi = import ./rpi {
nixosModules.raspberry-pi = import ./rpi {
inherit pinned;
core-overlay = self.overlays.core;
libcamera-overlay = self.overlays.libcamera;
};
sd-image = import ./sd-image;
packages.aarch64-linux = {
linux = pinned.rpi-kernels.latest.kernel;
};
nixosConfigurations = {
rpi-example = srcs.nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [ self.nixosModules.raspberry-pi self.nixosModules.sd-image ./example ];
};
};
checks.aarch64-linux = self.packages.aarch64-linux;
packages.aarch64-linux = with pinned.lib;
let
kernels =
foldlAttrs f { } pinned.rpi-kernels;
f = acc: kernel-version: board-attr-set:
foldlAttrs
(acc: board-version: drv: acc // {
"linux-${kernel-version}-${board-version}" = drv;
})
acc
board-attr-set;
in
{
example-sd-image = self.nixosConfigurations.rpi-example.config.system.build.sdImage;
firmware = pinned.raspberrypifw;
libcamera = pinned.libcamera;
wireless-firmware = pinned.raspberrypiWirelessFirmware;
uboot-rpi-arm64 = pinned.uboot-rpi-arm64;
} // kernels;
};
}

View file

@ -1,6 +1,5 @@
{ rpi-linux-stable-src
, rpi-linux-6_6_78-src
, rpi-linux-6_12_17-src
{ u-boot-src
, rpi-linux-6_6-src
, rpi-firmware-src
, rpi-firmware-nonfree-src
, rpi-bluez-firmware-src
@ -8,38 +7,77 @@
}:
final: prev:
let
versions = {
v6_6_51.src = rpi-linux-stable-src;
v6_6_78.src = rpi-linux-6_6_78-src;
v6_12_17 = {
src = rpi-linux-6_12_17-src;
patches = [
{
name = "remove-readme-target.patch";
patch = final.fetchpatch {
url = "https://github.com/raspberrypi/linux/commit/3c0fd51d184f1748b83d28e1113265425c19bcb5.patch";
hash = "sha256-v7uZOmPCUp2i7NGVgjqnQYe6dEBD+aATuP/oRs9jfuk=";
};
}
];
};
};
boards = [ "bcm2711" "bcm2712" ];
# The version to stick at `pkgs.rpi-kernels.latest'
latest = "v6_6_28";
# Helpers for building the `pkgs.rpi-kernels' map.
rpi-kernel = { version, board }:
rpi-kernel = { kernel, version, fw, wireless-fw, argsOverride ? null }:
let
kernel = builtins.getAttr version versions;
version-slug = builtins.replaceStrings [ "v" "_" ] [ "" "." ] version;
new-kernel = prev.linux_rpi4.override {
argsOverride = {
src = kernel;
inherit version;
modDirVersion = version;
} // (if builtins.isNull argsOverride then { } else argsOverride);
};
new-fw = prev.raspberrypifw.overrideAttrs (oldfw: { src = fw; });
new-wireless-fw = final.callPackage wireless-fw { };
version-slug = builtins.replaceStrings [ "." ] [ "_" ] version;
in
{
"${version}"."${board}" = (final.buildLinux {
modDirVersion = version-slug;
version = version-slug;
pname = "linux-rpi";
src = kernel.src;
defconfig = "${board}_defconfig";
structuredExtraConfig = with final.lib.kernel; {
"v${version-slug}" = {
kernel = new-kernel;
firmware = new-fw;
wireless-firmware = new-wireless-fw;
};
};
rpi-kernels = builtins.foldl' (b: a: b // rpi-kernel a) { };
in
{
# disable firmware compression so that brcm firmware can be found at
# the path expected by raspberry pi firmware/device tree
compressFirmwareXz = x: x;
# provide generic rpi arm64 u-boot
uboot_rpi_arm64 = prev.buildUBoot rec {
defconfig = "rpi_arm64_defconfig";
extraMeta.platforms = [ "aarch64-linux" ];
filesToInstall = [ "u-boot.bin" ];
version = "2024.04";
patches = [ ];
makeFlags = [ ];
src = u-boot-src;
# In raspberry pi sbcs the firmware manipulates the device tree in
# a variety of ways before handing it off to the linux kernel. [1]
# Since we have installed u-boot in place of a linux kernel we may
# pass the device tree passed by the firmware onto the kernel, or
# we may provide the kernel with a device tree of our own. This
# configuration uses the device tree provided by firmware so that
# we don't have to be aware of all manipulation done by the
# firmware and attempt to mimic it.
#
# 1. https://forums.raspberrypi.com/viewtopic.php?t=329799#p1974233
};
# default to latest firmware
raspberrypiWirelessFirmware = final.rpi-kernels.latest.wireless-firmware;
raspberrypifw = final.rpi-kernels.latest.firmware;
} // {
# rpi kernels and firmware are available at
# `pkgs.rpi-kernels.<VERSION>.{kernel,firmware,wireless-firmware}'.
#
# For example: `pkgs.rpi-kernels.v5_15_87.kernel'
rpi-kernels = rpi-kernels [{
version = "6.6.28";
kernel = rpi-linux-6_6-src;
fw = rpi-firmware-src;
wireless-fw = import ./raspberrypi-wireless-firmware.nix {
bluez-firmware = rpi-bluez-firmware-src;
firmware-nonfree = rpi-firmware-nonfree-src;
};
argsOverride = {
structuredExtraConfig = with prev.lib.kernel; {
# The perl script to generate kernel options sets unspecified
# parameters to `m` if possible [1]. This results in the
# unspecified config option KUNIT [2] getting set to `m` which
@ -58,65 +96,10 @@ let
# [2] https://github.com/raspberrypi/linux/blob/1.20230405/lib/kunit/Kconfig#L5-L14
# [3] https://github.com/raspberrypi/linux/blob/bb63dc31e48948bc2649357758c7a152210109c4/drivers/gpu/drm/vc4/Kconfig#L38-L52
KUNIT = no;
GPIO_PWM = no;
};
features.efiBootStub = false;
kernelPatches =
if kernel ? "patches" then kernel.patches else [ ];
ignoreConfigErrors = true;
}).overrideAttrs
(oldAttrs: {
postConfigure = ''
# The v7 defconfig has this set to '-v7' which screws up our modDirVersion.
sed -i $buildRoot/.config -e 's/^CONFIG_LOCALVERSION=.*/CONFIG_LOCALVERSION=""/'
sed -i $buildRoot/include/config/auto.conf -e 's/^CONFIG_LOCALVERSION=.*/CONFIG_LOCALVERSION=""/'
'';
});
};
rpi-kernels = builtins.foldl'
(b: a: final.lib.recursiveUpdate b (rpi-kernel a))
{ };
in
{
# disable firmware compression so that brcm firmware can be found at
# the path expected by raspberry pi firmware/device tree
compressFirmwareXz = x: x;
compressFirmwareZstd = x: x;
# provide generic rpi arm64 u-boot
uboot-rpi-arm64 = final.buildUBoot {
defconfig = "rpi_arm64_defconfig";
extraMeta.platforms = [ "aarch64-linux" ];
filesToInstall = [ "u-boot.bin" ];
# In raspberry pi sbcs the firmware manipulates the device tree in
# a variety of ways before handing it off to the linux kernel. [1]
# Since we have installed u-boot in place of a linux kernel we may
# pass the device tree passed by the firmware onto the kernel, or
# we may provide the kernel with a device tree of our own. This
# configuration uses the device tree provided by firmware so that
# we don't have to be aware of all manipulation done by the
# firmware and attempt to mimic it.
#
# 1. https://forums.raspberrypi.com/viewtopic.php?t=329799#p1974233
}] // {
latest = final.rpi-kernels."${latest}";
};
# default to latest firmware
raspberrypiWirelessFirmware = final.callPackage
(
import ./raspberrypi-wireless-firmware.nix {
bluez-firmware = rpi-bluez-firmware-src;
firmware-nonfree = rpi-firmware-nonfree-src;
}
)
{ };
raspberrypifw = prev.raspberrypifw.overrideAttrs (oldfw: { src = rpi-firmware-src; });
} // {
# rpi kernels and firmware are available at
# `pkgs.rpi-kernels.<VERSION>.<BOARD>'.
#
# For example: `pkgs.rpi-kernels.v6_6_78.bcm2712'
rpi-kernels = rpi-kernels (
final.lib.cartesianProduct
{ board = boards; version = (builtins.attrNames versions); }
);
}

View file

@ -0,0 +1,43 @@
{ libcamera-apps-src
, lib
, stdenv
, fetchFromGitHub
, fetchpatch
, meson
, pkg-config
, libjpeg
, libtiff
, libpng
, libcamera
, libepoxy
, boost
, libexif
, ninja
}:
stdenv.mkDerivation rec {
pname = "libcamera-apps";
version = "v1.4.1";
src = libcamera-apps-src;
nativeBuildInputs = [ meson pkg-config ];
buildInputs = [ libjpeg libtiff libcamera libepoxy boost libexif libpng ninja ];
mesonFlags = [
"-Denable_qt=false"
"-Denable_opencv=false"
"-Denable_tflite=false"
"-Denable_drm=true"
];
# Meson is no longer able to pick up Boost automatically.
# https://github.com/NixOS/nixpkgs/issues/86131
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
meta = with lib; {
description = "Userland tools interfacing with Raspberry Pi cameras";
homepage = "https://github.com/raspberrypi/libcamera-apps";
license = licenses.bsd2;
platforms = [ "aarch64-linux" ];
};
}

View file

@ -1,16 +1,17 @@
{ rpicam-apps-src
{ libcamera-apps-src
, libcamera-src
, libpisp-src
, ...
}:
final: prev: {
# A recent known working version of rpicam-apps
final: prev:
{
# A recent known working version of libcamera-apps
libcamera-apps =
final.callPackage ./rpicam-apps.nix { inherit rpicam-apps-src; };
final.callPackage ./libcamera-apps.nix { inherit libcamera-apps-src; };
libpisp = final.stdenv.mkDerivation {
name = "libpisp";
version = "1.0.7";
version = "1.0.3";
src = libpisp-src;
nativeBuildInputs = with final; [ pkg-config meson ninja ];
buildInputs = with final; [ nlohmann_json boost ];
@ -21,37 +22,9 @@ final: prev: {
};
libcamera = prev.libcamera.overrideAttrs (old: {
version = "0.3.1";
version = "0.1.0";
src = libcamera-src;
buildInputs = old.buildInputs ++ (with final; [
libpisp
openssl
libtiff
(python3.withPackages (ps: with ps; [
python3-gnutls
pybind11
pyyaml
ply
]))
libglibutil
gst_all_1.gst-plugins-base
]);
buildInputs = old.buildInputs ++ (with final; [ libpisp ]);
patches = [ ];
postPatch = ''
patchShebangs src/py/ utils/
'';
mesonFlags = [
"--buildtype=release"
"-Dpipelines=rpi/vc4,rpi/pisp"
"-Dipas=rpi/vc4,rpi/pisp"
"-Dv4l2=true"
"-Dgstreamer=enabled"
"-Dtest=false"
"-Dlc-compliance=disabled"
"-Dcam=disabled"
"-Dqcam=disabled"
"-Ddocumentation=enabled"
"-Dpycamera=enabled"
];
});
}

View file

@ -1,9 +1,9 @@
{ bluez-firmware, firmware-nonfree }:
{ lib, stdenvNoCC }:
{ lib, stdenvNoCC, fetchFromGitHub }:
stdenvNoCC.mkDerivation {
pname = "raspberrypi-wireless-firmware";
version = "2024-02-26";
version = "2023-11-15";
srcs = [ ];

View file

@ -1,30 +0,0 @@
{ rpicam-apps-src, lib, pkgs, stdenv }:
stdenv.mkDerivation {
pname = "libcamera-apps";
version = "v1.5.0";
src = rpicam-apps-src;
nativeBuildInputs = with pkgs; [ meson pkg-config ];
buildInputs = with pkgs; [ libjpeg libtiff libcamera libepoxy boost libexif libpng ffmpeg libdrm ninja ];
mesonFlags = [
"-Denable_qt=disabled"
"-Denable_opencv=disabled"
"-Denable_tflite=disabled"
"-Denable_egl=disabled"
"-Denable_hailo=disabled"
"-Denable_drm=enabled"
];
# Meson is no longer able to pick up Boost automatically.
# https://github.com/NixOS/nixpkgs/issues/86131
BOOST_INCLUDEDIR = "${lib.getDev pkgs.boost}/include";
BOOST_LIBRARYDIR = "${lib.getLib pkgs.boost}/lib";
meta = with lib; {
description = "Userland tools interfacing with Raspberry Pi cameras";
homepage = "https://github.com/raspberrypi/libcamera-apps";
license = licenses.bsd2;
platforms = [ "aarch64-linux" ];
};
}

View file

@ -93,7 +93,7 @@ in
};
};
base-dt-params = lib.mkOption {
type = with lib.types; attrsOf (submodule dt-param);
type = with lib.types; attrsOf (submodule rpi-config-param);
default = { };
example = {
i2c = {

View file

@ -1,37 +1,14 @@
{ pinned, core-overlay, libcamera-overlay }:
{ lib, pkgs, config, ... }:
let
cfg = config.raspberry-pi-nix;
version = cfg.kernel-version;
board = cfg.board;
kernel = config.system.build.kernel;
initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
let cfg = config.raspberry-pi-nix;
in
{
imports = [ ./config.nix ./i2c.nix ];
imports = [ ../sd-image ./config.nix ./i2c.nix ];
options = with lib; {
raspberry-pi-nix = {
kernel-version = mkOption {
default = "v6_6_51";
type = types.str;
description = "Kernel version to build.";
};
board = mkOption {
type = types.enum [ "bcm2711" "bcm2712" ];
description = ''
The kernel board version to build.
Examples at: https://www.raspberrypi.com/documentation/computers/linux_kernel.html#native-build-configuration
without the _defconfig part.
'';
};
firmware-partition-label = mkOption {
default = "FIRMWARE";
type = types.str;
description = "label of rpi firmware partition";
};
pin-inputs = {
pin-kernel = {
enable = mkOption {
default = true;
type = types.bool;
@ -61,7 +38,7 @@ in
};
uboot = {
enable = mkOption {
default = false;
default = true;
type = types.bool;
description = ''
If enabled then uboot is used as the bootloader. If disabled
@ -74,25 +51,24 @@ in
cm4 with an nvme drive.
'';
};
package = mkPackageOption pkgs "uboot-rpi-arm64" { };
};
serial-console = {
enable = mkOption {
default = true;
type = types.bool;
description = ''
Whether to enable a console on serial0.
Corresponds with raspi-config's setting
"Would you like a login shell to be accessible over serial?"
'';
};
};
};
};
config = {
boot.kernelParams =
if cfg.uboot.enable then [ ]
else [
# This is ugly and fragile, but the sdImage image has an msdos
# table, so the partition table id is a 1-indexed hex
# number. So, we drop the hex prefix and stick on a "02" to
# refer to the root partition.
"root=PARTUUID=${lib.strings.removePrefix "0x" config.sdImage.firmwarePartitionID}-02"
"rootfstype=ext4"
"fsck.repair=yes"
"rootwait"
"init=/sbin/init"
];
systemd.services = {
"raspberry-pi-firmware-migrate" =
{
@ -111,7 +87,7 @@ in
{
Type = "oneshot";
MountImages =
"/dev/disk/by-label/${cfg.firmware-partition-label}:${firmware-path}";
"/dev/disk/by-label/${config.sdImage.firmwarePartitionName}:${firmware-path}";
StateDirectory = "raspberrypi-firmware";
ExecStart = pkgs.writeShellScript "migrate-rpi-firmware" ''
shopt -s nullglob
@ -119,7 +95,8 @@ in
TARGET_FIRMWARE_DIR="${firmware-path}"
TARGET_OVERLAYS_DIR="$TARGET_FIRMWARE_DIR/overlays"
TMPFILE="$TARGET_FIRMWARE_DIR/tmp"
KERNEL="${kernel}/${config.system.boot.loader.kernelFile}"
UBOOT="${pkgs.uboot_rpi_arm64}/u-boot.bin"
KERNEL="${pkgs.rpi-kernels.latest.kernel}/Image"
SHOULD_UBOOT=${if cfg.uboot.enable then "1" else "0"}
SRC_FIRMWARE_DIR="${pkgs.raspberrypifw}/share/raspberrypi/boot"
STARTFILES=("$SRC_FIRMWARE_DIR"/start*.elf)
@ -130,28 +107,24 @@ in
SRC_OVERLAYS=("$SRC_OVERLAYS_DIR"/*)
CONFIG="${config.hardware.raspberry-pi.config-output}"
${lib.strings.optionalString cfg.uboot.enable ''
UBOOT="${cfg.uboot.package}/u-boot.bin"
migrate_uboot() {
echo "migrating uboot"
touch "$STATE_DIRECTORY/uboot-migration-in-progress"
cp "$UBOOT" "$TMPFILE"
mv -T "$TMPFILE" "$TARGET_FIRMWARE_DIR/u-boot-rpi-arm64.bin"
echo "${builtins.toString cfg.uboot.package}" > "$STATE_DIRECTORY/uboot-version"
echo "${
builtins.toString pkgs.uboot_rpi_arm64
}" > "$STATE_DIRECTORY/uboot-version"
rm "$STATE_DIRECTORY/uboot-migration-in-progress"
}
''}
migrate_kernel() {
echo "migrating kernel"
touch "$STATE_DIRECTORY/kernel-migration-in-progress"
cp "$KERNEL" "$TMPFILE"
mv -T "$TMPFILE" "$TARGET_FIRMWARE_DIR/kernel.img"
cp "${initrd}" "$TMPFILE"
mv -T "$TMPFILE" "$TARGET_FIRMWARE_DIR/initrd"
echo "${
builtins.toString kernel
builtins.toString pkgs.rpi-kernels.latest.kernel
}" > "$STATE_DIRECTORY/kernel-version"
rm "$STATE_DIRECTORY/kernel-migration-in-progress"
}
@ -200,16 +173,14 @@ in
rm "$STATE_DIRECTORY/firmware-migration-in-progress"
}
${lib.strings.optionalString cfg.uboot.enable ''
if [[ "$SHOULD_UBOOT" -eq 1 ]] && [[ -f "$STATE_DIRECTORY/uboot-migration-in-progress" || ! -f "$STATE_DIRECTORY/uboot-version" || $(< "$STATE_DIRECTORY/uboot-version") != ${
builtins.toString cfg.uboot.package
builtins.toString pkgs.uboot_rpi_arm64
} ]]; then
migrate_uboot
fi
''}
if [[ "$SHOULD_UBOOT" -ne 1 ]] && [[ ! -f "$STATE_DIRECTORY/kernel-version" || $(< "$STATE_DIRECTORY/kernel-version") != ${
builtins.toString kernel
builtins.toString pkgs.rpi-kernels.latest.kernel
} ]]; then
migrate_kernel
fi
@ -263,14 +234,6 @@ in
enable = true;
value = if cfg.uboot.enable then "u-boot-rpi-arm64.bin" else "kernel.img";
};
ramfsfile = {
enable = !cfg.uboot.enable;
value = "initrd";
};
ramfsaddr = {
enable = !cfg.uboot.enable;
value = -1;
};
arm_64bit = {
enable = true;
value = true;
@ -306,50 +269,26 @@ in
};
nixpkgs = {
overlays =
let
rpi-overlays = [ core-overlay ]
overlays = [ core-overlay ]
++ (if config.raspberry-pi-nix.libcamera-overlay.enable
then [ libcamera-overlay ] else [ ]);
rpi-overlay = lib.composeManyExtensions rpi-overlays;
pin-prev-overlay = overlay: pinned-prev: final: prev:
let
# apply the overlay to pinned-prev and fix that so no references to the actual final
# and prev appear in applied-overlay
applied-overlay =
lib.fix (final: pinned-prev // overlay final pinned-prev);
# We only want to set keys that appear in the overlay, so restrict applied-overlay to
# these keys
restricted-overlay = lib.getAttrs (builtins.attrNames (overlay { } { })) applied-overlay;
in
prev // restricted-overlay;
in
if cfg.pin-inputs.enable
then [ (pin-prev-overlay rpi-overlay pinned) ]
else [ rpi-overlay ];
};
boot = {
kernelParams =
if cfg.uboot.enable then [ ]
else builtins.concatLists [
[ "console=tty1" ]
(if cfg.serial-console.enable then [
# https://github.com/raspberrypi/firmware/issues/1539#issuecomment-784498108
"console=serial0,115200n8"
] else [ ]
)
[ "init=/sbin/init" ]
];
initrd = {
availableKernelModules = [
initrd.availableKernelModules = [
"usbhid"
"usb_storage"
"vc4"
"pcie_brcmstb" # required for the pcie bus to work
"reset-raspberrypi" # required for vl805 firmware to load
];
};
kernelPackages = pkgs.linuxPackagesFor pkgs.rpi-kernels."${version}"."${board}";
# This pin is not necessary, it would be fine to replace it with
# `pkgs.rpi-kernels.latest.kernel`. It is helpful to ensure
# cache hits for kernel builds though.
kernelPackages =
if cfg.pin-kernel.enable
then pinned.linuxPackagesFor (pinned.rpi-kernels.latest.kernel)
else pkgs.linuxPackagesFor (pkgs.rpi-kernels.latest.kernel);
loader = {
grub.enable = lib.mkDefault false;
initScript.enable = !cfg.uboot.enable;
@ -363,8 +302,6 @@ in
};
hardware.enableRedistributableFirmware = true;
users.groups = builtins.listToAttrs (map (k: { name = k; value = { }; })
[ "input" "sudo" "plugdev" "games" "netdev" "gpio" "i2c" "spi" ]);
services = {
udev.extraRules =
let shell = "${pkgs.bash}/bin/bash";

View file

@ -8,16 +8,8 @@
boot.consoleLogLevel = lib.mkDefault 7;
boot.kernelParams = [
# This is ugly and fragile, but the sdImage image has an msdos
# table, so the partition table id is a 1-indexed hex
# number. So, we drop the hex prefix and stick on a "02" to
# refer to the root partition.
"root=PARTUUID=${lib.strings.removePrefix "0x" config.sdImage.firmwarePartitionID}-02"
"rootfstype=ext4"
"fsck.repair=yes"
"rootwait"
];
# https://github.com/raspberrypi/firmware/issues/1539#issuecomment-784498108
boot.kernelParams = [ "console=serial0,115200n8" "console=tty1" ];
sdImage =
let
@ -27,19 +19,13 @@
${lib.strings.concatStringsSep " " config.boot.kernelParams}
'';
};
cfg = config.raspberry-pi-nix;
version = cfg.kernel-version;
board = cfg.board;
kernel = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
populate-kernel =
if cfg.uboot.enable
if config.raspberry-pi-nix.uboot.enable
then ''
cp ${cfg.uboot.package}/u-boot.bin firmware/u-boot-rpi-arm64.bin
cp ${pkgs.uboot_rpi_arm64}/u-boot.bin firmware/u-boot-rpi-arm64.bin
''
else ''
cp "${kernel}" firmware/kernel.img
cp "${initrd}" firmware/initrd
cp "${pkgs.rpi-kernels.latest.kernel}/Image" firmware/kernel.img
cp "${kernel-params}" firmware/cmdline.txt
'';
in
@ -50,7 +36,7 @@
cp ${config.hardware.raspberry-pi.config-output} firmware/config.txt
'';
populateRootCommands =
if cfg.uboot.enable
if config.raspberry-pi-nix.uboot.enable
then ''
mkdir -p ./files/boot
${config.boot.loader.generic-extlinux-compatible.populateCmd} -c ${config.system.build.toplevel} -d ./files/boot

View file

@ -30,8 +30,7 @@ let
} // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
uuid = config.sdImage.rootPartitionUUID;
});
in
{
in {
imports = [ ];
options.sdImage = {
@ -83,6 +82,14 @@ in
'';
};
firmwarePartitionName = mkOption {
type = types.str;
default = "FIRMWARE";
description = ''
Name of the filesystem which holds the boot firmware.
'';
};
rootPartitionUUID = mkOption {
type = types.nullOr types.str;
default = null;
@ -153,7 +160,7 @@ in
config = {
fileSystems = {
"/boot/firmware" = {
device = "/dev/disk/by-label/${config.raspberry-pi-nix.firmware-partition-label}";
device = "/dev/disk/by-label/${config.sdImage.firmwarePartitionName}";
fsType = "vfat";
};
"/" = {
@ -219,7 +226,7 @@ in
# Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
eval $(partx $img -o START,SECTORS --nr 1 --pairs)
truncate -s $((SECTORS * 512)) firmware_part.img
faketime "1970-01-01 00:00:00" mkfs.vfat -i ${config.sdImage.firmwarePartitionID} -n ${config.raspberry-pi-nix.firmware-partition-label} firmware_part.img
faketime "1970-01-01 00:00:00" mkfs.vfat -i ${config.sdImage.firmwarePartitionID} -n ${config.sdImage.firmwarePartitionName} firmware_part.img
# Populate the files intended for /boot/firmware
mkdir firmware
@ -237,8 +244,7 @@ in
zstd -T$NIX_BUILD_CORES --rm $img
fi
'';
})
{ };
}) { };
boot.postBootCommands = lib.mkIf config.sdImage.expandOnBoot ''
# On the first boot do some maintenance tasks
@ -248,7 +254,7 @@ in
# Figure out device names for the boot device and root filesystem.
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
bootDevice=$(lsblk -npo PKNAME $rootPart)
partNum=$(lsblk -npo PARTN $rootPart)
partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}')
# Resize the root partition and the filesystem to fit the disk
echo ",+," | sfdisk -N$partNum --no-reread $bootDevice