mirror of
https://github.com/nix-community/raspberry-pi-nix.git
synced 2025-11-08 19:46:03 +01:00
Merge fb248c0047 into f317116725
This commit is contained in:
commit
c0cba6304f
17 changed files with 570 additions and 229 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
result
|
||||
66
README.md
66
README.md
|
|
@ -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.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
|
||||
```
|
||||
nix build '.#nixosConfigurations.rpi-example.config.system.build.sdImage'
|
||||
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;
|
||||
});
|
||||
```
|
||||
|
||||
## The firmware partition
|
||||
Add the configuration for your specific board:
|
||||
|
||||
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.
|
||||
```
|
||||
raspberry-pi-nix.board = "bcm2711";
|
||||
hardware.raspberry-pi.config = {
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Then get nix to build your image:
|
||||
|
||||
```
|
||||
nix build '.#nixosConfigurations.my-raspberry-pi.config.system.build.image'
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
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`
|
||||
|
|
|
|||
8
atomic-copy/atomic-copy-clobber.nix
Normal file
8
atomic-copy/atomic-copy-clobber.nix
Normal file
|
|
@ -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
atomic-copy/atomic-copy-clobber.sh
Normal file
18
atomic-copy/atomic-copy-clobber.sh
Normal file
|
|
@ -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
atomic-copy/atomic-copy-safe.nix
Normal file
8
atomic-copy/atomic-copy-safe.nix
Normal file
|
|
@ -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
atomic-copy/atomic-copy-safe.sh
Normal file
20
atomic-copy/atomic-copy-safe.sh
Normal file
|
|
@ -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
|
||||
fi
|
||||
81
example/common.nix
Normal file
81
example/common.nix
Normal file
|
|
@ -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 = {
|
||||
"/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="
|
||||
];
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
6
example/direct.nix
Normal file
6
example/direct.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{ config, inputs, lib, modulesPath, pkgs, ... }: {
|
||||
imports = [
|
||||
./common.nix
|
||||
];
|
||||
raspberry-pi-nix.uboot.enable = false;
|
||||
}
|
||||
6
example/uboot.nix
Normal file
6
example/uboot.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{ config, inputs, lib, modulesPath, pkgs, ... }: {
|
||||
imports = [
|
||||
./common.nix
|
||||
];
|
||||
raspberry-pi-nix.uboot.enable = true;
|
||||
}
|
||||
13
flake.nix
13
flake.nix
|
|
@ -63,12 +63,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;
|
||||
|
|
@ -85,7 +89,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;
|
||||
|
|
|
|||
5
generic-extlinux-compatible/README.md
Normal file
5
generic-extlinux-compatible/README.md
Normal file
|
|
@ -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
generic-extlinux-compatible/default.nix
Normal file
3
generic-extlinux-compatible/default.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{ ... }: {
|
||||
imports = [ ./generic-extlinux-compatible.nix ];
|
||||
}
|
||||
8
generic-extlinux-compatible/extlinux-conf-builder.nix
Normal file
8
generic-extlinux-compatible/extlinux-conf-builder.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{ pkgs }:
|
||||
|
||||
pkgs.substituteAll {
|
||||
src = ./extlinux-conf-builder.sh;
|
||||
isExecutable = true;
|
||||
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
|
||||
inherit (pkgs) bash;
|
||||
}
|
||||
163
generic-extlinux-compatible/extlinux-conf-builder.sh
Normal file
163
generic-extlinux-compatible/extlinux-conf-builder.sh
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#! @bash@/bin/sh -e
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
export PATH=/empty
|
||||
for i in @path@; do PATH=$PATH:$i/bin; done
|
||||
|
||||
usage() {
|
||||
echo "usage: $0 -t <timeout> -c <path-to-default-configuration> [-d <boot-dir>] [-g <num-generations>] [-n <dtbName>] [-r]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
timeout= # Timeout in centiseconds
|
||||
default= # Default configuration
|
||||
target=/boot # Target directory
|
||||
numGenerations=0 # Number of other generations to include in the menu
|
||||
|
||||
while getopts "t:c:d:g:n:r" opt; do
|
||||
case "$opt" in
|
||||
t) # U-Boot interprets '0' as infinite and negative as instant boot
|
||||
if [ "$OPTARG" -lt 0 ]; then
|
||||
timeout=0
|
||||
elif [ "$OPTARG" = 0 ]; then
|
||||
timeout=-10
|
||||
else
|
||||
timeout=$((OPTARG * 10))
|
||||
fi
|
||||
;;
|
||||
c) default="$OPTARG" ;;
|
||||
d) target="$OPTARG" ;;
|
||||
g) numGenerations="$OPTARG" ;;
|
||||
n) dtbName="$OPTARG" ;;
|
||||
r) noDeviceTree=1 ;;
|
||||
\?) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ "$timeout" = "" -o "$default" = "" ] && usage
|
||||
|
||||
mkdir -p $target/nixos
|
||||
mkdir -p $target/extlinux
|
||||
|
||||
# Convert a path to a file in the Nix store such as
|
||||
# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
|
||||
cleanName() {
|
||||
local path="$1"
|
||||
echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
|
||||
}
|
||||
|
||||
# Copy a file from the Nix store to $target/nixos.
|
||||
declare -A filesCopied
|
||||
|
||||
copyToKernelsDir() {
|
||||
local src=$(readlink -f "$1")
|
||||
local dst="$target/nixos/$(cleanName $src)"
|
||||
# Don't copy the file if $dst already exists. This means that we
|
||||
# have to create $dst atomically to prevent partially copied
|
||||
# kernels or initrd if this script is ever interrupted.
|
||||
if ! test -e $dst; then
|
||||
local dstTmp=$dst.tmp.$$
|
||||
cp -r $src $dstTmp
|
||||
mv $dstTmp $dst
|
||||
fi
|
||||
filesCopied[$dst]=1
|
||||
result=$dst
|
||||
}
|
||||
|
||||
# Copy its kernel, initrd and dtbs to $target/nixos, and echo out an
|
||||
# extlinux menu entry
|
||||
addEntry() {
|
||||
local path=$(readlink -f "$1")
|
||||
local tag="$2" # Generation number or 'default'
|
||||
|
||||
if ! test -e $path/kernel -a -e $path/initrd; then
|
||||
return
|
||||
fi
|
||||
|
||||
copyToKernelsDir "$path/kernel"; kernel=$result
|
||||
copyToKernelsDir "$path/initrd"; initrd=$result
|
||||
dtbDir=$(readlink -m "$path/dtbs")
|
||||
if [ -e "$dtbDir" ]; then
|
||||
copyToKernelsDir "$dtbDir"; dtbs=$result
|
||||
fi
|
||||
|
||||
timestampEpoch=$(stat -L -c '%Z' $path)
|
||||
|
||||
timestamp=$(date "+%Y-%m-%d %H:%M" -d @$timestampEpoch)
|
||||
nixosLabel="$(cat $path/nixos-version)"
|
||||
extraParams="$(cat $path/kernel-params)"
|
||||
|
||||
echo
|
||||
echo "LABEL nixos-$tag"
|
||||
if [ "$tag" = "default" ]; then
|
||||
echo " MENU LABEL NixOS - Default"
|
||||
else
|
||||
echo " MENU LABEL NixOS - Configuration $tag ($timestamp - $nixosLabel)"
|
||||
fi
|
||||
echo " LINUX ../nixos/$(basename $kernel)"
|
||||
echo " INITRD ../nixos/$(basename $initrd)"
|
||||
echo " APPEND init=$path/init $extraParams"
|
||||
|
||||
if [ -n "$noDeviceTree" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -d "$dtbDir" ]; then
|
||||
# if a dtbName was specified explicitly, use that, else use FDTDIR
|
||||
if [ -n "$dtbName" ]; then
|
||||
echo " FDT ../nixos/$(basename $dtbs)/${dtbName}"
|
||||
else
|
||||
echo " FDTDIR ../nixos/$(basename $dtbs)"
|
||||
fi
|
||||
else
|
||||
if [ -n "$dtbName" ]; then
|
||||
echo "Explicitly requested dtbName $dtbName, but there's no FDTDIR - bailing out." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
tmpFile="$target/extlinux/extlinux.conf.tmp.$$"
|
||||
|
||||
cat > $tmpFile <<EOF
|
||||
# Generated file, all changes will be lost on nixos-rebuild!
|
||||
|
||||
# Change this to e.g. nixos-42 to temporarily boot to an older configuration.
|
||||
DEFAULT nixos-default
|
||||
|
||||
MENU TITLE ------------------------------------------------------------
|
||||
TIMEOUT $timeout
|
||||
EOF
|
||||
|
||||
addEntry $default default >> $tmpFile
|
||||
|
||||
if [ "$numGenerations" -gt 0 ]; then
|
||||
# Add up to $numGenerations generations of the system profile to the menu,
|
||||
# in reverse (most recent to least recent) order.
|
||||
for generation in $(
|
||||
(cd /nix/var/nix/profiles && ls -d system-*-link) \
|
||||
| sed 's/system-\([0-9]\+\)-link/\1/' \
|
||||
| sort -n -r \
|
||||
| head -n $numGenerations); do
|
||||
link=/nix/var/nix/profiles/system-$generation-link
|
||||
addEntry $link "${generation}-default"
|
||||
for specialisation in $(
|
||||
ls /nix/var/nix/profiles/system-$generation-link/specialisation \
|
||||
| sort -n -r); do
|
||||
link=/nix/var/nix/profiles/system-$generation-link/specialisation/$specialisation
|
||||
addEntry $link "${generation}-${specialisation}"
|
||||
done
|
||||
done >> $tmpFile
|
||||
fi
|
||||
|
||||
mv -f $tmpFile $target/extlinux/extlinux.conf
|
||||
|
||||
# Remove obsolete files from $target/nixos.
|
||||
for fn in $target/nixos/*; do
|
||||
if ! test "${filesCopied[$fn]}" = 1; then
|
||||
echo "Removing no longer needed boot file: $fn"
|
||||
chmod +w -- "$fn"
|
||||
rm -rf -- "$fn"
|
||||
fi
|
||||
done
|
||||
132
generic-extlinux-compatible/generic-extlinux-compatible.nix
Normal file
132
generic-extlinux-compatible/generic-extlinux-compatible.nix
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
blCfg = config.boot.loader;
|
||||
dtCfg = config.hardware.deviceTree;
|
||||
cfg = blCfg.generic-extlinux-compatible-pi-loader;
|
||||
|
||||
timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout;
|
||||
|
||||
# The builder used to write during system activation
|
||||
builder = import ./extlinux-conf-builder.nix { inherit pkgs; };
|
||||
# The builder exposed in populateCmd, which runs on the build architecture
|
||||
populateBuilder = import ./extlinux-conf-builder.nix { pkgs = pkgs.buildPackages; };
|
||||
in
|
||||
{
|
||||
options = {
|
||||
boot.loader.generic-extlinux-compatible-pi-loader = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to generate an extlinux-compatible configuration file
|
||||
under `/boot/extlinux.conf`. For instance,
|
||||
U-Boot's generic distro boot support uses this file format.
|
||||
|
||||
See [U-boot's documentation](https://u-boot.readthedocs.io/en/latest/develop/distro.html)
|
||||
for more information.
|
||||
'';
|
||||
};
|
||||
|
||||
useGenerationDeviceTree = mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to generate Device Tree-related directives in the
|
||||
extlinux configuration.
|
||||
|
||||
When enabled, the bootloader will attempt to load the device
|
||||
tree binaries from the generation's kernel.
|
||||
|
||||
Note that this affects all generations, regardless of the
|
||||
setting value used in their configurations.
|
||||
'';
|
||||
};
|
||||
|
||||
configurationLimit = mkOption {
|
||||
default = 20;
|
||||
example = 10;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Maximum number of configurations in the boot menu.
|
||||
'';
|
||||
};
|
||||
|
||||
mirroredBoots = mkOption {
|
||||
default = [ { path = "/boot"; } ];
|
||||
example = [
|
||||
{ path = "/boot1"; }
|
||||
{ path = "/boot2"; }
|
||||
];
|
||||
description = ''
|
||||
Mirror the boot configuration to multiple paths.
|
||||
'';
|
||||
|
||||
type = with types; listOf (submodule {
|
||||
options = {
|
||||
path = mkOption {
|
||||
example = "/boot1";
|
||||
type = types.str;
|
||||
description = ''
|
||||
The path to the boot directory where the extlinux-compatible
|
||||
configuration files will be written.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
populateCmd = mkOption {
|
||||
type = types.str;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Contains the builder command used to populate an image,
|
||||
honoring all options except the `-c <path-to-default-configuration>`
|
||||
argument.
|
||||
Useful to have for sdImage.populateRootCommands
|
||||
'';
|
||||
};
|
||||
|
||||
extraCommandsAfter = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
Optional commands to run after installing the bootloader.
|
||||
Useful for putting Raspberry Pi firmwares on the boot partition.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
builderArgs = "-g ${toString cfg.configurationLimit} -t ${timeoutStr}"
|
||||
+ lib.optionalString (dtCfg.name != null) " -n ${dtCfg.name}"
|
||||
+ lib.optionalString (!cfg.useGenerationDeviceTree) " -r";
|
||||
installBootLoader = pkgs.writeScript "install-extlinux-conf.sh" (''
|
||||
#!${pkgs.runtimeShell}
|
||||
set -e
|
||||
'' + flip concatMapStrings cfg.mirroredBoots (args: ''
|
||||
${builder} ${builderArgs} -d '${args.path}' -c "$@"
|
||||
'') + ''
|
||||
${lib.concatLines cfg.extraCommandsAfter}
|
||||
'');
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
system.build.installBootLoader = installBootLoader;
|
||||
system.boot.loader.id = "generic-extlinux-compatible-pi-loader";
|
||||
|
||||
boot.loader.generic-extlinux-compatible-pi-loader.populateCmd = "${populateBuilder} ${builderArgs}";
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.mirroredBoots != [ ];
|
||||
message = ''
|
||||
You must not remove all elements from option 'boot.loader.generic-extlinux-compatible-pi-loader.mirroredBoots',
|
||||
otherwise the system will not be bootable.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
217
rpi/default.nix
217
rpi/default.nix
|
|
@ -1,15 +1,20 @@
|
|||
{ pinned, core-overlay, libcamera-overlay }:
|
||||
{ lib, pkgs, config, ... }:
|
||||
|
||||
with lib;
|
||||
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}";
|
||||
atomicCopySafe = import ../atomic-copy/atomic-copy-safe.nix { inherit pkgs; };
|
||||
atomicCopyClobber = import ../atomic-copy/atomic-copy-clobber.nix { inherit pkgs; };
|
||||
|
||||
# used for direct-to-kernel boot only: emulate cleanName()
|
||||
# https://github.com/NixOS/nixpkgs/blob/904ecf0b4e055dc465f5ae6574be2af8cc25dec3/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh#L47
|
||||
kernelStorePath = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
|
||||
kernelBootPath = "nixos/${builtins.replaceStrings [ "/nix/store/" "/" ] [ "" "-" ] kernelStorePath}";
|
||||
in
|
||||
{
|
||||
imports = [ ./config.nix ./i2c.nix ];
|
||||
imports = [ ../generic-extlinux-compatible ./config.nix ./i2c.nix ];
|
||||
|
||||
options = with lib; {
|
||||
raspberry-pi-nix = {
|
||||
|
|
@ -77,153 +82,16 @@ in
|
|||
|
||||
package = mkPackageOption pkgs "uboot-rpi-arm64" { };
|
||||
};
|
||||
rootGPUID = mkOption {
|
||||
description = "The UID of the root partition";
|
||||
type = types.str;
|
||||
# https://github.com/NixOS/nixpkgs/blob/23e89b7da85c3640bbc2173fe04f4bd114342367/nixos/lib/make-disk-image.nix#L177
|
||||
default = "F222513B-DED1-49FA-B591-20CE86A2FE7F";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
systemd.services = {
|
||||
"raspberry-pi-firmware-migrate" =
|
||||
{
|
||||
description = "update the firmware partition";
|
||||
wantedBy = if cfg.firmware-migration-service.enable then [ "multi-user.target" ] else [ ];
|
||||
serviceConfig =
|
||||
let
|
||||
firmware-path = "/boot/firmware";
|
||||
kernel-params = pkgs.writeTextFile {
|
||||
name = "cmdline.txt";
|
||||
text = ''
|
||||
${lib.strings.concatStringsSep " " config.boot.kernelParams}
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
Type = "oneshot";
|
||||
MountImages =
|
||||
"/dev/disk/by-label/${cfg.firmware-partition-label}:${firmware-path}";
|
||||
StateDirectory = "raspberrypi-firmware";
|
||||
ExecStart = pkgs.writeShellScript "migrate-rpi-firmware" ''
|
||||
shopt -s nullglob
|
||||
|
||||
TARGET_FIRMWARE_DIR="${firmware-path}"
|
||||
TARGET_OVERLAYS_DIR="$TARGET_FIRMWARE_DIR/overlays"
|
||||
TMPFILE="$TARGET_FIRMWARE_DIR/tmp"
|
||||
KERNEL="${kernel}/${config.system.boot.loader.kernelFile}"
|
||||
SHOULD_UBOOT=${if cfg.uboot.enable then "1" else "0"}
|
||||
SRC_FIRMWARE_DIR="${pkgs.raspberrypifw}/share/raspberrypi/boot"
|
||||
STARTFILES=("$SRC_FIRMWARE_DIR"/start*.elf)
|
||||
DTBS=("$SRC_FIRMWARE_DIR"/*.dtb)
|
||||
BOOTCODE="$SRC_FIRMWARE_DIR/bootcode.bin"
|
||||
FIXUPS=("$SRC_FIRMWARE_DIR"/fixup*.dat)
|
||||
SRC_OVERLAYS_DIR="$SRC_FIRMWARE_DIR/overlays"
|
||||
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"
|
||||
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
|
||||
}" > "$STATE_DIRECTORY/kernel-version"
|
||||
rm "$STATE_DIRECTORY/kernel-migration-in-progress"
|
||||
}
|
||||
|
||||
migrate_cmdline() {
|
||||
echo "migrating cmdline"
|
||||
touch "$STATE_DIRECTORY/cmdline-migration-in-progress"
|
||||
cp "${kernel-params}" "$TMPFILE"
|
||||
mv -T "$TMPFILE" "$TARGET_FIRMWARE_DIR/cmdline.txt"
|
||||
echo "${
|
||||
builtins.toString kernel-params
|
||||
}" > "$STATE_DIRECTORY/cmdline-version"
|
||||
rm "$STATE_DIRECTORY/cmdline-migration-in-progress"
|
||||
}
|
||||
|
||||
migrate_config() {
|
||||
echo "migrating config.txt"
|
||||
touch "$STATE_DIRECTORY/config-migration-in-progress"
|
||||
cp "$CONFIG" "$TMPFILE"
|
||||
mv -T "$TMPFILE" "$TARGET_FIRMWARE_DIR/config.txt"
|
||||
echo "${config.hardware.raspberry-pi.config-output}" > "$STATE_DIRECTORY/config-version"
|
||||
rm "$STATE_DIRECTORY/config-migration-in-progress"
|
||||
}
|
||||
|
||||
migrate_firmware() {
|
||||
echo "migrating raspberrypi firmware"
|
||||
touch "$STATE_DIRECTORY/firmware-migration-in-progress"
|
||||
for SRC in "''${STARTFILES[@]}" "''${DTBS[@]}" "$BOOTCODE" "''${FIXUPS[@]}"
|
||||
do
|
||||
cp "$SRC" "$TMPFILE"
|
||||
mv -T "$TMPFILE" "$TARGET_FIRMWARE_DIR/$(basename "$SRC")"
|
||||
done
|
||||
|
||||
if [[ ! -d "$TARGET_OVERLAYS_DIR" ]]; then
|
||||
mkdir "$TARGET_OVERLAYS_DIR"
|
||||
fi
|
||||
|
||||
for SRC in "''${SRC_OVERLAYS[@]}"
|
||||
do
|
||||
cp "$SRC" "$TMPFILE"
|
||||
mv -T "$TMPFILE" "$TARGET_OVERLAYS_DIR/$(basename "$SRC")"
|
||||
done
|
||||
echo "${
|
||||
builtins.toString pkgs.raspberrypifw
|
||||
}" > "$STATE_DIRECTORY/firmware-version"
|
||||
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
|
||||
} ]]; then
|
||||
migrate_uboot
|
||||
fi
|
||||
''}
|
||||
|
||||
if [[ "$SHOULD_UBOOT" -ne 1 ]] && [[ ! -f "$STATE_DIRECTORY/kernel-version" || $(< "$STATE_DIRECTORY/kernel-version") != ${
|
||||
builtins.toString kernel
|
||||
} ]]; then
|
||||
migrate_kernel
|
||||
fi
|
||||
|
||||
if [[ "$SHOULD_UBOOT" -ne 1 ]] && [[ ! -f "$STATE_DIRECTORY/cmdline-version" || $(< "$STATE_DIRECTORY/cmdline-version") != ${
|
||||
builtins.toString kernel-params
|
||||
} ]]; then
|
||||
migrate_cmdline
|
||||
fi
|
||||
|
||||
if [[ -f "$STATE_DIRECTORY/config-migration-in-progress" || ! -f "$STATE_DIRECTORY/config-version" || $(< "$STATE_DIRECTORY/config-version") != ${
|
||||
builtins.toString config.hardware.raspberry-pi.config-output
|
||||
} ]]; then
|
||||
migrate_config
|
||||
fi
|
||||
|
||||
if [[ -f "$STATE_DIRECTORY/firmware-migration-in-progress" || ! -f "$STATE_DIRECTORY/firmware-version" || $(< "$STATE_DIRECTORY/firmware-version") != ${
|
||||
builtins.toString pkgs.raspberrypifw
|
||||
} ]]; then
|
||||
migrate_firmware
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# 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 = {
|
||||
|
|
@ -245,11 +113,9 @@ in
|
|||
};
|
||||
all = {
|
||||
options = {
|
||||
# The firmware will start our u-boot binary rather than a
|
||||
# linux kernel.
|
||||
kernel = {
|
||||
enable = true;
|
||||
value = if cfg.uboot.enable then "u-boot-rpi-arm64.bin" else "kernel.img";
|
||||
value = if cfg.uboot.enable then "u-boot-rpi-arm64.bin" else kernelBootPath;
|
||||
};
|
||||
ramfsfile = {
|
||||
enable = !cfg.uboot.enable;
|
||||
|
|
@ -318,13 +184,15 @@ in
|
|||
};
|
||||
boot = {
|
||||
kernelParams =
|
||||
if cfg.uboot.enable then [ ]
|
||||
[ "console=serial0,115200n8" "console=tty1" ] ++
|
||||
(if cfg.uboot.enable then [ ]
|
||||
else [
|
||||
"console=tty1"
|
||||
# https://github.com/raspberrypi/firmware/issues/1539#issuecomment-784498108
|
||||
"console=serial0,115200n8"
|
||||
"init=/sbin/init"
|
||||
];
|
||||
"root=PARTUUID=${cfg.rootGPUID}"
|
||||
"rootfstype=ext4"
|
||||
"fsck.repair=yes"
|
||||
"rootwait"
|
||||
"init=/nix/var/nix/profiles/system/init"
|
||||
]);
|
||||
initrd = {
|
||||
availableKernelModules = [
|
||||
"usbhid"
|
||||
|
|
@ -337,12 +205,43 @@ in
|
|||
kernelPackages = pkgs.linuxPackagesFor pkgs.rpi-kernels."${version}"."${board}";
|
||||
loader = {
|
||||
grub.enable = lib.mkDefault false;
|
||||
initScript.enable = !cfg.uboot.enable;
|
||||
generic-extlinux-compatible = {
|
||||
enable = lib.mkDefault cfg.uboot.enable;
|
||||
|
||||
generic-extlinux-compatible.enable = false;
|
||||
generic-extlinux-compatible-pi-loader = {
|
||||
# extlinux-style boot is only used when uboot is enabled
|
||||
# when uboot is disabled, use this module to put files into
|
||||
# the boot partition as part of installBootloader
|
||||
enable = true;
|
||||
# We want to use the device tree provided by firmware, so don't
|
||||
# add FDTDIR to the extlinux conf file.
|
||||
useGenerationDeviceTree = false;
|
||||
extraCommandsAfter = let
|
||||
configTxt = config.hardware.raspberry-pi.config-output;
|
||||
kernelParams = pkgs.writeTextFile {
|
||||
name = "cmdline.txt";
|
||||
text = ''
|
||||
${lib.strings.concatStringsSep " " config.boot.kernelParams}
|
||||
'';
|
||||
};
|
||||
script = flip concatMapStrings config.boot.loader.generic-extlinux-compatible-pi-loader.mirroredBoots (args: ''
|
||||
# Add raspi files
|
||||
cd ${pkgs.raspberrypifw}/share/raspberrypi/boot
|
||||
${atomicCopySafe} bootcode.bin ${args.path}/bootcode.bin
|
||||
${atomicCopySafe} overlays ${args.path}/overlays
|
||||
${pkgs.findutils}/bin/find . -type f -name 'fixup*.dat' -exec ${atomicCopySafe} {} ${args.path}/{} \;
|
||||
${pkgs.findutils}/bin/find . -type f -name 'start*.elf' -exec ${atomicCopySafe} {} ${args.path}/{} \;
|
||||
${pkgs.findutils}/bin/find . -type f -name '*.dtb' -exec ${atomicCopySafe} {} ${args.path}/{} \;
|
||||
|
||||
# Add config.txt
|
||||
${atomicCopyClobber} ${configTxt} ${args.path}/config.txt
|
||||
'' + (if cfg.uboot.enable then ''
|
||||
# Add u-boot files
|
||||
${atomicCopySafe} ${cfg.uboot.package}/u-boot.bin ${args.path}/u-boot-rpi-arm64.bin
|
||||
'' else ''
|
||||
# Add kernel params
|
||||
${atomicCopyClobber} ${kernelParams} ${args.path}/cmdline.txt
|
||||
''));
|
||||
in [ (toString (pkgs.writeShellScript "cp-pi-loaders.sh" script)) ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue