diff options
Diffstat (limited to 'nixos/modules/system')
-rw-r--r-- | nixos/modules/system/boot/kernel.nix | 7 | ||||
-rw-r--r-- | nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py | 69 | ||||
-rw-r--r-- | nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix | 65 | ||||
-rw-r--r-- | nixos/modules/system/boot/networkd.nix | 4 | ||||
-rw-r--r-- | nixos/modules/system/boot/plymouth.nix | 4 | ||||
-rw-r--r-- | nixos/modules/system/boot/stage-1.nix | 13 | ||||
-rw-r--r-- | nixos/modules/system/boot/systemd/coredump.nix | 2 | ||||
-rw-r--r-- | nixos/modules/system/boot/systemd/initrd.nix | 2 | ||||
-rw-r--r-- | nixos/modules/system/boot/systemd/oomd.nix | 6 | ||||
-rw-r--r-- | nixos/modules/system/boot/systemd/repart.nix | 16 | ||||
-rw-r--r-- | nixos/modules/system/boot/uki.nix | 33 | ||||
-rw-r--r-- | nixos/modules/system/etc/build-composefs-dump.py | 3 | ||||
-rw-r--r-- | nixos/modules/system/etc/etc.nix | 4 |
13 files changed, 170 insertions, 58 deletions
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index a46331ccd431d..b0ac857feb4b8 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -81,6 +81,13 @@ in extraStructuredConfig.FOO = lib.kernel.yes; features.foo = true; } + { + name = "foo-ml-mbox"; + patch = (fetchurl { + url = "https://lore.kernel.org/lkml/19700205182810.58382-1-email@domain/t.mbox.gz"; + hash = "sha256-..."; + }); + } ] ''; description = lib.mdDoc '' diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index a9978d7adf803..258cf622a894a 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/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/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index ea4553b8208f6..645b764760dad 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/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/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index a7399bd55e770..88d6a2ded873c 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/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/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix index b041b8951fa37..16bca40993aeb 100644 --- a/nixos/modules/system/boot/plymouth.nix +++ b/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/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index e990aeea7a14b..90a74c0ac5788 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -3,7 +3,7 @@ # the modules necessary to mount the root file system, then calls the # init in the root file system to start the second boot stage. -{ config, lib, utils, pkgs, ... }: +{ config, options, lib, utils, pkgs, ... }: with lib; @@ -621,6 +621,11 @@ in path the secret should have inside the initrd, the value is the path it should be copied from (or null for the same path inside and out). + + Note that `nixos-rebuild switch` will generate the initrd + also for past generations, so if secrets are moved or deleted + you will also have to garbage collect the generations that + use those secrets. ''; example = literalExpression '' @@ -631,10 +636,8 @@ in }; boot.initrd.supportedFilesystems = mkOption { - default = [ ]; - example = [ "btrfs" ]; - type = types.listOf types.str; - description = lib.mdDoc "Names of supported filesystem types in the initial ramdisk."; + default = { }; + inherit (options.boot.supportedFilesystems) example type description; }; boot.initrd.verbose = mkOption { diff --git a/nixos/modules/system/boot/systemd/coredump.nix b/nixos/modules/system/boot/systemd/coredump.nix index 03ef00e5683c1..271d8f86d0e61 100644 --- a/nixos/modules/system/boot/systemd/coredump.nix +++ b/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/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 9641921fc795a..f83837fbc6d41 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -90,8 +90,6 @@ let inherit (cfg) packages package; }; - fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems; - kernel-name = config.boot.kernelPackages.kernel.name or "kernel"; modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; }; firmware = config.hardware.firmware; diff --git a/nixos/modules/system/boot/systemd/oomd.nix b/nixos/modules/system/boot/systemd/oomd.nix index 000b18c01609a..edc25784367a4 100644 --- a/nixos/modules/system/boot/systemd/oomd.nix +++ b/nixos/modules/system/boot/systemd/oomd.nix @@ -49,15 +49,15 @@ in { systemd.slices."-".sliceConfig = lib.mkIf cfg.enableRootSlice { ManagedOOMMemoryPressure = "kill"; - ManagedOOMMemoryPressureLimit = "80%"; + ManagedOOMMemoryPressureLimit = lib.mkDefault "80%"; }; systemd.slices."system".sliceConfig = lib.mkIf cfg.enableSystemSlice { ManagedOOMMemoryPressure = "kill"; - ManagedOOMMemoryPressureLimit = "80%"; + ManagedOOMMemoryPressureLimit = lib.mkDefault "80%"; }; systemd.slices."user-".sliceConfig = lib.mkIf cfg.enableUserSlices { ManagedOOMMemoryPressure = "kill"; - ManagedOOMMemoryPressureLimit = "80%"; + ManagedOOMMemoryPressureLimit = lib.mkDefault "80%"; }; systemd.user.units."slice" = lib.mkIf cfg.enableUserSlices { text = '' diff --git a/nixos/modules/system/boot/systemd/repart.nix b/nixos/modules/system/boot/systemd/repart.nix index 3be744acd0b3b..6cc387cb6f43f 100644 --- a/nixos/modules/system/boot/systemd/repart.nix +++ b/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"; diff --git a/nixos/modules/system/boot/uki.nix b/nixos/modules/system/boot/uki.nix index 63c4e0c0e3913..ce00ac8e63978 100644 --- a/nixos/modules/system/boot/uki.nix +++ b/nixos/modules/system/boot/uki.nix @@ -27,6 +27,20 @@ in description = lib.mdDoc "Version of the image or generation the UKI belongs to"; }; + tries = lib.mkOption { + type = lib.types.nullOr lib.types.ints.unsigned; + default = null; + description = lib.mdDoc '' + Number of boot attempts before this UKI is considered bad. + + If no tries are specified (the default) automatic boot assessment remains inactive. + + See documentation on [Automatic Boot Assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/) and + [boot counting](https://uapi-group.org/specifications/specs/boot_loader_specification/#boot-counting) + for more information. + ''; + }; + settings = lib.mkOption { type = format.type; description = lib.mdDoc '' @@ -51,16 +65,16 @@ in else "nixos"); - boot.uki.settings = lib.mkOptionDefault { + boot.uki.settings = { UKI = { - Linux = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; - Initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; - Cmdline = "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"; - Stub = "${pkgs.systemd}/lib/systemd/boot/efi/linux${efiArch}.efi.stub"; - Uname = "${config.boot.kernelPackages.kernel.modDirVersion}"; - OSRelease = "@${config.system.build.etc}/etc/os-release"; + Linux = lib.mkOptionDefault "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; + Initrd = lib.mkOptionDefault "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; + Cmdline = lib.mkOptionDefault "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"; + Stub = lib.mkOptionDefault "${pkgs.systemd}/lib/systemd/boot/efi/linux${efiArch}.efi.stub"; + Uname = lib.mkOptionDefault "${config.boot.kernelPackages.kernel.modDirVersion}"; + OSRelease = lib.mkOptionDefault "@${config.system.build.etc}/etc/os-release"; # This is needed for cross compiling. - EFIArch = efiArch; + EFIArch = lib.mkOptionDefault efiArch; }; }; @@ -69,8 +83,9 @@ in name = config.boot.uki.name; version = config.boot.uki.version; versionInfix = if version != null then "_${version}" else ""; + triesInfix = if cfg.tries != null then "+${builtins.toString cfg.tries}" else ""; in - name + versionInfix + ".efi"; + name + versionInfix + triesInfix + ".efi"; system.build.uki = pkgs.runCommand config.system.boot.loader.ukiFile { } '' mkdir -p $out diff --git a/nixos/modules/system/etc/build-composefs-dump.py b/nixos/modules/system/etc/build-composefs-dump.py index bf4ec791ecf7d..bba454dd888d6 100644 --- a/nixos/modules/system/etc/build-composefs-dump.py +++ b/nixos/modules/system/etc/build-composefs-dump.py @@ -199,7 +199,8 @@ def main() -> None: size=os.stat(source).st_size, filetype=FileType.file, mode=mode, - payload=target, + # payload needs to be relative path in this case + payload=target.lstrip("/"), ) paths[target] = composefs_path add_leading_directories(target, attrs, paths) diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix index baf37ba6def34..9f735364196c6 100644 --- a/nixos/modules/system/etc/etc.nix +++ b/nixos/modules/system/etc/etc.nix @@ -238,7 +238,9 @@ in # this should not run because /etc is mounted via a systemd mount unit # instead. To a large extent this mimics what composefs does. Because # it's relatively simple, however, we avoid the composefs dependency. - if [[ ! $IN_NIXOS_SYSTEMD_STAGE1 ]]; then + # Since this script is not idempotent, it should not run when etc hasn't + # changed. + if [[ ! $IN_NIXOS_SYSTEMD_STAGE1 ]] && [[ "${config.system.build.etc}/etc" != "$(readlink -f /run/current-system/etc)" ]]; then echo "remounting /etc..." tmpMetadataMount=$(mktemp --directory) |