Skip to content
This repository was archived by the owner on Mar 23, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
result
66 changes: 44 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ this repository aims to deliver the following benefits:
3. Make it easy to build an image suitable for flashing to an sd-card,
without the need to first go through an installation media.

The important modules are `overlay/default.nix`, `rpi/default.nix`,
The important modules are `overlays/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.

Expand All @@ -25,7 +25,11 @@ upgrading.

## Example

See the `rpi-example` config in this flake for an example config built by CI.
See the `/example` in this flake for an configs [built by CI](https://buildbot.nix-community.org/#/projects/15).

* `/example/direct.nix` boots directly into the linux kernel
* `/example/uboot.nix` boots into uboot, provides a generation selection menu,
then into linux

## Using the provided cache to avoid compiling linux
This repo uses the raspberry pi linux kernel fork, and compiling linux takes a
Expand All @@ -36,35 +40,53 @@ to use this cache.

## 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:
Use [`make-disk-image.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/make-disk-image.nix) like so:

```
system.build.image = (import "${toString modulesPath}/../lib/make-disk-image.nix" {
inherit lib config pkgs;
format = "raw";
partitionTableType = "efi";
copyChannel = false;
diskSize = "auto";
additionalSpace = "64M";
bootSize = "128M";
touchEFIVars = false;
installBootLoader = true;
label = "nixos";
deterministic = true;
});
```

Add the configuration for your specific board:

```
raspberry-pi-nix.board = "bcm2711";
hardware.raspberry-pi.config = {
...
};
```

Then get nix to build your image:

```
nix build '.#nixosConfigurations.rpi-example.config.system.build.sdImage'
nix build '.#nixosConfigurations.my-raspberry-pi.config.system.build.image'
```

## The firmware partition
## The partition layout

The image produced is partitioned in the same way as any x86 EFI-boot disk generated by `make-disk-image.nix`: There is a partition labelled `ESP` which contains the firmware, kernel, config.txt and u-boot or cmdline.txt. The second partition labeled `nixos` is the root partition which contains the nix store and everything else.

> [!NOTE]
> The boot partition is called ESP which stands for EFI System Partition. Raspberry Pis don't boot with EFI, but `ESP` is the hardcoded partition name used in `make-disk-image.nix` when using `partitionTableType=efi`, which just happens to be the closest partition layout to what's needed. By setting `touchEFIVars=false` you can avoid any EFI boot variables being set, not that they will affect the Raspberry Pi's boot process.

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.
Files in the boot partition are managed as long as the `rpi` package is imported, whether using uboot or direct-to-kernel boot. Following the upstream behaviour of `generic-extlinux-compatible/default.nix`, these files are not overwritten once created. The only exceptions are `cmdline.txt` and `config.txt` which will get overwritten every time `nixos-rebuild switch` is run. In practice, this means that you can enjoy kernel updates (because each version is put into a new file), but firmware / bootcode updates will probably not be installed if they arrive in an existing file.

## `config.txt` generation

As noted, the `config.txt` file is generated by the NixOS
configuration and automatically updated on when the nix configuration
is modified.
The `config.txt` file is generated by `rpi` package and automatically written to the boot partition when running `nixos-rebuild switch`.

The relevant nixos option is
`hardware.raspberry-pi.config`. Configuration is partitioned into
The relevant nixos option is `hardware.raspberry-pi.config`. Configuration is partitioned into
three sections:

1. Base device tree parameters `base-dt-params`
Expand Down
8 changes: 8 additions & 0 deletions atomic-copy/atomic-copy-clobber.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{ pkgs }:

pkgs.substituteAll {
src = ./atomic-copy-clobber.sh;
isExecutable = true;
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
inherit (pkgs) bash;
}
18 changes: 18 additions & 0 deletions atomic-copy/atomic-copy-clobber.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#! @bash@/bin/sh -e

# copy+paste of copyToKernelsDir https://github.com/NixOS/nixpkgs/blob/904ecf0b4e055dc465f5ae6574be2af8cc25dec3/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh#L53
# but without the check which skips the copy if the destination exists

shopt -s nullglob

export PATH=/empty
for i in @path@; do PATH=$PATH:$i/bin; done

src=$(readlink -f "$1")
dst="$2"

# Create $dst atomically to prevent partially copied files
# if this script is ever interrupted.
dstTmp=$dst.tmp.$$
cp -r $src $dstTmp
mv $dstTmp $dst
8 changes: 8 additions & 0 deletions atomic-copy/atomic-copy-safe.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{ pkgs }:

pkgs.substituteAll {
src = ./atomic-copy-safe.sh;
isExecutable = true;
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
inherit (pkgs) bash;
}
20 changes: 20 additions & 0 deletions atomic-copy/atomic-copy-safe.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#! @bash@/bin/sh -e

# copy+paste of copyToKernelsDir https://github.com/NixOS/nixpkgs/blob/904ecf0b4e055dc465f5ae6574be2af8cc25dec3/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh#L53

shopt -s nullglob

export PATH=/empty
for i in @path@; do PATH=$PATH:$i/bin; done

src=$(readlink -f "$1")
dst="$2"

# Don't copy the file if $dst already exists.
# Also create $dst atomically to prevent partially copied files
# if this script is ever interrupted.
if ! test -e $dst; then
dstTmp=$dst.tmp.$$
cp -r $src $dstTmp
mv $dstTmp $dst
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tstat you said:

Additionally, the systemd script that installs the kernel, initrd, and firmware today has some care taken to not modify any files that haven't changed, and to track if the installation process was halted at a bad time (like in the middle of firmware installation). It would be good to retain that.

This line moves the files into the right place after they've been copied to the boot filesystem. Notice line 16 though - the file won't be copied if it already exists at the destination. This is copied from the generic-extlinux-compatible shell script. A modified version atomic-copy-clobber.sh does the same but overwrites the file, which is used for config.txt and cmdline.txt. Is this in line with what you mean?

fi
81 changes: 81 additions & 0 deletions example/common.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{ config, inputs, lib, modulesPath, pkgs, ... }: {
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;
};
fileSystems = {
Copy link
Author

@rcambrj rcambrj Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tstat you said:

One concern I have is that, on master, everything that makes assumptions about the partition scheme is broken off into a separate module (the sd-image one), while everything else is in the rpi module. Then, by not importing the sd image module, you can use this repo with any file system or partition scheme.

I do want to maintain that separation, as the rpi module is useful on its own.

You'll notice that config.fileSystems is declared in the machine's configuration, so the file systems can be mounted as needed. I actually use this approach on my machines with custom mounts to achieve impermanence.

There's a parallel discussion in nix-community/disko#550 about using disko to customise the partition layout using make-disk-image, which faces some challenges. TLDR is: try using systemd-repart.

This doesn't mean that there aren't assumptions made - the /boot directory is critical to putting the files in the right spot, but at least the mount can be customised as needed and other filesystems/mounts added as needed too (in theory - haven't tried).

To talk a little more about this /boot assumption though... config.boot.loader.generic-extlinux-compatible-pi-loader.mirroredBoots and its original config.boot.loader.generic-extlinux-compatible.mirroredBoots allow the selection of the location for the boot files (and even multiple locations), but I've noticed that extlinux-conf-builder.sh makes an assumption about this location :( this bug would need fixing before changing the /boot path.

"/boot" = {
device = "/dev/disk/by-label/ESP";
fsType = "vfat";
};
"/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
autoResize = true;
};
};
boot.growPartition = true;
system.build.image = (import "${toString modulesPath}/../lib/make-disk-image.nix" {
inherit lib config pkgs;
format = "raw";
partitionTableType = "efi";
copyChannel = false;
diskSize = "auto";
additionalSpace = "64M";
bootSize = "128M";
touchEFIVars = false;
installBootLoader = true;
label = "nixos";
deterministic = true;
});

nix.settings.substituters = lib.mkForce config.nix.settings.trusted-substituters;
nix.settings.trusted-substituters = [
"https://cache.nixos.org/"
"https://nix-community.cachix.org"
];
nix.settings.trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
}
44 changes: 0 additions & 44 deletions example/default.nix

This file was deleted.

6 changes: 6 additions & 0 deletions example/direct.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{ config, inputs, lib, modulesPath, pkgs, ... }: {
imports = [
./common.nix
];
raspberry-pi-nix.uboot.enable = false;
}
6 changes: 6 additions & 0 deletions example/uboot.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{ config, inputs, lib, modulesPath, pkgs, ... }: {
imports = [
./common.nix
];
raspberry-pi-nix.uboot.enable = true;
}
13 changes: 9 additions & 4 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,16 @@
core-overlay = self.overlays.core;
libcamera-overlay = self.overlays.libcamera;
};
sd-image = import ./sd-image;
generic-extlinux-compatible = import ./generic-extlinux-compatible;
};
nixosConfigurations = {
rpi-example = srcs.nixpkgs.lib.nixosSystem {
rpi-example-direct = srcs.nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [ self.nixosModules.raspberry-pi self.nixosModules.sd-image ./example ];
modules = [ self.nixosModules.raspberry-pi ./example/direct.nix ];
};
rpi-example-uboot = srcs.nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [ self.nixosModules.raspberry-pi ./example/uboot.nix ];
};
};
checks.aarch64-linux = self.packages.aarch64-linux;
Expand All @@ -81,7 +85,8 @@
board-attr-set;
in
{
example-sd-image = self.nixosConfigurations.rpi-example.config.system.build.sdImage;
example-image-direct = self.nixosConfigurations.rpi-example-direct.config.system.build.image;
example-image-uboot = self.nixosConfigurations.rpi-example-uboot.config.system.build.image;
firmware = pinned.raspberrypifw;
libcamera = pinned.libcamera;
wireless-firmware = pinned.raspberrypiWirelessFirmware;
Expand Down
5 changes: 5 additions & 0 deletions generic-extlinux-compatible/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Copied from:

https://github.com/NixOS/nixpkgs/blob/93fb96ecdead26092b8425383fffb0422bf52182/nixos/modules/system/boot/loader/generic-extlinux-compatible/default.nix

The goal is to merge the changes back in once tested.
3 changes: 3 additions & 0 deletions generic-extlinux-compatible/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{ ... }: {
imports = [ ./generic-extlinux-compatible.nix ];
}
8 changes: 8 additions & 0 deletions generic-extlinux-compatible/extlinux-conf-builder.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{ pkgs }:

pkgs.substituteAll {
src = ./extlinux-conf-builder.sh;
isExecutable = true;
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
inherit (pkgs) bash;
}
Loading