at 23.11-pre 11 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 python3 = pkgs.python3.withPackages (ps: [ ps.packaging ]); 11 12 systemdBootBuilder = pkgs.substituteAll { 13 src = ./systemd-boot-builder.py; 14 15 isExecutable = true; 16 17 inherit python3; 18 19 systemd = config.systemd.package; 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.memtest86-efi; 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 python3 ]; 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 imports = 73 [ (mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ]) 74 ]; 75 76 options.boot.loader.systemd-boot = { 77 enable = mkOption { 78 default = false; 79 80 type = types.bool; 81 82 description = lib.mdDoc "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager"; 83 }; 84 85 editor = mkOption { 86 default = true; 87 88 type = types.bool; 89 90 description = lib.mdDoc '' 91 Whether to allow editing the kernel command-line before 92 boot. It is recommended to set this to false, as it allows 93 gaining root access by passing init=/bin/sh as a kernel 94 parameter. However, it is enabled by default for backwards 95 compatibility. 96 ''; 97 }; 98 99 configurationLimit = mkOption { 100 default = null; 101 example = 120; 102 type = types.nullOr types.int; 103 description = lib.mdDoc '' 104 Maximum number of latest generations in the boot menu. 105 Useful to prevent boot partition running out of disk space. 106 107 `null` means no limit i.e. all generations 108 that were not garbage collected yet. 109 ''; 110 }; 111 112 extraInstallCommands = mkOption { 113 default = ""; 114 example = '' 115 default_cfg=$(cat /boot/loader/loader.conf | grep default | awk '{print $2}') 116 init_value=$(cat /boot/loader/entries/$default_cfg | grep init= | awk '{print $2}') 117 sed -i "s|@INIT@|$init_value|g" /boot/custom/config_with_placeholder.conf 118 ''; 119 type = types.lines; 120 description = lib.mdDoc '' 121 Additional shell commands inserted in the bootloader installer 122 script after generating menu entries. It can be used to expand 123 on extra boot entries that cannot incorporate certain pieces of 124 information (such as the resulting `init=` kernel parameter). 125 ''; 126 }; 127 128 consoleMode = mkOption { 129 default = "keep"; 130 131 type = types.enum [ "0" "1" "2" "auto" "max" "keep" ]; 132 133 description = lib.mdDoc '' 134 The resolution of the console. The following values are valid: 135 136 - `"0"`: Standard UEFI 80x25 mode 137 - `"1"`: 80x50 mode, not supported by all devices 138 - `"2"`: The first non-standard mode provided by the device firmware, if any 139 - `"auto"`: Pick a suitable mode automatically using heuristics 140 - `"max"`: Pick the highest-numbered available mode 141 - `"keep"`: Keep the mode selected by firmware (the default) 142 ''; 143 }; 144 145 memtest86 = { 146 enable = mkOption { 147 default = false; 148 type = types.bool; 149 description = lib.mdDoc '' 150 Make MemTest86 available from the systemd-boot menu. MemTest86 is a 151 program for testing memory. MemTest86 is an unfree program, so 152 this requires `allowUnfree` to be set to 153 `true`. 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/memtest86.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/memtest86.efi" = "''${pkgs.memtest86-efi}/BOOTX64.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 # TODO: This is hard-coded to use the 64-bit EFI app, but it could probably 280 # be updated to use the 32-bit EFI app on 32-bit systems. The 32-bit EFI 281 # app filename is BOOTIA32.efi. 282 (mkIf cfg.memtest86.enable { 283 "efi/memtest86/BOOTX64.efi" = "${pkgs.memtest86-efi}/BOOTX64.efi"; 284 }) 285 (mkIf cfg.netbootxyz.enable { 286 "efi/netbootxyz/netboot.xyz.efi" = "${pkgs.netbootxyz-efi}"; 287 }) 288 ]; 289 290 boot.loader.systemd-boot.extraEntries = mkMerge [ 291 (mkIf cfg.memtest86.enable { 292 "${cfg.memtest86.entryFilename}" = '' 293 title MemTest86 294 efi /efi/memtest86/BOOTX64.efi 295 ''; 296 }) 297 (mkIf cfg.netbootxyz.enable { 298 "${cfg.netbootxyz.entryFilename}" = '' 299 title netboot.xyz 300 efi /efi/netbootxyz/netboot.xyz.efi 301 ''; 302 }) 303 ]; 304 305 system = { 306 build.installBootLoader = finalSystemdBootBuilder; 307 308 boot.loader.id = "systemd-boot"; 309 310 requiredKernelConfig = with config.lib.kernelConfig; [ 311 (isYes "EFI_STUB") 312 ]; 313 }; 314 }; 315}