mirror of
https://github.com/nix-community/raspberry-pi-nix.git
synced 2025-11-09 12:06:02 +01:00
Add wip netboot image gen
This commit is contained in:
parent
aaec735faf
commit
65bf5b2d4f
4 changed files with 291 additions and 1 deletions
|
|
@ -60,11 +60,12 @@
|
||||||
libcamera-overlay = self.overlays.libcamera;
|
libcamera-overlay = self.overlays.libcamera;
|
||||||
};
|
};
|
||||||
sd-image = import ./sd-image;
|
sd-image = import ./sd-image;
|
||||||
|
net-image = import ./net-image;
|
||||||
};
|
};
|
||||||
nixosConfigurations = {
|
nixosConfigurations = {
|
||||||
rpi-example = srcs.nixpkgs.lib.nixosSystem {
|
rpi-example = srcs.nixpkgs.lib.nixosSystem {
|
||||||
system = "aarch64-linux";
|
system = "aarch64-linux";
|
||||||
modules = [ self.nixosModules.raspberry-pi self.nixosModules.sd-image ./example ];
|
modules = [ self.nixosModules.raspberry-pi self.nixosModules.sd-image self.nixosModules.net-image ./example ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
checks.aarch64-linux = self.packages.aarch64-linux;
|
checks.aarch64-linux = self.packages.aarch64-linux;
|
||||||
|
|
@ -82,6 +83,7 @@
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
example-sd-image = self.nixosConfigurations.rpi-example.config.system.build.sdImage;
|
example-sd-image = self.nixosConfigurations.rpi-example.config.system.build.sdImage;
|
||||||
|
example-net-image = self.nixosConfigurations.rpi-example.config.system.build.netImage;
|
||||||
firmware = pinned.raspberrypifw;
|
firmware = pinned.raspberrypifw;
|
||||||
libcamera = pinned.libcamera;
|
libcamera = pinned.libcamera;
|
||||||
wireless-firmware = pinned.raspberrypiWirelessFirmware;
|
wireless-firmware = pinned.raspberrypiWirelessFirmware;
|
||||||
|
|
|
||||||
71
net-image/default.nix
Normal file
71
net-image/default.nix
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [ ./net-image.nix ];
|
||||||
|
|
||||||
|
config = {
|
||||||
|
boot.loader.grub.enable = false;
|
||||||
|
|
||||||
|
boot.consoleLogLevel = lib.mkDefault 7;
|
||||||
|
|
||||||
|
boot.kernelParams = [
|
||||||
|
"rw"
|
||||||
|
"nfsroot=${config.netImage.nfsRoot}"
|
||||||
|
"ip=dhcp"
|
||||||
|
"root=/dev/nfs"
|
||||||
|
"rootwait"
|
||||||
|
"elevator=deadline"
|
||||||
|
"console=tty1"
|
||||||
|
"console=serial0,115200n8"
|
||||||
|
"init=/sbin/init"
|
||||||
|
"loglevel=7"
|
||||||
|
];
|
||||||
|
|
||||||
|
netImage =
|
||||||
|
let
|
||||||
|
kernel-params = pkgs.writeTextFile {
|
||||||
|
name = "cmdline.txt";
|
||||||
|
text = ''
|
||||||
|
${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
|
||||||
|
then ''
|
||||||
|
cp ${cfg.uboot.package}/u-boot.bin firmware/u-boot-rpi-arm64.bin
|
||||||
|
''
|
||||||
|
else ''
|
||||||
|
cp "${kernel}" firmware/kernel.img
|
||||||
|
cp "${initrd}" firmware/initrd
|
||||||
|
cp "${kernel-params}" firmware/cmdline.txt
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
populateFirmwareCommands = ''
|
||||||
|
${populate-kernel}
|
||||||
|
cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/{start*.elf,*.dtb,bootcode.bin,fixup*.dat,overlays} firmware
|
||||||
|
cp ${config.hardware.raspberry-pi.config-output} firmware/config.txt
|
||||||
|
'';
|
||||||
|
populateRootCommands =
|
||||||
|
if cfg.uboot.enable
|
||||||
|
then ''
|
||||||
|
mkdir -p ./files/boot
|
||||||
|
${config.boot.loader.generic-extlinux-compatible.populateCmd} -c ${config.system.build.toplevel} -d ./files/boot
|
||||||
|
''
|
||||||
|
else ''
|
||||||
|
mkdir -p ./files/sbin
|
||||||
|
content="$(
|
||||||
|
echo "#!${pkgs.bash}/bin/bash"
|
||||||
|
echo "exec ${config.system.build.toplevel}/init"
|
||||||
|
)"
|
||||||
|
echo "$content" > ./files/sbin/init
|
||||||
|
chmod 744 ./files/sbin/init
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
54
net-image/make-root-fs.nix
Normal file
54
net-image/make-root-fs.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Builds an ext4 image containing a populated /nix/store with the closure
|
||||||
|
# of store paths passed in the storePaths parameter, in addition to the
|
||||||
|
# contents of a directory that can be populated with commands. The
|
||||||
|
# generated image is sized to only fit its contents, with the expectation
|
||||||
|
# that a script resizes the filesystem at boot time.
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
# List of derivations to be included
|
||||||
|
storePaths,
|
||||||
|
# Shell commands to populate the ./files directory.
|
||||||
|
# All files in that directory are copied to the root of the FS.
|
||||||
|
populateImageCommands ? "",
|
||||||
|
perl
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
netbootClosureInfo = pkgs.buildPackages.closureInfo { rootPaths = storePaths; };
|
||||||
|
in
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "root-fs";
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
perl
|
||||||
|
];
|
||||||
|
|
||||||
|
buildCommand = ''
|
||||||
|
echo "Populating image with command: ${populateImageCommands}"
|
||||||
|
mkdir -p ./files
|
||||||
|
${populateImageCommands}
|
||||||
|
|
||||||
|
echo "Preparing store paths for image..."
|
||||||
|
# Create nix/store before copying path
|
||||||
|
mkdir -p ./rootImage/nix/store
|
||||||
|
|
||||||
|
xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${netbootClosureInfo}/store-paths
|
||||||
|
(
|
||||||
|
GLOBIGNORE=".:.."
|
||||||
|
shopt -u dotglob
|
||||||
|
|
||||||
|
for f in ./files/*; do
|
||||||
|
cp -a --reflink=auto -t ./rootImage/ "$f"
|
||||||
|
done
|
||||||
|
)
|
||||||
|
|
||||||
|
# Also include a manifest of the closures in a format suitable for nix-store --load-db
|
||||||
|
cp ${netbootClosureInfo}/registration ./rootImage/nix-path-registration
|
||||||
|
|
||||||
|
# done
|
||||||
|
echo "Image populated."
|
||||||
|
ls -aR ./rootImage
|
||||||
|
cp -r ./rootImage/ $out
|
||||||
|
'';
|
||||||
|
}
|
||||||
163
net-image/net-image.nix
Normal file
163
net-image/net-image.nix
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
# This module creates a bootable netboot image containing the given NixOS
|
||||||
|
# configuration. The generated image consists of a `/boot` partition
|
||||||
|
# (for TFTP boot) and a `/root` partition (for NFS root). The goal is to
|
||||||
|
# allow the system to boot over the network, using TFTP to retrieve the
|
||||||
|
# boot files and NFS to mount the root filesystem, enabling fully
|
||||||
|
# headless deployment of the NixOS system.
|
||||||
|
|
||||||
|
# The generated netboot image consists of two directories:
|
||||||
|
# - `/boot`: Contains the necessary bootloader, kernel, and initrd files
|
||||||
|
# required for booting the system. These files will be served via TFTP
|
||||||
|
# to the target machine.
|
||||||
|
# - `/root`: Contains the root filesystem that will be mounted by the
|
||||||
|
# target machine over NFS. This is typically an ext4 root partition
|
||||||
|
# populated with the necessary NixOS configuration.
|
||||||
|
|
||||||
|
# The image is generated in such a way that it can be used to netboot a
|
||||||
|
# Raspberry Pi (or any other compatible hardware) directly, as long as
|
||||||
|
# the appropriate network boot infrastructure (TFTP server for `/boot`
|
||||||
|
# and NFS server for `/root`) is configured.
|
||||||
|
|
||||||
|
# The image does not include a bootable SD card but instead prepares the
|
||||||
|
# filesystem and boot files for network-based booting. The NixOS
|
||||||
|
# configuration will be automatically applied when the system boots.
|
||||||
|
|
||||||
|
# The generated image will be placed in
|
||||||
|
# config.system.build.netImage. This image is intended to be deployed
|
||||||
|
# to a TFTP server (for the boot files) and an NFS server (for the root
|
||||||
|
# filesystem) for a fully headless, network-booted NixOS system.
|
||||||
|
|
||||||
|
# Note: This module assumes that you have already set up the TFTP and
|
||||||
|
# NFS servers on your network, and the target machine is configured
|
||||||
|
# for network booting.
|
||||||
|
|
||||||
|
{ modulesPath, config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
rootfsImage = pkgs.callPackage (builtins.path { path = ./make-root-fs.nix; }) ({
|
||||||
|
inherit (config.netImage) storePaths;
|
||||||
|
populateImageCommands = config.netImage.populateRootCommands;
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ];
|
||||||
|
|
||||||
|
options.netImage = {
|
||||||
|
rootDirectoryName = mkOption {
|
||||||
|
default =
|
||||||
|
"${config.netImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}";
|
||||||
|
description = ''
|
||||||
|
Name of the generated root directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
imageBaseName = mkOption {
|
||||||
|
default = "nixos-net-image";
|
||||||
|
description = ''
|
||||||
|
Prefix of the name of the generated image file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
storePaths = mkOption {
|
||||||
|
type = with types; listOf package;
|
||||||
|
example = literalExpression "[ pkgs.stdenv ]";
|
||||||
|
description = ''
|
||||||
|
Derivations to be included in the Nix store in the generated Netboot image.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
nfsRoot = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "192.168.0.108:/mnt/nfsshare/${config.netImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system},v3";
|
||||||
|
description = ''
|
||||||
|
cmdline.txt nfs parameter for the root filesystem.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
populateFirmwareCommands = mkOption {
|
||||||
|
example =
|
||||||
|
literalExpression "'' cp \${pkgs.myBootLoader}/u-boot.bin firmware/ ''";
|
||||||
|
description = ''
|
||||||
|
Shell commands to populate the ./firmware directory.
|
||||||
|
All files in that directory are copied to the
|
||||||
|
/boot/firmware partition on the Netboot image.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
populateRootCommands = mkOption {
|
||||||
|
example = literalExpression
|
||||||
|
"''\${config.boot.loader.generic-extlinux-compatible.populateCmd} -c \${config.system.build.toplevel} -d ./files/boot''";
|
||||||
|
description = ''
|
||||||
|
Shell commands to populate the ./files directory.
|
||||||
|
All files in that directory are copied to the
|
||||||
|
root (/) partition on the Netboot image. Use this to
|
||||||
|
populate the ./files/boot (/boot) directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
postBuildCommands = mkOption {
|
||||||
|
example = literalExpression
|
||||||
|
"'' dd if=\${pkgs.myBootLoader}/SPL of=$img bs=1024 seek=1 conv=notrunc ''";
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Shell commands to run after the image is built.
|
||||||
|
Can be used for boards requiring to dd u-boot SPL before actual partitions.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
netImage.storePaths = [ config.system.build.toplevel ];
|
||||||
|
|
||||||
|
system.build.netImage = pkgs.callPackage
|
||||||
|
({ stdenv, util-linux }:
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = config.netImage.rootDirectoryName;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ util-linux ];
|
||||||
|
|
||||||
|
buildCommand = ''
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
mkdir -p $out/nix-support $out/net-image
|
||||||
|
export rootfs=$out/net-image/${config.netImage.rootDirectoryName}
|
||||||
|
export bootfs=$out/net-image/boot
|
||||||
|
|
||||||
|
echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system
|
||||||
|
|
||||||
|
echo "Exporting rootfs image"
|
||||||
|
mkdir -p $rootfs
|
||||||
|
mv ${rootfsImage} $rootfs
|
||||||
|
|
||||||
|
# Populate the files intended for /boot/firmware
|
||||||
|
mkdir -p firmware
|
||||||
|
${config.netImage.populateFirmwareCommands}
|
||||||
|
mkdir -p $bootfs
|
||||||
|
mv firmware $bootfs
|
||||||
|
|
||||||
|
${config.netImage.postBuildCommands}
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
{ };
|
||||||
|
|
||||||
|
boot.postBootCommands = ''
|
||||||
|
# On the first boot do some maintenance tasks
|
||||||
|
if [ -f /nix-path-registration ]; then
|
||||||
|
set -euo pipefail
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Register the contents of the initial Nix store
|
||||||
|
${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration
|
||||||
|
|
||||||
|
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
|
||||||
|
touch /etc/NIXOS
|
||||||
|
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
|
||||||
|
|
||||||
|
# Prevents this from running on later boots.
|
||||||
|
rm -f /nix-path-registration
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue