mirror of
https://github.com/nix-community/raspberry-pi-nix.git
synced 2025-11-08 19:46:03 +01:00
Add 'NetImage' config option for Pi Netboot (#1)
* Add wip netboot image gen * rem firmware dir * additional logging * nixosConfigurations * config.fileSystems * rework rootfs output dir * nfs/boot/firmware * boot.initrd.network.enable = true; * nfsOptions * neededForBoot, supportedFilesystems
This commit is contained in:
parent
aaec735faf
commit
48321bb555
4 changed files with 386 additions and 0 deletions
|
|
@ -60,12 +60,17 @@
|
|||
libcamera-overlay = self.overlays.libcamera;
|
||||
};
|
||||
sd-image = import ./sd-image;
|
||||
net-image = import ./net-image;
|
||||
};
|
||||
nixosConfigurations = {
|
||||
rpi-example = srcs.nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
modules = [ self.nixosModules.raspberry-pi self.nixosModules.sd-image ./example ];
|
||||
};
|
||||
rpi-net-example = srcs.nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
modules = [ self.nixosModules.raspberry-pi self.nixosModules.net-image ./example ];
|
||||
};
|
||||
};
|
||||
checks.aarch64-linux = self.packages.aarch64-linux;
|
||||
packages.aarch64-linux = with pinned.lib;
|
||||
|
|
@ -82,6 +87,7 @@
|
|||
in
|
||||
{
|
||||
example-sd-image = self.nixosConfigurations.rpi-example.config.system.build.sdImage;
|
||||
example-net-image = self.nixosConfigurations.rpi-net-example.config.system.build.netImage;
|
||||
firmware = pinned.raspberrypifw;
|
||||
libcamera = pinned.libcamera;
|
||||
wireless-firmware = pinned.raspberrypiWirelessFirmware;
|
||||
|
|
|
|||
83
net-image/default.nix
Normal file
83
net-image/default.nix
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [ ./net-image.nix ];
|
||||
|
||||
config = {
|
||||
boot.loader.grub.enable = false;
|
||||
|
||||
boot.consoleLogLevel = lib.mkDefault 8;
|
||||
|
||||
boot.kernelParams = [
|
||||
# Read-only root filesystem
|
||||
"ro"
|
||||
# NFS root filesystem location
|
||||
"nfsroot=${config.netImage.nfsRoot},v3"
|
||||
# Root filesystem device
|
||||
"root=/dev/nfs"
|
||||
# Wait for root filesystem
|
||||
"rootwait"
|
||||
# I/O scheduler
|
||||
"elevator=deadline"
|
||||
# Enable systemd debug shell
|
||||
"systemd.debug_shell=1"
|
||||
# Set systemd log level to debug
|
||||
"systemd.log_level=info"
|
||||
# Disable splash screen
|
||||
"disable_splash"
|
||||
# Early printk to serial console
|
||||
"earlyprintk=serial,ttyS0,115200"
|
||||
# Enable initcall debugging
|
||||
"initcall_debug"
|
||||
# Print timestamps in printk messages
|
||||
"printk.time=1"
|
||||
];
|
||||
|
||||
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 ./u-boot-rpi-arm64.bin
|
||||
''
|
||||
else ''
|
||||
cp "${kernel}" ./kernel.img
|
||||
cp "${initrd}" ./initrd
|
||||
cp "${kernel-params}" ./cmdline.txt
|
||||
'';
|
||||
in
|
||||
{
|
||||
populateFirmwareCommands = ''
|
||||
${populate-kernel}
|
||||
cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/{start*.elf,*.dtb,bootcode.bin,fixup*.dat,overlays} ./
|
||||
cp ${config.hardware.raspberry-pi.config-output} ./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
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
52
net-image/make-root-fs.nix
Normal file
52
net-image/make-root-fs.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Builds a directory 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.
|
||||
{
|
||||
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.
|
||||
populateRootCommands ? "",
|
||||
perl
|
||||
}:
|
||||
|
||||
let
|
||||
netbootClosureInfo = pkgs.buildPackages.closureInfo { rootPaths = storePaths; };
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "root-fs";
|
||||
|
||||
nativeBuildInputs = [
|
||||
perl
|
||||
];
|
||||
|
||||
buildCommand = ''
|
||||
echo "Populating image with command: ${populateRootCommands}"
|
||||
mkdir -p ./files
|
||||
${populateRootCommands}
|
||||
|
||||
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
|
||||
'';
|
||||
}
|
||||
245
net-image/net-image.nix
Normal file
245
net-image/net-image.nix
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
# This module creates the files necessary containing the given NixOS
|
||||
# configuration. The generated directories consists of a `boot` directory
|
||||
# (for TFTP boot) and a `root` directory (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 files 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 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.
|
||||
|
||||
# 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;
|
||||
populateRootCommands = config.netImage.populateRootCommands;
|
||||
});
|
||||
in
|
||||
{
|
||||
imports = [ ];
|
||||
|
||||
options.netImage = {
|
||||
rootDirectoryName = mkOption {
|
||||
default =
|
||||
"${config.netImage.directoryBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}";
|
||||
description = ''
|
||||
Name of the generated root directory.
|
||||
'';
|
||||
};
|
||||
|
||||
directoryBaseName = mkOption {
|
||||
default = "nixos-net-image";
|
||||
description = ''
|
||||
Prefix of the name of the generated root directory.
|
||||
'';
|
||||
};
|
||||
|
||||
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.directoryBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}";
|
||||
description = ''
|
||||
cmdline.txt nfs parameter for the root filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
nfsOptions = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [
|
||||
# Disable file locking
|
||||
"nolock"
|
||||
# Mount the filesystem read-write
|
||||
"rw"
|
||||
# Use NFS version 3
|
||||
"vers=3"
|
||||
# Set the read buffer size to 131072 bytes
|
||||
"rsize=131072"
|
||||
# Set the write buffer size to 131072 bytes
|
||||
"wsize=131072"
|
||||
# Set the maximum filename length to 255 characters
|
||||
"namlen=255"
|
||||
# Use hard mounts (retry indefinitely on failure)
|
||||
"hard"
|
||||
# Disable Access Control Lists
|
||||
"noacl"
|
||||
# Use TCP as the transport protocol
|
||||
"proto=tcp"
|
||||
# Set the NFS timeout to 11 tenths of a second
|
||||
"timeo=11"
|
||||
# Set the number of NFS retransmissions to 3
|
||||
"retrans=3"
|
||||
# Use the 'sys' security flavor
|
||||
"sec=sys"
|
||||
# Use NFS mount protocol version 3
|
||||
"mountvers=3"
|
||||
# Use TCP for the mount protocol
|
||||
"mountproto=tcp"
|
||||
# Enable local locking
|
||||
"local_lock=all"
|
||||
# Do not update inode access times on reads
|
||||
"noatime"
|
||||
# Do not update directory inode access times on reads
|
||||
"nodiratime"
|
||||
];
|
||||
description = ''
|
||||
NFS options to use when mounting the root filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
populateFirmwareCommands = mkOption {
|
||||
example =
|
||||
literalExpression "'' cp \${pkgs.myBootLoader}/u-boot.bin ./ ''";
|
||||
description = ''
|
||||
Shell commands to populate the ./ directory.
|
||||
All files in that directory are copied to the
|
||||
tftp files 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.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# net
|
||||
networking.useDHCP = lib.mkForce true;
|
||||
networking.interfaces.eth0.useDHCP = lib.mkForce true;
|
||||
networking.interfaces.wlan0.useDHCP = lib.mkForce false;
|
||||
|
||||
# boot
|
||||
boot.initrd.network.enable = lib.mkForce true;
|
||||
boot.initrd.network.flushBeforeStage2 = lib.mkForce false;
|
||||
boot.initrd.supportedFilesystems = [
|
||||
# Network File System (NFS) support for mounting root over the network
|
||||
"nfs"
|
||||
# Overlay filesystem for layering file systems
|
||||
"overlay"
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [
|
||||
# Network File System (NFS) module
|
||||
"nfs"
|
||||
# Overlay filesystem module
|
||||
"overlay"
|
||||
# Broadcom PHY library for Ethernet device support
|
||||
"bcm_phy_lib"
|
||||
# Broadcom-specific driver module
|
||||
"broadcom"
|
||||
# Broadcom GENET Ethernet controller driver
|
||||
"genet"
|
||||
];
|
||||
|
||||
boot.initrd.kernelModules = [
|
||||
# Network File System (NFS) module
|
||||
"nfs"
|
||||
# Overlay filesystem module
|
||||
"overlay"
|
||||
# Broadcom PHY library for Ethernet device support
|
||||
"bcm_phy_lib"
|
||||
# Broadcom-specific driver module
|
||||
"broadcom"
|
||||
# Broadcom GENET Ethernet controller driver
|
||||
"genet"
|
||||
];
|
||||
|
||||
|
||||
# fileSystems
|
||||
fileSystems = {
|
||||
"/boot/firmware" = {
|
||||
device = "${config.netImage.nfsRoot}/boot/firmware";
|
||||
fsType = "nfs";
|
||||
options = config.netImage.nfsOptions;
|
||||
neededForBoot = lib.mkForce true;
|
||||
};
|
||||
"/" = {
|
||||
device = "${config.netImage.nfsRoot}";
|
||||
fsType = "nfs";
|
||||
options = config.netImage.nfsOptions;
|
||||
neededForBoot = lib.mkForce true;
|
||||
};
|
||||
};
|
||||
|
||||
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/os/${config.netImage.rootDirectoryName}
|
||||
export bootfs=$out/net-image/boot
|
||||
|
||||
echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system
|
||||
|
||||
# Populate the files intended for NFS
|
||||
echo "Exporting rootfs image"
|
||||
mkdir -p $rootfs
|
||||
cp -r ${rootfsImage}/* $rootfs
|
||||
|
||||
# Populate the files intended for TFTP
|
||||
echo "Exporting rootfs image"
|
||||
${config.netImage.populateFirmwareCommands}
|
||||
mkdir -p $bootfs
|
||||
cp -r . $bootfs
|
||||
mkdir -p $rootfs/boot/firmware
|
||||
cp -r . $rootfs/boot/firmware
|
||||
'';
|
||||
})
|
||||
{ };
|
||||
|
||||
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