at 23.11-beta 10 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.boot.loader.systemd-boot; 7 8 efi = config.boot.loader.efi; 9 10 systemdBootBuilder = pkgs.substituteAll { 11 src = ./systemd-boot-builder.py; 12 13 isExecutable = true; 14 15 inherit (pkgs) python3; 16 17 systemd = config.systemd.package; 18 19 bootspecTools = pkgs.bootspec; 20 21 nix = config.nix.package.out; 22 23 timeout = optionalString (config.boot.loader.timeout != null) config.boot.loader.timeout; 24 25 editor = if cfg.editor then "True" else "False"; 26 27 configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit; 28 29 inherit (cfg) consoleMode graceful; 30 31 inherit (efi) efiSysMountPoint canTouchEfiVariables; 32 33 inherit (config.system.nixos) distroName; 34 35 memtest86 = optionalString cfg.memtest86.enable pkgs.memtest86plus; 36 37 netbootxyz = optionalString cfg.netbootxyz.enable pkgs.netbootxyz-efi; 38 39 copyExtraFiles = pkgs.writeShellScript "copy-extra-files" '' 40 empty_file=$(${pkgs.coreutils}/bin/mktemp) 41 42 ${concatStrings (mapAttrsToList (n: v: '' 43 ${pkgs.coreutils}/bin/install -Dp "${v}" "${efi.efiSysMountPoint}/"${escapeShellArg n} 44 ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/"${escapeShellArg n} 45 '') cfg.extraFiles)} 46 47 ${concatStrings (mapAttrsToList (n: v: '' 48 ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${efi.efiSysMountPoint}/loader/entries/"${escapeShellArg n} 49 ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/loader/entries/"${escapeShellArg n} 50 '') cfg.extraEntries)} 51 ''; 52 }; 53 54 checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { 55 nativeBuildInputs = [ pkgs.mypy ]; 56 } '' 57 install -m755 ${systemdBootBuilder} $out 58 mypy \ 59 --no-implicit-optional \ 60 --disallow-untyped-calls \ 61 --disallow-untyped-defs \ 62 $out 63 ''; 64 65 finalSystemdBootBuilder = pkgs.writeScript "install-systemd-boot.sh" '' 66 #!${pkgs.runtimeShell} 67 ${checkedSystemdBootBuilder} "$@" 68 ${cfg.extraInstallCommands} 69 ''; 70in { 71 72 meta.maintainers = with lib.maintainers; [ julienmalka ]; 73 74 imports = 75 [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) 76 ]; 77 78 options.boot.loader.systemd-boot = { 79 enable = mkOption { 80 default = false; 81 82 type = types.bool; 83 84 description = lib.mdDoc "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager"; 85 }; 86 87 editor = mkOption { 88 default = true; 89 90 type = types.bool; 91 92 description = lib.mdDoc '' 93 Whether to allow editing the kernel command-line before 94 boot. It is recommended to set this to false, as it allows 95 gaining root access by passing init=/bin/sh as a kernel 96 parameter. However, it is enabled by default for backwards 97 compatibility. 98 ''; 99 }; 100 101 configurationLimit = mkOption { 102 default = null; 103 example = 120; 104 type = types.nullOr types.int; 105 description = lib.mdDoc '' 106 Maximum number of latest generations in the boot menu. 107 Useful to prevent boot partition running out of disk space. 108 109 `null` means no limit i.e. all generations 110 that were not garbage collected yet. 111 ''; 112 }; 113 114 extraInstallCommands = mkOption { 115 default = ""; 116 example = '' 117 default_cfg=$(cat /boot/loader/loader.conf | grep default | awk '{print $2}') 118 init_value=$(cat /boot/loader/entries/$default_cfg | grep init= | awk '{print $2}') 119 sed -i "s|@INIT@|$init_value|g" /boot/custom/config_with_placeholder.conf 120 ''; 121 type = types.lines; 122 description = lib.mdDoc '' 123 Additional shell commands inserted in the bootloader installer 124 script after generating menu entries. It can be used to expand 125 on extra boot entries that cannot incorporate certain pieces of 126 information (such as the resulting `init=` kernel parameter). 127 ''; 128 }; 129 130 consoleMode = mkOption { 131 default = "keep"; 132 133 type = types.enum [ "0" "1" "2" "auto" "max" "keep" ]; 134 135 description = lib.mdDoc '' 136 The resolution of the console. The following values are valid: 137 138 - `"0"`: Standard UEFI 80x25 mode 139 - `"1"`: 80x50 mode, not supported by all devices 140 - `"2"`: The first non-standard mode provided by the device firmware, if any 141 - `"auto"`: Pick a suitable mode automatically using heuristics 142 - `"max"`: Pick the highest-numbered available mode 143 - `"keep"`: Keep the mode selected by firmware (the default) 144 ''; 145 }; 146 147 memtest86 = { 148 enable = mkOption { 149 default = false; 150 type = types.bool; 151 description = lib.mdDoc '' 152 Make Memtest86+ available from the systemd-boot menu. Memtest86+ is a 153 program for testing memory. 154 ''; 155 }; 156 157 entryFilename = mkOption { 158 default = "memtest86.conf"; 159 type = types.str; 160 description = lib.mdDoc '' 161 `systemd-boot` orders the menu entries by the config file names, 162 so if you want something to appear after all the NixOS entries, 163 it should start with {file}`o` or onwards. 164 ''; 165 }; 166 }; 167 168 netbootxyz = { 169 enable = mkOption { 170 default = false; 171 type = types.bool; 172 description = lib.mdDoc '' 173 Make `netboot.xyz` available from the 174 `systemd-boot` menu. `netboot.xyz` 175 is a menu system that allows you to boot OS installers and 176 utilities over the network. 177 ''; 178 }; 179 180 entryFilename = mkOption { 181 default = "o_netbootxyz.conf"; 182 type = types.str; 183 description = lib.mdDoc '' 184 `systemd-boot` orders the menu entries by the config file names, 185 so if you want something to appear after all the NixOS entries, 186 it should start with {file}`o` or onwards. 187 ''; 188 }; 189 }; 190 191 extraEntries = mkOption { 192 type = types.attrsOf types.lines; 193 default = {}; 194 example = literalExpression '' 195 { "memtest86.conf" = ''' 196 title Memtest86+ 197 efi /efi/memtest86/memtest.efi 198 '''; } 199 ''; 200 description = lib.mdDoc '' 201 Any additional entries you want added to the `systemd-boot` menu. 202 These entries will be copied to {file}`/boot/loader/entries`. 203 Each attribute name denotes the destination file name, 204 and the corresponding attribute value is the contents of the entry. 205 206 `systemd-boot` orders the menu entries by the config file names, 207 so if you want something to appear after all the NixOS entries, 208 it should start with {file}`o` or onwards. 209 ''; 210 }; 211 212 extraFiles = mkOption { 213 type = types.attrsOf types.path; 214 default = {}; 215 example = literalExpression '' 216 { "efi/memtest86/memtest.efi" = "''${pkgs.memtest86plus}/memtest.efi"; } 217 ''; 218 description = lib.mdDoc '' 219 A set of files to be copied to {file}`/boot`. 220 Each attribute name denotes the destination file name in 221 {file}`/boot`, while the corresponding 222 attribute value specifies the source file. 223 ''; 224 }; 225 226 graceful = mkOption { 227 default = false; 228 229 type = types.bool; 230 231 description = lib.mdDoc '' 232 Invoke `bootctl install` with the `--graceful` option, 233 which ignores errors when EFI variables cannot be written or when the EFI System Partition 234 cannot be found. Currently only applies to random seed operations. 235 236 Only enable this option if `systemd-boot` otherwise fails to install, as the 237 scope or implication of the `--graceful` option may change in the future. 238 ''; 239 }; 240 241 }; 242 243 config = mkIf cfg.enable { 244 assertions = [ 245 { 246 assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub; 247 message = "This kernel does not support the EFI boot stub"; 248 } 249 ] ++ concatMap (filename: [ 250 { 251 assertion = !(hasInfix "/" filename); 252 message = "boot.loader.systemd-boot.extraEntries.${lib.strings.escapeNixIdentifier filename} is invalid: entries within folders are not supported"; 253 } 254 { 255 assertion = hasSuffix ".conf" filename; 256 message = "boot.loader.systemd-boot.extraEntries.${lib.strings.escapeNixIdentifier filename} is invalid: entries must have a .conf file extension"; 257 } 258 ]) (builtins.attrNames cfg.extraEntries) 259 ++ concatMap (filename: [ 260 { 261 assertion = !(hasPrefix "/" filename); 262 message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: paths must not begin with a slash"; 263 } 264 { 265 assertion = !(hasInfix ".." filename); 266 message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: paths must not reference the parent directory"; 267 } 268 { 269 assertion = !(hasInfix "nixos/.extra-files" (toLower filename)); 270 message = "boot.loader.systemd-boot.extraFiles.${lib.strings.escapeNixIdentifier filename} is invalid: files cannot be placed in the nixos/.extra-files directory"; 271 } 272 ]) (builtins.attrNames cfg.extraFiles); 273 274 boot.loader.grub.enable = mkDefault false; 275 276 boot.loader.supportsInitrdSecrets = true; 277 278 boot.loader.systemd-boot.extraFiles = mkMerge [ 279 (mkIf cfg.memtest86.enable { 280 "efi/memtest86/memtest.efi" = "${pkgs.memtest86plus.efi}"; 281 }) 282 (mkIf cfg.netbootxyz.enable { 283 "efi/netbootxyz/netboot.xyz.efi" = "${pkgs.netbootxyz-efi}"; 284 }) 285 ]; 286 287 boot.loader.systemd-boot.extraEntries = mkMerge [ 288 (mkIf cfg.memtest86.enable { 289 "${cfg.memtest86.entryFilename}" = '' 290 title Memtest86+ 291 efi /efi/memtest86/memtest.efi 292 ''; 293 }) 294 (mkIf cfg.netbootxyz.enable { 295 "${cfg.netbootxyz.entryFilename}" = '' 296 title netboot.xyz 297 efi /efi/netbootxyz/netboot.xyz.efi 298 ''; 299 }) 300 ]; 301 302 system = { 303 build.installBootLoader = finalSystemdBootBuilder; 304 305 boot.loader.id = "systemd-boot"; 306 307 requiredKernelConfig = with config.lib.kernelConfig; [ 308 (isYes "EFI_STUB") 309 ]; 310 }; 311 }; 312}