about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/system/boot
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2024-03-01 11:40:12 +0100
committerAlyssa Ross <hi@alyssa.is>2024-03-01 11:40:12 +0100
commitbf6d657e5dbcb5e39fda280ef7e86b2a7794ca86 (patch)
tree8eb035cbab19794f6415cc460fac7226f7a58afc /nixpkgs/nixos/modules/system/boot
parent66f707d69f1e423db5a35c2fe43b32781125a9af (diff)
parent09c1497ce5d4ed4a0edfdd44450d3048074cb300 (diff)
downloadnixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.gz
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.bz2
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.lz
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.xz
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.zst
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.zip
Merge branch 'nixos-unstable-small' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/nixos/modules/system/boot')
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py69
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix65
-rw-r--r--nixpkgs/nixos/modules/system/boot/networkd.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/plymouth.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/coredump.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/repart.nix16
6 files changed, 123 insertions, 37 deletions
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index a9978d7adf80..258cf622a894 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -17,6 +17,9 @@ from dataclasses import dataclass
 
 # These values will be replaced with actual values during the package build
 EFI_SYS_MOUNT_POINT = "@efiSysMountPoint@"
+BOOT_MOUNT_POINT = "@bootMountPoint@"
+LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"  # Always stored on the ESP
+NIXOS_DIR = "@nixosDir@"
 TIMEOUT = "@timeout@"
 EDITOR = "@editor@" == "1"
 CONSOLE_MODE = "@consoleMode@"
@@ -28,6 +31,7 @@ CONFIGURATION_LIMIT = int("@configurationLimit@")
 CAN_TOUCH_EFI_VARIABLES = "@canTouchEfiVariables@"
 GRACEFUL = "@graceful@"
 COPY_EXTRA_FILES = "@copyExtraFiles@"
+CHECK_MOUNTPOINTS = "@checkMountpoints@"
 
 @dataclass
 class BootSpec:
@@ -87,7 +91,7 @@ def generation_conf_filename(profile: str | None, generation: int, specialisatio
 
 
 def write_loader_conf(profile: str | None, generation: int, specialisation: str | None) -> None:
-    with open(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", 'w') as f:
+    with open(f"{LOADER_CONF}.tmp", 'w') as f:
         if TIMEOUT != "":
             f.write(f"timeout {TIMEOUT}\n")
         f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation))
@@ -96,7 +100,7 @@ def write_loader_conf(profile: str | None, generation: int, specialisation: str
         f.write(f"console-mode {CONSOLE_MODE}\n")
         f.flush()
         os.fsync(f.fileno())
-    os.rename(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf")
+    os.rename(f"{LOADER_CONF}.tmp", LOADER_CONF)
 
 
 def get_bootspec(profile: str | None, generation: int) -> BootSpec:
@@ -126,9 +130,9 @@ def copy_from_file(file: str, dry_run: bool = False) -> str:
     store_file_path = os.path.realpath(file)
     suffix = os.path.basename(store_file_path)
     store_dir = os.path.basename(os.path.dirname(store_file_path))
-    efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
+    efi_file_path = f"{NIXOS_DIR}/{store_dir}-{suffix}.efi"
     if not dry_run:
-        copy_if_not_exists(store_file_path, f"{EFI_SYS_MOUNT_POINT}%s" % (efi_file_path))
+        copy_if_not_exists(store_file_path, f"{BOOT_MOUNT_POINT}{efi_file_path}")
     return efi_file_path
 
 def write_entry(profile: str | None, generation: int, specialisation: str | None,
@@ -145,7 +149,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
 
     try:
         if bootspec.initrdSecrets is not None:
-            subprocess.check_call([bootspec.initrdSecrets, f"{EFI_SYS_MOUNT_POINT}%s" % (initrd)])
+            subprocess.check_call([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)])
     except subprocess.CalledProcessError:
         if current:
             print("failed to create initrd secrets!", file=sys.stderr)
@@ -155,7 +159,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
                   f'for "{title} - Configuration {generation}", an older generation', file=sys.stderr)
             print("note: this is normal after having removed "
                   "or renamed a file in `boot.initrd.secrets`", file=sys.stderr)
-    entry_file = f"{EFI_SYS_MOUNT_POINT}/loader/entries/%s" % (
+    entry_file = f"{BOOT_MOUNT_POINT}/loader/entries/%s" % (
         generation_conf_filename(profile, generation, specialisation))
     tmp_path = "%s.tmp" % (entry_file)
     kernel_params = "init=%s " % bootspec.init
@@ -202,14 +206,14 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
 
 
 def remove_old_entries(gens: list[SystemIdentifier]) -> None:
-    rex_profile = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$")
-    rex_generation = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$")
+    rex_profile = re.compile(r"^" + re.escape(BOOT_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$")
+    rex_generation = re.compile(r"^" + re.escape(BOOT_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$")
     known_paths = []
     for gen in gens:
         bootspec = get_bootspec(gen.profile, gen.generation)
         known_paths.append(copy_from_file(bootspec.kernel, True))
         known_paths.append(copy_from_file(bootspec.initrd, True))
-    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"):
+    for path in glob.iglob(f"{BOOT_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"):
         if rex_profile.match(path):
             prof = rex_profile.sub(r"\1", path)
         else:
@@ -220,11 +224,18 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None:
             continue
         if not (prof, gen_number, None) in gens:
             os.unlink(path)
-    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/*"):
+    for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"):
         if not path in known_paths and not os.path.isdir(path):
             os.unlink(path)
 
 
+def cleanup_esp() -> None:
+    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*"):
+        os.unlink(path)
+    if os.path.isdir(f"{EFI_SYS_MOUNT_POINT}/{NIXOS_DIR}"):
+        shutil.rmtree(f"{EFI_SYS_MOUNT_POINT}/{NIXOS_DIR}")
+
+
 def get_profiles() -> list[str]:
     if os.path.isdir("/nix/var/nix/profiles/system-profiles/"):
         return [x
@@ -255,6 +266,9 @@ def install_bootloader(args: argparse.Namespace) -> None:
     # flags to pass to bootctl install/update
     bootctl_flags = []
 
+    if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT:
+        bootctl_flags.append(f"--boot-path={BOOT_MOUNT_POINT}")
+
     if CAN_TOUCH_EFI_VARIABLES != "1":
         bootctl_flags.append("--no-variables")
 
@@ -263,8 +277,8 @@ def install_bootloader(args: argparse.Namespace) -> None:
 
     if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
         # bootctl uses fopen() with modes "wxe" and fails if the file exists.
-        if os.path.exists(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"):
-            os.unlink(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf")
+        if os.path.exists(LOADER_CONF):
+            os.unlink(LOADER_CONF)
 
         subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"])
     else:
@@ -291,13 +305,15 @@ def install_bootloader(args: argparse.Namespace) -> None:
             print("updating systemd-boot from %s to %s" % (installed_version, available_version))
             subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["update"])
 
-    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos", exist_ok=True)
-    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/loader/entries", exist_ok=True)
+    os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}", exist_ok=True)
+    os.makedirs(f"{BOOT_MOUNT_POINT}/loader/entries", exist_ok=True)
 
     gens = get_generations()
     for profile in get_profiles():
         gens += get_generations(profile)
+
     remove_old_entries(gens)
+
     for gen in gens:
         try:
             bootspec = get_bootspec(gen.profile, gen.generation)
@@ -315,9 +331,15 @@ def install_bootloader(args: argparse.Namespace) -> None:
             else:
                 raise e
 
-    for root, _, files in os.walk(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", topdown=False):
-        relative_root = root.removeprefix(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files").removeprefix("/")
-        actual_root = os.path.join(f"{EFI_SYS_MOUNT_POINT}", relative_root)
+    if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT:
+        # Cleanup any entries in ESP if xbootldrMountPoint is set.
+        # If the user later unsets xbootldrMountPoint, entries in XBOOTLDR will not be cleaned up
+        # automatically, as we don't have information about the mount point anymore.
+        cleanup_esp()
+
+    for root, _, files in os.walk(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", topdown=False):
+        relative_root = root.removeprefix(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files").removeprefix("/")
+        actual_root = os.path.join(f"{BOOT_MOUNT_POINT}", relative_root)
 
         for file in files:
             actual_file = os.path.join(actual_root, file)
@@ -330,7 +352,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
             os.rmdir(actual_root)
         os.rmdir(root)
 
-    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", exist_ok=True)
+    os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True)
 
     subprocess.check_call(COPY_EXTRA_FILES)
 
@@ -340,6 +362,8 @@ def main() -> None:
     parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot")
     args = parser.parse_args()
 
+    subprocess.check_call(CHECK_MOUNTPOINTS)
+
     try:
         install_bootloader(args)
     finally:
@@ -347,9 +371,14 @@ def main() -> None:
         # it can leave the system in an unbootable state, when a crash/outage
         # happens shortly after an update. To decrease the likelihood of this
         # event sync the efi filesystem after each update.
-        rc = libc.syncfs(os.open(f"{EFI_SYS_MOUNT_POINT}", os.O_RDONLY))
+        rc = libc.syncfs(os.open(f"{BOOT_MOUNT_POINT}", os.O_RDONLY))
         if rc != 0:
-            print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
+            print(f"could not sync {BOOT_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
+
+        if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT:
+            rc = libc.syncfs(os.open(EFI_SYS_MOUNT_POINT, os.O_RDONLY))
+            if rc != 0:
+                print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
 
 
 if __name__ == '__main__':
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index ea4553b8208f..645b764760da 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -7,7 +7,7 @@ let
 
   efi = config.boot.loader.efi;
 
-  systemdBootBuilder = pkgs.substituteAll {
+  systemdBootBuilder = pkgs.substituteAll rec {
     src = ./systemd-boot-builder.py;
 
     isExecutable = true;
@@ -28,33 +28,48 @@ let
 
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
 
+    bootMountPoint = if cfg.xbootldrMountPoint != null
+      then cfg.xbootldrMountPoint
+      else efi.efiSysMountPoint;
+
+    nixosDir = "/EFI/nixos";
+
     inherit (config.system.nixos) distroName;
 
     memtest86 = optionalString cfg.memtest86.enable pkgs.memtest86plus;
 
     netbootxyz = optionalString cfg.netbootxyz.enable pkgs.netbootxyz-efi;
 
+    checkMountpoints = pkgs.writeShellScript "check-mountpoints" ''
+      fail() {
+        echo "$1 = '$2' is not a mounted partition. Is the path configured correctly?" >&2
+        exit 1
+      }
+      ${pkgs.util-linuxMinimal}/bin/findmnt ${efiSysMountPoint} > /dev/null || fail efiSysMountPoint ${efiSysMountPoint}
+      ${lib.optionalString
+        (cfg.xbootldrMountPoint != null)
+        "${pkgs.util-linuxMinimal}/bin/findmnt ${cfg.xbootldrMountPoint} > /dev/null || fail xbootldrMountPoint ${cfg.xbootldrMountPoint}"}
+    '';
+
     copyExtraFiles = pkgs.writeShellScript "copy-extra-files" ''
       empty_file=$(${pkgs.coreutils}/bin/mktemp)
 
       ${concatStrings (mapAttrsToList (n: v: ''
-        ${pkgs.coreutils}/bin/install -Dp "${v}" "${efi.efiSysMountPoint}/"${escapeShellArg n}
-        ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -Dp "${v}" "${bootMountPoint}/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/"${escapeShellArg n}
       '') cfg.extraFiles)}
 
       ${concatStrings (mapAttrsToList (n: v: ''
-        ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${efi.efiSysMountPoint}/loader/entries/"${escapeShellArg n}
-        ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/loader/entries/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${bootMountPoint}/loader/entries/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/loader/entries/"${escapeShellArg n}
       '') cfg.extraEntries)}
     '';
   };
 
-  checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" {
-    nativeBuildInputs = [ pkgs.mypy ];
-  } ''
+  checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { } ''
     mkdir -p $out/bin
     install -m755 ${systemdBootBuilder} $out/bin/systemd-boot-builder
-    mypy \
+    ${lib.getExe pkgs.buildPackages.mypy} \
       --no-implicit-optional \
       --disallow-untyped-calls \
       --disallow-untyped-defs \
@@ -101,6 +116,18 @@ in {
       '';
     };
 
+    xbootldrMountPoint = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = lib.mdDoc ''
+        Where the XBOOTLDR partition is mounted.
+
+        If set, this partition will be used as $BOOT to store boot loader entries and extra files
+        instead of the EFI partition. As per the bootloader specification, it is recommended that
+        the EFI and XBOOTLDR partitions be mounted at `/efi` and `/boot`, respectively.
+      '';
+    };
+
     configurationLimit = mkOption {
       default = null;
       example = 120;
@@ -110,7 +137,7 @@ in {
         Useful to prevent boot partition running out of disk space.
 
         `null` means no limit i.e. all generations
-        that were not garbage collected yet.
+        that have not been garbage collected yet.
       '';
     };
 
@@ -202,7 +229,7 @@ in {
       '';
       description = lib.mdDoc ''
         Any additional entries you want added to the `systemd-boot` menu.
-        These entries will be copied to {file}`/boot/loader/entries`.
+        These entries will be copied to {file}`$BOOT/loader/entries`.
         Each attribute name denotes the destination file name,
         and the corresponding attribute value is the contents of the entry.
 
@@ -219,9 +246,9 @@ in {
         { "efi/memtest86/memtest.efi" = "''${pkgs.memtest86plus}/memtest.efi"; }
       '';
       description = lib.mdDoc ''
-        A set of files to be copied to {file}`/boot`.
+        A set of files to be copied to {file}`$BOOT`.
         Each attribute name denotes the destination file name in
-        {file}`/boot`, while the corresponding
+        {file}`$BOOT`, while the corresponding
         attribute value specifies the source file.
       '';
     };
@@ -246,6 +273,18 @@ in {
   config = mkIf cfg.enable {
     assertions = [
       {
+        assertion = (hasPrefix "/" efi.efiSysMountPoint);
+        message = "The ESP mount point '${efi.efiSysMountPoint}' must be an absolute path";
+      }
+      {
+        assertion = cfg.xbootldrMountPoint == null || (hasPrefix "/" cfg.xbootldrMountPoint);
+        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' must be an absolute path";
+      }
+      {
+        assertion = cfg.xbootldrMountPoint != efi.efiSysMountPoint;
+        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${efi.efiSysMountPoint}'";
+      }
+      {
         assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
         message = "This kernel does not support the EFI boot stub";
       }
diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix
index a7399bd55e77..88d6a2ded873 100644
--- a/nixpkgs/nixos/modules/system/boot/networkd.nix
+++ b/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -647,9 +647,9 @@ let
           "BatmanAdvanced"
         ])
         # Note: For DHCP the values both, none, v4, v6 are deprecated
-        (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"])
+        (assertValueOneOf "DHCP" (boolValues ++ ["ipv4" "ipv6"]))
         (assertValueOneOf "DHCPServer" boolValues)
-        (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "fallback" "ipv4-fallback"])
+        (assertValueOneOf "LinkLocalAddressing" (boolValues ++ ["ipv4" "ipv6" "fallback" "ipv4-fallback"]))
         (assertValueOneOf "IPv6LinkLocalAddressGenerationMode" ["eui64" "none" "stable-privacy" "random"])
         (assertValueOneOf "IPv4LLRoute" boolValues)
         (assertValueOneOf "DefaultRouteOnDevice" boolValues)
diff --git a/nixpkgs/nixos/modules/system/boot/plymouth.nix b/nixpkgs/nixos/modules/system/boot/plymouth.nix
index b041b8951fa3..16bca40993ae 100644
--- a/nixpkgs/nixos/modules/system/boot/plymouth.nix
+++ b/nixpkgs/nixos/modules/system/boot/plymouth.nix
@@ -186,6 +186,8 @@ in
           # module might come from a theme
           cp ${themesEnv}/lib/plymouth/*.so $out
           cp ${plymouth}/lib/plymouth/renderers/*.so $out/renderers
+          # useless in the initrd, and adds several megabytes to the closure
+          rm $out/renderers/x11.so
         '';
         "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} ''
           # Check if the actual requested theme is here
@@ -271,6 +273,8 @@ in
       # module might come from a theme
       cp ${themesEnv}/lib/plymouth/*.so $out/lib/plymouth
       cp ${plymouth}/lib/plymouth/renderers/*.so $out/lib/plymouth/renderers
+      # useless in the initrd, and adds several megabytes to the closure
+      rm $out/lib/plymouth/renderers/x11.so
 
       mkdir -p $out/share/plymouth/themes
       cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
index 03ef00e5683c..271d8f86d0e6 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
@@ -52,7 +52,7 @@ in {
           # See: https://github.com/NixOS/nixpkgs/issues/213408
           pkgs.substitute {
             src = "${systemd}/example/sysctl.d/50-coredump.conf";
-            replacements = [
+            substitutions = [
               "--replace"
               "${systemd}"
               "${pkgs.symlinkJoin { name = "systemd"; paths = [ systemd ]; }}"
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
index 3be744acd0b3..6cc387cb6f43 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
@@ -10,6 +10,20 @@ let
     "repart.d"
     format
     (lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions);
+
+  partitionAssertions = lib.mapAttrsToList (fileName: definition:
+    let
+      maxLabelLength = 36; # GPT_LABEL_MAX defined in systemd's gpt.h
+      labelLength = builtins.stringLength definition.Label;
+    in
+    {
+      assertion = definition ? Label -> maxLabelLength >= labelLength;
+      message = ''
+        The partition label '${definition.Label}' defined for '${fileName}' is ${toString labelLength}
+        characters long, but the maximum label length supported by systemd is ${toString maxLabelLength}.
+      '';
+    }
+  ) cfg.partitions;
 in
 {
   options = {
@@ -81,7 +95,7 @@ in
           'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled.
         '';
       }
-    ];
+    ] ++ partitionAssertions;
 
     # systemd-repart uses loopback devices for partition creation
     boot.initrd.availableKernelModules = lib.optional initrdCfg.enable "loop";