Loading nixos/doc/manual/release-notes/rl-2411.section.md +2 −0 Original line number Diff line number Diff line Loading @@ -631,6 +631,8 @@ The derivation now installs "impl" headers selectively instead of by a wildcard. Use `imgui.src` if you just want to access the unpacked sources. - The new `boot.loader.systemd-boot.windows` option makes setting up dual-booting with Windows on a different drive easier - Linux 4.19 has been removed because it will reach its end of life within the lifespan of 24.11 - Unprivileged access to the kernel syslog via `dmesg` is now restricted by default. Users wanting to keep an Loading nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +293 −92 Original line number Diff line number Diff line { config, lib, pkgs, ... }: { config, lib, pkgs, ... }: with lib; Loading @@ -10,9 +15,12 @@ let # We check the source code in a derivation that does not depend on the # system configuration so that most users don't have to redo the check and require # the necessary dependencies. checkedSource = pkgs.runCommand "systemd-boot" { checkedSource = pkgs.runCommand "systemd-boot" { preferLocalBuild = true; } '' } '' install -m755 -D ${./systemd-boot-builder.py} $out ${lib.getExe pkgs.buildPackages.mypy} \ --no-implicit-optional \ Loading @@ -21,6 +29,8 @@ let $out ''; edk2ShellEspPath = "efi/edk2-uefi-shell/shell.efi"; systemdBootBuilder = pkgs.substituteAll rec { name = "systemd-boot"; Loading @@ -44,13 +54,17 @@ let configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit; inherit (cfg) consoleMode graceful editor rebootForBitlocker; inherit (cfg) consoleMode graceful editor rebootForBitlocker ; inherit (efi) efiSysMountPoint canTouchEfiVariables; bootMountPoint = if cfg.xbootldrMountPoint != null then cfg.xbootldrMountPoint else efi.efiSysMountPoint; bootMountPoint = if cfg.xbootldrMountPoint != null then cfg.xbootldrMountPoint else efi.efiSysMountPoint; nixosDir = "/EFI/nixos"; Loading @@ -60,29 +74,35 @@ let netbootxyz = optionalString cfg.netbootxyz.enable pkgs.netbootxyz-efi; edk2-uefi-shell = optionalString cfg.edk2-uefi-shell.enable pkgs.edk2-uefi-shell; 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}"} ${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: '' ${concatStrings ( mapAttrsToList (n: v: '' ${pkgs.coreutils}/bin/install -Dp "${v}" "${bootMountPoint}/"${escapeShellArg n} ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/"${escapeShellArg n} '') cfg.extraFiles)} '') cfg.extraFiles )} ${concatStrings (mapAttrsToList (n: v: '' ${concatStrings ( mapAttrsToList (n: v: '' ${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)} '') cfg.extraEntries )} ''; }; Loading @@ -91,20 +111,58 @@ let ${systemdBootBuilder}/bin/systemd-boot "$@" ${cfg.extraInstallCommands} ''; in { in { meta.maintainers = with lib.maintainers; [ julienmalka ]; imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ] ) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.memtest86.entryFilename) ) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.netbootxyz.entryFilename) ) ]; Loading @@ -124,7 +182,7 @@ in { sortKey = mkOption { default = "nixos"; type = lib.types.str; type = types.str; description = '' The sort key used for the NixOS bootloader entries. This key determines sorting relative to non-NixOS entries. Loading Loading @@ -218,7 +276,15 @@ in { consoleMode = mkOption { default = "keep"; type = types.enum [ "0" "1" "2" "5" "auto" "max" "keep" ]; type = types.enum [ "0" "1" "2" "5" "auto" "max" "keep" ]; description = '' The resolution of the console. The following values are valid: Loading Loading @@ -281,6 +347,29 @@ in { }; }; edk2-uefi-shell = { enable = mkOption { type = types.bool; default = false; description = '' Make the EDK2 UEFI Shell available from the systemd-boot menu. It can be used to manually boot other operating systems or for debugging. ''; }; sortKey = mkOption { type = types.str; default = "o_edk2-uefi-shell"; description = '' `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`.. ''; }; }; extraEntries = mkOption { type = types.attrsOf types.lines; default = { }; Loading Loading @@ -349,10 +438,92 @@ in { Windows can unseal the encryption key. ''; }; windows = mkOption { default = { }; description = '' Make Windows bootable from systemd-boot. This option is not necessary when Windows and NixOS use the same EFI System Partition (ESP). In that case, Windows will automatically be detected by systemd-boot. However, if Windows is installed on a separate drive or ESP, you can use this option to add a menu entry for each installation manually. The attribute name is used for the title of the menu entry and internal file names. ''; example = literalExpression '' { "10".efiDeviceHandle = "HD0c3"; "11-ame" = { title = "Windows 11 Ameliorated Edition"; efiDeviceHandle = "HD0b1"; }; "11-home" = { title = "Windows 11 Home"; efiDeviceHandle = "FS1"; sortKey = "z_windows"; }; } ''; type = types.attrsOf ( types.submodule ( { name, ... }: { options = { efiDeviceHandle = mkOption { type = types.str; example = "HD1b3"; description = '' The device handle of the EFI System Partition (ESP) where the Windows bootloader is located. This is the device handle that the EDK2 UEFI Shell uses to load the bootloader. To find this handle, follow these steps: 1. Set {option}`boot.loader.systemd-boot.edk2-uefi-shell.enable` to `true` 2. Run `nixos-rebuild boot` 3. Reboot and select "EDK2 UEFI Shell" from the systemd-boot menu 4. Run `map -c` to list all consistent device handles 5. For each device handle (for example, `HD0c1`), run `ls HD0c1:\EFI` 6. If the output contains the directory `Microsoft`, you might have found the correct device handle 7. Run `HD0c1:\EFI\Microsoft\Boot\Bootmgfw.efi` to check if Windows boots correctly 8. If it does, this device handle is the one you need (in this example, `HD0c1`) This option is required, there is no useful default. ''; }; title = mkOption { type = types.str; example = "Michaelsoft Binbows"; default = "Windows ${name}"; defaultText = ''attribute name of this entry, prefixed with "Windows "''; description = '' The title of the boot menu entry. ''; }; sortKey = mkOption { type = types.str; default = "o_windows_${name}"; defaultText = ''attribute name of this entry, prefixed with "o_windows_"''; description = '' `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`.. ''; }; }; } ) ); }; }; config = mkIf cfg.enable { assertions = [ assertions = [ { assertion = (hasPrefix "/" efi.efiSysMountPoint); message = "The ESP mount point '${toString efi.efiSysMountPoint}' must be an absolute path"; Loading @@ -370,10 +541,14 @@ in { message = "This kernel does not support the EFI boot stub"; } { assertion = cfg.installDeviceTree -> config.hardware.deviceTree.enable -> config.hardware.deviceTree.name != null; assertion = cfg.installDeviceTree -> config.hardware.deviceTree.enable -> config.hardware.deviceTree.name != null; message = "Cannot install devicetree without 'config.hardware.deviceTree.enable' enabled and 'config.hardware.deviceTree.name' set"; } ] ++ concatMap (filename: [ ] ++ concatMap (filename: [ { assertion = !(hasInfix "/" filename); message = "boot.loader.systemd-boot.extraEntries.${lib.strings.escapeNixIdentifier filename} is invalid: entries within folders are not supported"; Loading @@ -396,7 +571,13 @@ in { assertion = !(hasInfix "nixos/.extra-files" (toLower filename)); message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: files cannot be placed in the nixos/.extra-files directory"; } ]) (builtins.attrNames cfg.extraFiles); ]) (builtins.attrNames cfg.extraFiles) ++ concatMap (winVersion: [ { assertion = lib.match "^[-_0-9A-Za-z]+$" winVersion != null; message = "boot.loader.systemd-boot.windows.${winVersion} is invalid: key must only contain alphanumeric characters, hyphens, and underscores"; } ]) (builtins.attrNames cfg.windows); boot.loader.grub.enable = mkDefault false; Loading @@ -409,9 +590,13 @@ in { (mkIf cfg.netbootxyz.enable { "efi/netbootxyz/netboot.xyz.efi" = "${pkgs.netbootxyz-efi}"; }) (mkIf (cfg.edk2-uefi-shell.enable || cfg.windows != { }) { ${edk2ShellEspPath} = "${pkgs.edk2-uefi-shell}/shell.efi"; }) ]; boot.loader.systemd-boot.extraEntries = mkMerge [ boot.loader.systemd-boot.extraEntries = mkMerge ( [ (mkIf cfg.memtest86.enable { "memtest86.conf" = '' title Memtest86+ Loading @@ -426,7 +611,23 @@ in { sort-key ${cfg.netbootxyz.sortKey} ''; }) ]; (mkIf cfg.edk2-uefi-shell.enable { "edk2-uefi-shell.conf" = '' title EDK2 UEFI Shell efi /${edk2ShellEspPath} sort-key ${cfg.edk2-uefi-shell.sortKey} ''; }) ] ++ (mapAttrsToList (winVersion: cfg: { "windows_${winVersion}.conf" = '' title ${cfg.title} efi /${edk2ShellEspPath} options -nointerrupt -nomap -noversion ${cfg.efiDeviceHandle}:EFI\Microsoft\Boot\Bootmgfw.efi sort-key ${cfg.sortKey} ''; }) cfg.windows) ); boot.bootspec.extensions."org.nixos.systemd-boot" = { inherit (config.boot.loader.systemd-boot) sortKey; Loading nixos/tests/systemd-boot.nix +305 −187 Original line number Diff line number Diff line { system ? builtins.currentSystem, { system ? builtins.currentSystem, config ? { }, pkgs ? import ../.. { inherit system config; } pkgs ? import ../.. { inherit system config; }, }: with import ../lib/testing-python.nix { inherit system pkgs; }; Loading @@ -16,7 +17,13 @@ let system.switch.enable = true; }; commonXbootldr = { config, lib, pkgs, ... }: commonXbootldr = { config, lib, pkgs, ... }: let diskImage = import ../lib/make-disk-image.nix { inherit config lib pkgs; Loading Loading @@ -85,7 +92,10 @@ in { basic = makeTest { name = "systemd-boot"; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = common; Loading Loading @@ -117,9 +127,12 @@ in virtualisation.useSecureBoot = true; }; testScript = let testScript = let efiArch = pkgs.stdenv.hostPlatform.efiArch; in { nodes, ... }: '' in { nodes, ... }: '' machine.start(allow_reboot=True) machine.wait_for_unit("multi-user.target") Loading @@ -141,7 +154,9 @@ in nodes.machine = commonXbootldr; testScript = { nodes, ... }: '' testScript = { nodes, ... }: '' ${customDiskImage nodes} machine.start() Loading @@ -164,9 +179,14 @@ in # Check that specialisations create corresponding boot entries. specialisation = makeTest { name = "systemd-boot-specialisation"; meta.maintainers = with pkgs.lib.maintainers; [ lukegb julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ lukegb julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; specialisation.something.configuration = { boot.loader.systemd-boot.sortKey = "something"; Loading @@ -179,14 +199,18 @@ in # the correct contents. boot.loader.systemd-boot.installDeviceTree = pkgs.stdenv.hostPlatform.isAarch64; hardware.deviceTree.name = "dummy.dtb"; hardware.deviceTree.package = lib.mkForce (pkgs.runCommand "dummy-devicetree-package" { } '' hardware.deviceTree.package = lib.mkForce ( pkgs.runCommand "dummy-devicetree-package" { } '' mkdir -p $out cp ${pkgs.emptyFile} $out/dummy.dtb ''); '' ); }; }; testScript = { nodes, ... }: '' testScript = { nodes, ... }: '' machine.start() machine.wait_for_unit("multi-user.target") Loading @@ -199,7 +223,8 @@ in machine.succeed( "grep 'sort-key something' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) '' + pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isAarch64 '' '' + pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isAarch64 '' machine.succeed( r"grep 'devicetree /EFI/nixos/[a-z0-9]\{32\}.*dummy' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) Loading @@ -209,9 +234,14 @@ in # Boot without having created an EFI entry--instead using default "/EFI/BOOT/BOOTX64.EFI" fallback = makeTest { name = "systemd-boot-fallback"; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.efi.canTouchEfiVariables = mkForce false; }; Loading @@ -235,7 +265,10 @@ in update = makeTest { name = "systemd-boot-update"; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = common; Loading Loading @@ -270,11 +303,15 @@ in ''; }; memtest86 = with pkgs.lib; optionalAttrs (meta.availableOn { inherit system; } pkgs.memtest86plus) (makeTest { memtest86 = with pkgs.lib; optionalAttrs (meta.availableOn { inherit system; } pkgs.memtest86plus) (makeTest { name = "systemd-boot-memtest86"; meta.maintainers = with maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; }; Loading @@ -289,7 +326,9 @@ in name = "systemd-boot-netbootxyz"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.netbootxyz.enable = true; }; Loading @@ -300,11 +339,73 @@ in ''; }; edk2-uefi-shell = makeTest { name = "systemd-boot-edk2-uefi-shell"; meta.maintainers = with pkgs.lib.maintainers; [ iFreilicht ]; nodes.machine = { ... }: { imports = [ common ]; boot.loader.systemd-boot.edk2-uefi-shell.enable = true; }; testScript = '' machine.succeed("test -e /boot/loader/entries/edk2-uefi-shell.conf") machine.succeed("test -e /boot/efi/edk2-uefi-shell/shell.efi") ''; }; windows = makeTest { name = "systemd-boot-windows"; meta.maintainers = with pkgs.lib.maintainers; [ iFreilicht ]; nodes.machine = { ... }: { imports = [ common ]; boot.loader.systemd-boot.windows = { "7" = { efiDeviceHandle = "HD0c1"; sortKey = "before_all_others"; }; "Ten".efiDeviceHandle = "FS0"; "11" = { title = "Title with-_-punctuation ...?!"; efiDeviceHandle = "HD0d4"; sortKey = "zzz"; }; }; }; testScript = '' machine.succeed("test -e /boot/efi/edk2-uefi-shell/shell.efi") machine.succeed("test -e /boot/loader/entries/windows_7.conf") machine.succeed("test -e /boot/loader/entries/windows_Ten.conf") machine.succeed("test -e /boot/loader/entries/windows_11.conf") machine.succeed("grep 'efi /efi/edk2-uefi-shell/shell.efi' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'efi /efi/edk2-uefi-shell/shell.efi' /boot/loader/entries/windows_Ten.conf") machine.succeed("grep 'efi /efi/edk2-uefi-shell/shell.efi' /boot/loader/entries/windows_11.conf") machine.succeed("grep 'HD0c1:EFI\\\\Microsoft\\\\Boot\\\\Bootmgfw.efi' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'FS0:EFI\\\\Microsoft\\\\Boot\\\\Bootmgfw.efi' /boot/loader/entries/windows_Ten.conf") machine.succeed("grep 'HD0d4:EFI\\\\Microsoft\\\\Boot\\\\Bootmgfw.efi' /boot/loader/entries/windows_11.conf") machine.succeed("grep 'sort-key before_all_others' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'sort-key o_windows_Ten' /boot/loader/entries/windows_Ten.conf") machine.succeed("grep 'sort-key zzz' /boot/loader/entries/windows_11.conf") machine.succeed("grep 'title Windows 7' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'title Windows Ten' /boot/loader/entries/windows_Ten.conf") machine.succeed('grep "title Title with-_-punctuation ...?!" /boot/loader/entries/windows_11.conf') ''; }; memtestSortKey = makeTest { name = "systemd-boot-memtest-sortkey"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.sortKey = "apple"; Loading @@ -321,12 +422,16 @@ in name = "systemd-boot-entry-filename-xbootldr"; meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ commonXbootldr ]; boot.loader.systemd-boot.memtest86.enable = true; }; testScript = { nodes, ... }: '' testScript = { nodes, ... }: '' ${customDiskImage nodes} machine.start() Loading @@ -342,7 +447,9 @@ in name = "systemd-boot-extra-entries"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.extraEntries = { "banana.conf" = '' Loading @@ -361,7 +468,9 @@ in name = "systemd-boot-extra-files"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.extraFiles = { "efi/fruits/tomato.efi" = pkgs.netbootxyz-efi; Loading @@ -381,7 +490,9 @@ in nodes = { inherit common; machine = { pkgs, nodes, ... }: { machine = { pkgs, nodes, ... }: { imports = [ common ]; boot.loader.systemd-boot.extraFiles = { "efi/fruits/tomato.efi" = pkgs.netbootxyz-efi; Loading @@ -394,17 +505,22 @@ in ]; }; with_netbootxyz = { pkgs, ... }: { with_netbootxyz = { pkgs, ... }: { imports = [ common ]; boot.loader.systemd-boot.netbootxyz.enable = true; }; }; testScript = { nodes, ... }: let testScript = { nodes, ... }: let originalSystem = nodes.machine.system.build.toplevel; baseSystem = nodes.common.system.build.toplevel; finalSystem = nodes.with_netbootxyz.system.build.toplevel; in '' in '' machine.succeed("test -e /boot/efi/fruits/tomato.efi") machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi") Loading Loading @@ -438,7 +554,9 @@ in nodes = { inherit common; machine = { pkgs, nodes, ... }: { machine = { pkgs, nodes, ... }: { imports = [ common ]; # These are configs for different nodes, but we'll use them here in `machine` Loading @@ -448,7 +566,8 @@ in }; }; testScript = { nodes, ... }: testScript = { nodes, ... }: let baseSystem = nodes.common.system.build.toplevel; in Loading @@ -461,8 +580,7 @@ in ''; }; no-bootspec = makeTest { no-bootspec = makeTest { name = "systemd-boot-no-bootspec"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; Loading Loading
nixos/doc/manual/release-notes/rl-2411.section.md +2 −0 Original line number Diff line number Diff line Loading @@ -631,6 +631,8 @@ The derivation now installs "impl" headers selectively instead of by a wildcard. Use `imgui.src` if you just want to access the unpacked sources. - The new `boot.loader.systemd-boot.windows` option makes setting up dual-booting with Windows on a different drive easier - Linux 4.19 has been removed because it will reach its end of life within the lifespan of 24.11 - Unprivileged access to the kernel syslog via `dmesg` is now restricted by default. Users wanting to keep an Loading
nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +293 −92 Original line number Diff line number Diff line { config, lib, pkgs, ... }: { config, lib, pkgs, ... }: with lib; Loading @@ -10,9 +15,12 @@ let # We check the source code in a derivation that does not depend on the # system configuration so that most users don't have to redo the check and require # the necessary dependencies. checkedSource = pkgs.runCommand "systemd-boot" { checkedSource = pkgs.runCommand "systemd-boot" { preferLocalBuild = true; } '' } '' install -m755 -D ${./systemd-boot-builder.py} $out ${lib.getExe pkgs.buildPackages.mypy} \ --no-implicit-optional \ Loading @@ -21,6 +29,8 @@ let $out ''; edk2ShellEspPath = "efi/edk2-uefi-shell/shell.efi"; systemdBootBuilder = pkgs.substituteAll rec { name = "systemd-boot"; Loading @@ -44,13 +54,17 @@ let configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit; inherit (cfg) consoleMode graceful editor rebootForBitlocker; inherit (cfg) consoleMode graceful editor rebootForBitlocker ; inherit (efi) efiSysMountPoint canTouchEfiVariables; bootMountPoint = if cfg.xbootldrMountPoint != null then cfg.xbootldrMountPoint else efi.efiSysMountPoint; bootMountPoint = if cfg.xbootldrMountPoint != null then cfg.xbootldrMountPoint else efi.efiSysMountPoint; nixosDir = "/EFI/nixos"; Loading @@ -60,29 +74,35 @@ let netbootxyz = optionalString cfg.netbootxyz.enable pkgs.netbootxyz-efi; edk2-uefi-shell = optionalString cfg.edk2-uefi-shell.enable pkgs.edk2-uefi-shell; 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}"} ${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: '' ${concatStrings ( mapAttrsToList (n: v: '' ${pkgs.coreutils}/bin/install -Dp "${v}" "${bootMountPoint}/"${escapeShellArg n} ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/"${escapeShellArg n} '') cfg.extraFiles)} '') cfg.extraFiles )} ${concatStrings (mapAttrsToList (n: v: '' ${concatStrings ( mapAttrsToList (n: v: '' ${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)} '') cfg.extraEntries )} ''; }; Loading @@ -91,20 +111,58 @@ let ${systemdBootBuilder}/bin/systemd-boot "$@" ${cfg.extraInstallCommands} ''; in { in { meta.maintainers = with lib.maintainers; [ julienmalka ]; imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) imports = [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ] ) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] [ "boot" "loader" "systemd-boot" "memtest86" "entryFilename" ] [ "boot" "loader" "systemd-boot" "memtest86" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.memtest86.entryFilename) ) (lib.mkChangedOptionModule [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "entryFilename" ] [ "boot" "loader" "systemd-boot" "netbootxyz" "sortKey" ] (config: lib.strings.removeSuffix ".conf" config.boot.loader.systemd-boot.netbootxyz.entryFilename) ) ]; Loading @@ -124,7 +182,7 @@ in { sortKey = mkOption { default = "nixos"; type = lib.types.str; type = types.str; description = '' The sort key used for the NixOS bootloader entries. This key determines sorting relative to non-NixOS entries. Loading Loading @@ -218,7 +276,15 @@ in { consoleMode = mkOption { default = "keep"; type = types.enum [ "0" "1" "2" "5" "auto" "max" "keep" ]; type = types.enum [ "0" "1" "2" "5" "auto" "max" "keep" ]; description = '' The resolution of the console. The following values are valid: Loading Loading @@ -281,6 +347,29 @@ in { }; }; edk2-uefi-shell = { enable = mkOption { type = types.bool; default = false; description = '' Make the EDK2 UEFI Shell available from the systemd-boot menu. It can be used to manually boot other operating systems or for debugging. ''; }; sortKey = mkOption { type = types.str; default = "o_edk2-uefi-shell"; description = '' `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`.. ''; }; }; extraEntries = mkOption { type = types.attrsOf types.lines; default = { }; Loading Loading @@ -349,10 +438,92 @@ in { Windows can unseal the encryption key. ''; }; windows = mkOption { default = { }; description = '' Make Windows bootable from systemd-boot. This option is not necessary when Windows and NixOS use the same EFI System Partition (ESP). In that case, Windows will automatically be detected by systemd-boot. However, if Windows is installed on a separate drive or ESP, you can use this option to add a menu entry for each installation manually. The attribute name is used for the title of the menu entry and internal file names. ''; example = literalExpression '' { "10".efiDeviceHandle = "HD0c3"; "11-ame" = { title = "Windows 11 Ameliorated Edition"; efiDeviceHandle = "HD0b1"; }; "11-home" = { title = "Windows 11 Home"; efiDeviceHandle = "FS1"; sortKey = "z_windows"; }; } ''; type = types.attrsOf ( types.submodule ( { name, ... }: { options = { efiDeviceHandle = mkOption { type = types.str; example = "HD1b3"; description = '' The device handle of the EFI System Partition (ESP) where the Windows bootloader is located. This is the device handle that the EDK2 UEFI Shell uses to load the bootloader. To find this handle, follow these steps: 1. Set {option}`boot.loader.systemd-boot.edk2-uefi-shell.enable` to `true` 2. Run `nixos-rebuild boot` 3. Reboot and select "EDK2 UEFI Shell" from the systemd-boot menu 4. Run `map -c` to list all consistent device handles 5. For each device handle (for example, `HD0c1`), run `ls HD0c1:\EFI` 6. If the output contains the directory `Microsoft`, you might have found the correct device handle 7. Run `HD0c1:\EFI\Microsoft\Boot\Bootmgfw.efi` to check if Windows boots correctly 8. If it does, this device handle is the one you need (in this example, `HD0c1`) This option is required, there is no useful default. ''; }; title = mkOption { type = types.str; example = "Michaelsoft Binbows"; default = "Windows ${name}"; defaultText = ''attribute name of this entry, prefixed with "Windows "''; description = '' The title of the boot menu entry. ''; }; sortKey = mkOption { type = types.str; default = "o_windows_${name}"; defaultText = ''attribute name of this entry, prefixed with "o_windows_"''; description = '' `systemd-boot` orders the menu entries by their sort keys, so if you want something to appear after all the NixOS entries, it should start with {file}`o` or onwards. See also {option}`boot.loader.systemd-boot.sortKey`.. ''; }; }; } ) ); }; }; config = mkIf cfg.enable { assertions = [ assertions = [ { assertion = (hasPrefix "/" efi.efiSysMountPoint); message = "The ESP mount point '${toString efi.efiSysMountPoint}' must be an absolute path"; Loading @@ -370,10 +541,14 @@ in { message = "This kernel does not support the EFI boot stub"; } { assertion = cfg.installDeviceTree -> config.hardware.deviceTree.enable -> config.hardware.deviceTree.name != null; assertion = cfg.installDeviceTree -> config.hardware.deviceTree.enable -> config.hardware.deviceTree.name != null; message = "Cannot install devicetree without 'config.hardware.deviceTree.enable' enabled and 'config.hardware.deviceTree.name' set"; } ] ++ concatMap (filename: [ ] ++ concatMap (filename: [ { assertion = !(hasInfix "/" filename); message = "boot.loader.systemd-boot.extraEntries.${lib.strings.escapeNixIdentifier filename} is invalid: entries within folders are not supported"; Loading @@ -396,7 +571,13 @@ in { assertion = !(hasInfix "nixos/.extra-files" (toLower filename)); message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: files cannot be placed in the nixos/.extra-files directory"; } ]) (builtins.attrNames cfg.extraFiles); ]) (builtins.attrNames cfg.extraFiles) ++ concatMap (winVersion: [ { assertion = lib.match "^[-_0-9A-Za-z]+$" winVersion != null; message = "boot.loader.systemd-boot.windows.${winVersion} is invalid: key must only contain alphanumeric characters, hyphens, and underscores"; } ]) (builtins.attrNames cfg.windows); boot.loader.grub.enable = mkDefault false; Loading @@ -409,9 +590,13 @@ in { (mkIf cfg.netbootxyz.enable { "efi/netbootxyz/netboot.xyz.efi" = "${pkgs.netbootxyz-efi}"; }) (mkIf (cfg.edk2-uefi-shell.enable || cfg.windows != { }) { ${edk2ShellEspPath} = "${pkgs.edk2-uefi-shell}/shell.efi"; }) ]; boot.loader.systemd-boot.extraEntries = mkMerge [ boot.loader.systemd-boot.extraEntries = mkMerge ( [ (mkIf cfg.memtest86.enable { "memtest86.conf" = '' title Memtest86+ Loading @@ -426,7 +611,23 @@ in { sort-key ${cfg.netbootxyz.sortKey} ''; }) ]; (mkIf cfg.edk2-uefi-shell.enable { "edk2-uefi-shell.conf" = '' title EDK2 UEFI Shell efi /${edk2ShellEspPath} sort-key ${cfg.edk2-uefi-shell.sortKey} ''; }) ] ++ (mapAttrsToList (winVersion: cfg: { "windows_${winVersion}.conf" = '' title ${cfg.title} efi /${edk2ShellEspPath} options -nointerrupt -nomap -noversion ${cfg.efiDeviceHandle}:EFI\Microsoft\Boot\Bootmgfw.efi sort-key ${cfg.sortKey} ''; }) cfg.windows) ); boot.bootspec.extensions."org.nixos.systemd-boot" = { inherit (config.boot.loader.systemd-boot) sortKey; Loading
nixos/tests/systemd-boot.nix +305 −187 Original line number Diff line number Diff line { system ? builtins.currentSystem, { system ? builtins.currentSystem, config ? { }, pkgs ? import ../.. { inherit system config; } pkgs ? import ../.. { inherit system config; }, }: with import ../lib/testing-python.nix { inherit system pkgs; }; Loading @@ -16,7 +17,13 @@ let system.switch.enable = true; }; commonXbootldr = { config, lib, pkgs, ... }: commonXbootldr = { config, lib, pkgs, ... }: let diskImage = import ../lib/make-disk-image.nix { inherit config lib pkgs; Loading Loading @@ -85,7 +92,10 @@ in { basic = makeTest { name = "systemd-boot"; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = common; Loading Loading @@ -117,9 +127,12 @@ in virtualisation.useSecureBoot = true; }; testScript = let testScript = let efiArch = pkgs.stdenv.hostPlatform.efiArch; in { nodes, ... }: '' in { nodes, ... }: '' machine.start(allow_reboot=True) machine.wait_for_unit("multi-user.target") Loading @@ -141,7 +154,9 @@ in nodes.machine = commonXbootldr; testScript = { nodes, ... }: '' testScript = { nodes, ... }: '' ${customDiskImage nodes} machine.start() Loading @@ -164,9 +179,14 @@ in # Check that specialisations create corresponding boot entries. specialisation = makeTest { name = "systemd-boot-specialisation"; meta.maintainers = with pkgs.lib.maintainers; [ lukegb julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ lukegb julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; specialisation.something.configuration = { boot.loader.systemd-boot.sortKey = "something"; Loading @@ -179,14 +199,18 @@ in # the correct contents. boot.loader.systemd-boot.installDeviceTree = pkgs.stdenv.hostPlatform.isAarch64; hardware.deviceTree.name = "dummy.dtb"; hardware.deviceTree.package = lib.mkForce (pkgs.runCommand "dummy-devicetree-package" { } '' hardware.deviceTree.package = lib.mkForce ( pkgs.runCommand "dummy-devicetree-package" { } '' mkdir -p $out cp ${pkgs.emptyFile} $out/dummy.dtb ''); '' ); }; }; testScript = { nodes, ... }: '' testScript = { nodes, ... }: '' machine.start() machine.wait_for_unit("multi-user.target") Loading @@ -199,7 +223,8 @@ in machine.succeed( "grep 'sort-key something' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) '' + pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isAarch64 '' '' + pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isAarch64 '' machine.succeed( r"grep 'devicetree /EFI/nixos/[a-z0-9]\{32\}.*dummy' /boot/loader/entries/nixos-generation-1-specialisation-something.conf" ) Loading @@ -209,9 +234,14 @@ in # Boot without having created an EFI entry--instead using default "/EFI/BOOT/BOOTX64.EFI" fallback = makeTest { name = "systemd-boot-fallback"; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.efi.canTouchEfiVariables = mkForce false; }; Loading @@ -235,7 +265,10 @@ in update = makeTest { name = "systemd-boot-update"; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ]; nodes.machine = common; Loading Loading @@ -270,11 +303,15 @@ in ''; }; memtest86 = with pkgs.lib; optionalAttrs (meta.availableOn { inherit system; } pkgs.memtest86plus) (makeTest { memtest86 = with pkgs.lib; optionalAttrs (meta.availableOn { inherit system; } pkgs.memtest86plus) (makeTest { name = "systemd-boot-memtest86"; meta.maintainers = with maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; }; Loading @@ -289,7 +326,9 @@ in name = "systemd-boot-netbootxyz"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.netbootxyz.enable = true; }; Loading @@ -300,11 +339,73 @@ in ''; }; edk2-uefi-shell = makeTest { name = "systemd-boot-edk2-uefi-shell"; meta.maintainers = with pkgs.lib.maintainers; [ iFreilicht ]; nodes.machine = { ... }: { imports = [ common ]; boot.loader.systemd-boot.edk2-uefi-shell.enable = true; }; testScript = '' machine.succeed("test -e /boot/loader/entries/edk2-uefi-shell.conf") machine.succeed("test -e /boot/efi/edk2-uefi-shell/shell.efi") ''; }; windows = makeTest { name = "systemd-boot-windows"; meta.maintainers = with pkgs.lib.maintainers; [ iFreilicht ]; nodes.machine = { ... }: { imports = [ common ]; boot.loader.systemd-boot.windows = { "7" = { efiDeviceHandle = "HD0c1"; sortKey = "before_all_others"; }; "Ten".efiDeviceHandle = "FS0"; "11" = { title = "Title with-_-punctuation ...?!"; efiDeviceHandle = "HD0d4"; sortKey = "zzz"; }; }; }; testScript = '' machine.succeed("test -e /boot/efi/edk2-uefi-shell/shell.efi") machine.succeed("test -e /boot/loader/entries/windows_7.conf") machine.succeed("test -e /boot/loader/entries/windows_Ten.conf") machine.succeed("test -e /boot/loader/entries/windows_11.conf") machine.succeed("grep 'efi /efi/edk2-uefi-shell/shell.efi' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'efi /efi/edk2-uefi-shell/shell.efi' /boot/loader/entries/windows_Ten.conf") machine.succeed("grep 'efi /efi/edk2-uefi-shell/shell.efi' /boot/loader/entries/windows_11.conf") machine.succeed("grep 'HD0c1:EFI\\\\Microsoft\\\\Boot\\\\Bootmgfw.efi' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'FS0:EFI\\\\Microsoft\\\\Boot\\\\Bootmgfw.efi' /boot/loader/entries/windows_Ten.conf") machine.succeed("grep 'HD0d4:EFI\\\\Microsoft\\\\Boot\\\\Bootmgfw.efi' /boot/loader/entries/windows_11.conf") machine.succeed("grep 'sort-key before_all_others' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'sort-key o_windows_Ten' /boot/loader/entries/windows_Ten.conf") machine.succeed("grep 'sort-key zzz' /boot/loader/entries/windows_11.conf") machine.succeed("grep 'title Windows 7' /boot/loader/entries/windows_7.conf") machine.succeed("grep 'title Windows Ten' /boot/loader/entries/windows_Ten.conf") machine.succeed('grep "title Title with-_-punctuation ...?!" /boot/loader/entries/windows_11.conf') ''; }; memtestSortKey = makeTest { name = "systemd-boot-memtest-sortkey"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.sortKey = "apple"; Loading @@ -321,12 +422,16 @@ in name = "systemd-boot-entry-filename-xbootldr"; meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ commonXbootldr ]; boot.loader.systemd-boot.memtest86.enable = true; }; testScript = { nodes, ... }: '' testScript = { nodes, ... }: '' ${customDiskImage nodes} machine.start() Loading @@ -342,7 +447,9 @@ in name = "systemd-boot-extra-entries"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.extraEntries = { "banana.conf" = '' Loading @@ -361,7 +468,9 @@ in name = "systemd-boot-extra-files"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; nodes.machine = { pkgs, lib, ... }: { nodes.machine = { pkgs, lib, ... }: { imports = [ common ]; boot.loader.systemd-boot.extraFiles = { "efi/fruits/tomato.efi" = pkgs.netbootxyz-efi; Loading @@ -381,7 +490,9 @@ in nodes = { inherit common; machine = { pkgs, nodes, ... }: { machine = { pkgs, nodes, ... }: { imports = [ common ]; boot.loader.systemd-boot.extraFiles = { "efi/fruits/tomato.efi" = pkgs.netbootxyz-efi; Loading @@ -394,17 +505,22 @@ in ]; }; with_netbootxyz = { pkgs, ... }: { with_netbootxyz = { pkgs, ... }: { imports = [ common ]; boot.loader.systemd-boot.netbootxyz.enable = true; }; }; testScript = { nodes, ... }: let testScript = { nodes, ... }: let originalSystem = nodes.machine.system.build.toplevel; baseSystem = nodes.common.system.build.toplevel; finalSystem = nodes.with_netbootxyz.system.build.toplevel; in '' in '' machine.succeed("test -e /boot/efi/fruits/tomato.efi") machine.succeed("test -e /boot/efi/nixos/.extra-files/efi/fruits/tomato.efi") Loading Loading @@ -438,7 +554,9 @@ in nodes = { inherit common; machine = { pkgs, nodes, ... }: { machine = { pkgs, nodes, ... }: { imports = [ common ]; # These are configs for different nodes, but we'll use them here in `machine` Loading @@ -448,7 +566,8 @@ in }; }; testScript = { nodes, ... }: testScript = { nodes, ... }: let baseSystem = nodes.common.system.build.toplevel; in Loading @@ -461,8 +580,7 @@ in ''; }; no-bootspec = makeTest { no-bootspec = makeTest { name = "systemd-boot-no-bootspec"; meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ]; Loading