at 23.11-beta 30 kB view raw
1{ config, options, lib, pkgs, ... }: 2 3let 4 inherit (lib) 5 all 6 concatMap 7 concatMapStrings 8 concatStrings 9 concatStringsSep 10 escapeShellArg 11 flip 12 foldr 13 forEach 14 hasPrefix 15 mapAttrsToList 16 literalExpression 17 makeBinPath 18 mkDefault 19 mkIf 20 mkMerge 21 mkOption 22 mkRemovedOptionModule 23 mkRenamedOptionModule 24 optional 25 optionals 26 optionalString 27 replaceStrings 28 types 29 ; 30 31 cfg = config.boot.loader.grub; 32 33 efi = config.boot.loader.efi; 34 35 grubPkgs = 36 # Package set of targeted architecture 37 if cfg.forcei686 then pkgs.pkgsi686Linux else pkgs; 38 39 realGrub = if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; } 40 else grubPkgs.grub2; 41 42 grub = 43 # Don't include GRUB if we're only generating a GRUB menu (e.g., 44 # in EC2 instances). 45 if cfg.devices == ["nodev"] 46 then null 47 else realGrub; 48 49 grubEfi = 50 if cfg.efiSupport 51 then realGrub.override { efiSupport = cfg.efiSupport; } 52 else null; 53 54 f = x: optionalString (x != null) ("" + x); 55 56 grubConfig = args: 57 let 58 efiSysMountPoint = if args.efiSysMountPoint == null then args.path else args.efiSysMountPoint; 59 efiSysMountPoint' = replaceStrings [ "/" ] [ "-" ] efiSysMountPoint; 60 in 61 pkgs.writeText "grub-config.xml" (builtins.toXML 62 { splashImage = f cfg.splashImage; 63 splashMode = f cfg.splashMode; 64 backgroundColor = f cfg.backgroundColor; 65 entryOptions = f cfg.entryOptions; 66 subEntryOptions = f cfg.subEntryOptions; 67 # PC platforms (like x86_64-linux) have a non-EFI target (`grubTarget`), but other platforms 68 # (like aarch64-linux) have an undefined `grubTarget`. Avoid providing the path to a non-EFI 69 # GRUB on those platforms. 70 grub = f (if (grub.grubTarget or "") != "" then grub else ""); 71 grubTarget = f (grub.grubTarget or ""); 72 shell = "${pkgs.runtimeShell}"; 73 fullName = lib.getName realGrub; 74 fullVersion = lib.getVersion realGrub; 75 grubEfi = f grubEfi; 76 grubTargetEfi = optionalString cfg.efiSupport (f (grubEfi.grubTarget or "")); 77 bootPath = args.path; 78 storePath = config.boot.loader.grub.storePath; 79 bootloaderId = if args.efiBootloaderId == null then "${config.system.nixos.distroName}${efiSysMountPoint'}" else args.efiBootloaderId; 80 timeout = if config.boot.loader.timeout == null then -1 else config.boot.loader.timeout; 81 theme = f cfg.theme; 82 inherit efiSysMountPoint; 83 inherit (args) devices; 84 inherit (efi) canTouchEfiVariables; 85 inherit (cfg) 86 extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber 87 extraGrubInstallArgs 88 extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels 89 default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios 90 users 91 timeoutStyle 92 ; 93 path = with pkgs; makeBinPath ( 94 [ coreutils gnused gnugrep findutils diffutils btrfs-progs util-linux mdadm ] 95 ++ optional cfg.efiSupport efibootmgr 96 ++ optionals cfg.useOSProber [ busybox os-prober ]); 97 font = lib.optionalString (cfg.font != null) ( 98 if lib.last (lib.splitString "." cfg.font) == "pf2" 99 then cfg.font 100 else "${convertedFont}"); 101 }); 102 103 bootDeviceCounters = foldr (device: attr: attr // { ${device} = (attr.${device} or 0) + 1; }) {} 104 (concatMap (args: args.devices) cfg.mirroredBoots); 105 106 convertedFont = (pkgs.runCommand "grub-font-converted.pf2" {} 107 (builtins.concatStringsSep " " 108 ([ "${realGrub}/bin/grub-mkfont" 109 cfg.font 110 "--output" "$out" 111 ] ++ (optional (cfg.fontSize!=null) "--size ${toString cfg.fontSize}"))) 112 ); 113 114 defaultSplash = pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader.gnomeFilePath; 115in 116 117{ 118 119 ###### interface 120 121 options = { 122 123 boot.loader.grub = { 124 125 enable = mkOption { 126 default = !config.boot.isContainer; 127 defaultText = literalExpression "!config.boot.isContainer"; 128 type = types.bool; 129 description = lib.mdDoc '' 130 Whether to enable the GNU GRUB boot loader. 131 ''; 132 }; 133 134 version = mkOption { 135 visible = false; 136 type = types.int; 137 }; 138 139 device = mkOption { 140 default = ""; 141 example = "/dev/disk/by-id/wwn-0x500001234567890a"; 142 type = types.str; 143 description = lib.mdDoc '' 144 The device on which the GRUB boot loader will be installed. 145 The special value `nodev` means that a GRUB 146 boot menu will be generated, but GRUB itself will not 147 actually be installed. To install GRUB on multiple devices, 148 use `boot.loader.grub.devices`. 149 ''; 150 }; 151 152 devices = mkOption { 153 default = []; 154 example = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; 155 type = types.listOf types.str; 156 description = lib.mdDoc '' 157 The devices on which the boot loader, GRUB, will be 158 installed. Can be used instead of `device` to 159 install GRUB onto multiple devices. 160 ''; 161 }; 162 163 users = mkOption { 164 default = {}; 165 example = { 166 root = { hashedPasswordFile = "/path/to/file"; }; 167 }; 168 description = lib.mdDoc '' 169 User accounts for GRUB. When specified, the GRUB command line and 170 all boot options except the default are password-protected. 171 All passwords and hashes provided will be stored in /boot/grub/grub.cfg, 172 and will be visible to any local user who can read this file. Additionally, 173 any passwords and hashes provided directly in a Nix configuration 174 (as opposed to external files) will be copied into the Nix store, and 175 will be visible to all local users. 176 ''; 177 type = types.attrsOf (types.submodule { 178 options = { 179 hashedPasswordFile = mkOption { 180 example = "/path/to/file"; 181 default = null; 182 type = with types; uniq (nullOr str); 183 description = lib.mdDoc '' 184 Specifies the path to a file containing the password hash 185 for the account, generated with grub-mkpasswd-pbkdf2. 186 This hash will be stored in /boot/grub/grub.cfg, and will 187 be visible to any local user who can read this file. 188 ''; 189 }; 190 hashedPassword = mkOption { 191 example = "grub.pbkdf2.sha512.10000.674DFFDEF76E13EA...2CC972B102CF4355"; 192 default = null; 193 type = with types; uniq (nullOr str); 194 description = lib.mdDoc '' 195 Specifies the password hash for the account, 196 generated with grub-mkpasswd-pbkdf2. 197 This hash will be copied to the Nix store, and will be visible to all local users. 198 ''; 199 }; 200 passwordFile = mkOption { 201 example = "/path/to/file"; 202 default = null; 203 type = with types; uniq (nullOr str); 204 description = lib.mdDoc '' 205 Specifies the path to a file containing the 206 clear text password for the account. 207 This password will be stored in /boot/grub/grub.cfg, and will 208 be visible to any local user who can read this file. 209 ''; 210 }; 211 password = mkOption { 212 example = "Pa$$w0rd!"; 213 default = null; 214 type = with types; uniq (nullOr str); 215 description = lib.mdDoc '' 216 Specifies the clear text password for the account. 217 This password will be copied to the Nix store, and will be visible to all local users. 218 ''; 219 }; 220 }; 221 }); 222 }; 223 224 mirroredBoots = mkOption { 225 default = [ ]; 226 example = [ 227 { path = "/boot1"; devices = [ "/dev/disk/by-id/wwn-0x500001234567890a" ]; } 228 { path = "/boot2"; devices = [ "/dev/disk/by-id/wwn-0x500009876543210a" ]; } 229 ]; 230 description = lib.mdDoc '' 231 Mirror the boot configuration to multiple partitions and install grub 232 to the respective devices corresponding to those partitions. 233 ''; 234 235 type = with types; listOf (submodule { 236 options = { 237 238 path = mkOption { 239 example = "/boot1"; 240 type = types.str; 241 description = lib.mdDoc '' 242 The path to the boot directory where GRUB will be written. Generally 243 this boot path should double as an EFI path. 244 ''; 245 }; 246 247 efiSysMountPoint = mkOption { 248 default = null; 249 example = "/boot1/efi"; 250 type = types.nullOr types.str; 251 description = lib.mdDoc '' 252 The path to the efi system mount point. Usually this is the same 253 partition as the above path and can be left as null. 254 ''; 255 }; 256 257 efiBootloaderId = mkOption { 258 default = null; 259 example = "NixOS-fsid"; 260 type = types.nullOr types.str; 261 description = lib.mdDoc '' 262 The id of the bootloader to store in efi nvram. 263 The default is to name it NixOS and append the path or efiSysMountPoint. 264 This is only used if `boot.loader.efi.canTouchEfiVariables` is true. 265 ''; 266 }; 267 268 devices = mkOption { 269 default = [ ]; 270 example = [ "/dev/disk/by-id/wwn-0x500001234567890a" "/dev/disk/by-id/wwn-0x500009876543210a" ]; 271 type = types.listOf types.str; 272 description = lib.mdDoc '' 273 The path to the devices which will have the GRUB MBR written. 274 Note these are typically device paths and not paths to partitions. 275 ''; 276 }; 277 278 }; 279 }); 280 }; 281 282 configurationName = mkOption { 283 default = ""; 284 example = "Stable 2.6.21"; 285 type = types.str; 286 description = lib.mdDoc '' 287 GRUB entry name instead of default. 288 ''; 289 }; 290 291 storePath = mkOption { 292 default = "/nix/store"; 293 type = types.str; 294 description = lib.mdDoc '' 295 Path to the Nix store when looking for kernels at boot. 296 Only makes sense when copyKernels is false. 297 ''; 298 }; 299 300 extraPrepareConfig = mkOption { 301 default = ""; 302 type = types.lines; 303 description = lib.mdDoc '' 304 Additional bash commands to be run at the script that 305 prepares the GRUB menu entries. 306 ''; 307 }; 308 309 extraConfig = mkOption { 310 default = ""; 311 example = '' 312 serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 313 terminal_input --append serial 314 terminal_output --append serial 315 ''; 316 type = types.lines; 317 description = lib.mdDoc '' 318 Additional GRUB commands inserted in the configuration file 319 just before the menu entries. 320 ''; 321 }; 322 323 extraGrubInstallArgs = mkOption { 324 default = [ ]; 325 example = [ "--modules=nativedisk ahci pata part_gpt part_msdos diskfilter mdraid1x lvm ext2" ]; 326 type = types.listOf types.str; 327 description = lib.mdDoc '' 328 Additional arguments passed to `grub-install`. 329 330 A use case for this is to build specific GRUB2 modules 331 directly into the GRUB2 kernel image, so that they are available 332 and activated even in the `grub rescue` shell. 333 334 They are also necessary when the BIOS/UEFI is bugged and cannot 335 correctly read large disks (e.g. above 2 TB), so GRUB2's own 336 `nativedisk` and related modules can be used 337 to use its own disk drivers. The example shows one such case. 338 This is also useful for booting from USB. 339 See the 340 [ 341 GRUB source code 342 ](https://git.savannah.gnu.org/cgit/grub.git/tree/grub-core/commands/nativedisk.c?h=grub-2.04#n326) 343 for which disk modules are available. 344 345 The list elements are passed directly as `argv` 346 arguments to the `grub-install` program, in order. 347 ''; 348 }; 349 350 extraInstallCommands = mkOption { 351 default = ""; 352 example = '' 353 # the example below generates detached signatures that GRUB can verify 354 # https://www.gnu.org/software/grub/manual/grub/grub.html#Using-digital-signatures 355 ''${pkgs.findutils}/bin/find /boot -not -path "/boot/efi/*" -type f -name '*.sig' -delete 356 old_gpg_home=$GNUPGHOME 357 export GNUPGHOME="$(mktemp -d)" 358 ''${pkgs.gnupg}/bin/gpg --import ''${priv_key} > /dev/null 2>&1 359 ''${pkgs.findutils}/bin/find /boot -not -path "/boot/efi/*" -type f -exec ''${pkgs.gnupg}/bin/gpg --detach-sign "{}" \; > /dev/null 2>&1 360 rm -rf $GNUPGHOME 361 export GNUPGHOME=$old_gpg_home 362 ''; 363 type = types.lines; 364 description = lib.mdDoc '' 365 Additional shell commands inserted in the bootloader installer 366 script after generating menu entries. 367 ''; 368 }; 369 370 extraPerEntryConfig = mkOption { 371 default = ""; 372 example = "root (hd0)"; 373 type = types.lines; 374 description = lib.mdDoc '' 375 Additional GRUB commands inserted in the configuration file 376 at the start of each NixOS menu entry. 377 ''; 378 }; 379 380 extraEntries = mkOption { 381 default = ""; 382 type = types.lines; 383 example = '' 384 # GRUB 2 example 385 menuentry "Windows 7" { 386 chainloader (hd0,4)+1 387 } 388 389 # GRUB 2 with UEFI example, chainloading another distro 390 menuentry "Fedora" { 391 set root=(hd1,1) 392 chainloader /efi/fedora/grubx64.efi 393 } 394 ''; 395 description = lib.mdDoc '' 396 Any additional entries you want added to the GRUB boot menu. 397 ''; 398 }; 399 400 extraEntriesBeforeNixOS = mkOption { 401 default = false; 402 type = types.bool; 403 description = lib.mdDoc '' 404 Whether extraEntries are included before the default option. 405 ''; 406 }; 407 408 extraFiles = mkOption { 409 type = types.attrsOf types.path; 410 default = {}; 411 example = literalExpression '' 412 { "memtest.bin" = "''${pkgs.memtest86plus}/memtest.bin"; } 413 ''; 414 description = lib.mdDoc '' 415 A set of files to be copied to {file}`/boot`. 416 Each attribute name denotes the destination file name in 417 {file}`/boot`, while the corresponding 418 attribute value specifies the source file. 419 ''; 420 }; 421 422 useOSProber = mkOption { 423 default = false; 424 type = types.bool; 425 description = lib.mdDoc '' 426 If set to true, append entries for other OSs detected by os-prober. 427 ''; 428 }; 429 430 splashImage = mkOption { 431 type = types.nullOr types.path; 432 example = literalExpression "./my-background.png"; 433 description = lib.mdDoc '' 434 Background image used for GRUB. 435 Set to `null` to run GRUB in text mode. 436 437 ::: {.note} 438 File must be one of .png, .tga, .jpg, or .jpeg. JPEG images must 439 not be progressive. 440 The image will be scaled if necessary to fit the screen. 441 ::: 442 ''; 443 }; 444 445 backgroundColor = mkOption { 446 type = types.nullOr types.str; 447 example = "#7EBAE4"; 448 default = null; 449 description = lib.mdDoc '' 450 Background color to be used for GRUB to fill the areas the image isn't filling. 451 ''; 452 }; 453 454 timeoutStyle = mkOption { 455 default = "menu"; 456 type = types.enum [ "menu" "countdown" "hidden" ]; 457 description = lib.mdDoc '' 458 - `menu` shows the menu. 459 - `countdown` uses a text-mode countdown. 460 - `hidden` hides GRUB entirely. 461 462 When using a theme, the default value (`menu`) is appropriate for the graphical countdown. 463 464 When attempting to do flicker-free boot, `hidden` should be used. 465 466 See the [GRUB documentation section about `timeout_style`](https://www.gnu.org/software/grub/manual/grub/html_node/timeout.html). 467 468 ::: {.note} 469 If this option is set to countdown or hidden [...] and ESC or F4 are pressed, or SHIFT is held down during that time, it will display the menu and wait for input. 470 ::: 471 472 From: [Simple configuration handling page, under GRUB_TIMEOUT_STYLE](https://www.gnu.org/software/grub/manual/grub/html_node/Simple-configuration.html). 473 ''; 474 }; 475 476 entryOptions = mkOption { 477 default = "--class nixos --unrestricted"; 478 type = types.nullOr types.str; 479 description = lib.mdDoc '' 480 Options applied to the primary NixOS menu entry. 481 ''; 482 }; 483 484 subEntryOptions = mkOption { 485 default = "--class nixos"; 486 type = types.nullOr types.str; 487 description = lib.mdDoc '' 488 Options applied to the secondary NixOS submenu entry. 489 ''; 490 }; 491 492 theme = mkOption { 493 type = types.nullOr types.path; 494 example = literalExpression "pkgs.nixos-grub2-theme"; 495 default = null; 496 description = lib.mdDoc '' 497 Grub theme to be used. 498 ''; 499 }; 500 501 splashMode = mkOption { 502 type = types.enum [ "normal" "stretch" ]; 503 default = "stretch"; 504 description = lib.mdDoc '' 505 Whether to stretch the image or show the image in the top-left corner unstretched. 506 ''; 507 }; 508 509 font = mkOption { 510 type = types.nullOr types.path; 511 default = "${realGrub}/share/grub/unicode.pf2"; 512 defaultText = literalExpression ''"''${pkgs.grub2}/share/grub/unicode.pf2"''; 513 description = lib.mdDoc '' 514 Path to a TrueType, OpenType, or pf2 font to be used by Grub. 515 ''; 516 }; 517 518 fontSize = mkOption { 519 type = types.nullOr types.int; 520 example = 16; 521 default = null; 522 description = lib.mdDoc '' 523 Font size for the grub menu. Ignored unless `font` 524 is set to a ttf or otf font. 525 ''; 526 }; 527 528 gfxmodeEfi = mkOption { 529 default = "auto"; 530 example = "1024x768"; 531 type = types.str; 532 description = lib.mdDoc '' 533 The gfxmode to pass to GRUB when loading a graphical boot interface under EFI. 534 ''; 535 }; 536 537 gfxmodeBios = mkOption { 538 default = "1024x768"; 539 example = "auto"; 540 type = types.str; 541 description = lib.mdDoc '' 542 The gfxmode to pass to GRUB when loading a graphical boot interface under BIOS. 543 ''; 544 }; 545 546 gfxpayloadEfi = mkOption { 547 default = "keep"; 548 example = "text"; 549 type = types.str; 550 description = lib.mdDoc '' 551 The gfxpayload to pass to GRUB when loading a graphical boot interface under EFI. 552 ''; 553 }; 554 555 gfxpayloadBios = mkOption { 556 default = "text"; 557 example = "keep"; 558 type = types.str; 559 description = lib.mdDoc '' 560 The gfxpayload to pass to GRUB when loading a graphical boot interface under BIOS. 561 ''; 562 }; 563 564 configurationLimit = mkOption { 565 default = 100; 566 example = 120; 567 type = types.int; 568 description = lib.mdDoc '' 569 Maximum of configurations in boot menu. GRUB has problems when 570 there are too many entries. 571 ''; 572 }; 573 574 copyKernels = mkOption { 575 default = false; 576 type = types.bool; 577 description = lib.mdDoc '' 578 Whether the GRUB menu builder should copy kernels and initial 579 ramdisks to /boot. This is done automatically if /boot is 580 on a different partition than /. 581 ''; 582 }; 583 584 default = mkOption { 585 default = "0"; 586 type = types.either types.int types.str; 587 apply = toString; 588 description = lib.mdDoc '' 589 Index of the default menu item to be booted. 590 Can also be set to "saved", which will make GRUB select 591 the menu item that was used at the last boot. 592 ''; 593 }; 594 595 fsIdentifier = mkOption { 596 default = "uuid"; 597 type = types.enum [ "uuid" "label" "provided" ]; 598 description = lib.mdDoc '' 599 Determines how GRUB will identify devices when generating the 600 configuration file. A value of uuid / label signifies that grub 601 will always resolve the uuid or label of the device before using 602 it in the configuration. A value of provided means that GRUB will 603 use the device name as show in {command}`df` or 604 {command}`mount`. Note, zfs zpools / datasets are ignored 605 and will always be mounted using their labels. 606 ''; 607 }; 608 609 zfsSupport = mkOption { 610 default = false; 611 type = types.bool; 612 description = lib.mdDoc '' 613 Whether GRUB should be built against libzfs. 614 ''; 615 }; 616 617 efiSupport = mkOption { 618 default = false; 619 type = types.bool; 620 description = lib.mdDoc '' 621 Whether GRUB should be built with EFI support. 622 ''; 623 }; 624 625 efiInstallAsRemovable = mkOption { 626 default = false; 627 type = types.bool; 628 description = lib.mdDoc '' 629 Whether to invoke `grub-install` with 630 `--removable`. 631 632 Unless you turn this on, GRUB will install itself somewhere in 633 `boot.loader.efi.efiSysMountPoint` (exactly where 634 depends on other config variables). If you've set 635 `boot.loader.efi.canTouchEfiVariables` *AND* you 636 are currently booted in UEFI mode, then GRUB will use 637 `efibootmgr` to modify the boot order in the 638 EFI variables of your firmware to include this location. If you are 639 *not* booted in UEFI mode at the time GRUB is being installed, the 640 NVRAM will not be modified, and your system will not find GRUB at 641 boot time. However, GRUB will still return success so you may miss 642 the warning that gets printed ("`efibootmgr: EFI variables 643 are not supported on this system.`"). 644 645 If you turn this feature on, GRUB will install itself in a 646 special location within `efiSysMountPoint` (namely 647 `EFI/boot/boot$arch.efi`) which the firmwares 648 are hardcoded to try first, regardless of NVRAM EFI variables. 649 650 To summarize, turn this on if: 651 - You are installing NixOS and want it to boot in UEFI mode, 652 but you are currently booted in legacy mode 653 - You want to make a drive that will boot regardless of 654 the NVRAM state of the computer (like a USB "removable" drive) 655 - You simply dislike the idea of depending on NVRAM 656 state to make your drive bootable 657 ''; 658 }; 659 660 enableCryptodisk = mkOption { 661 default = false; 662 type = types.bool; 663 description = lib.mdDoc '' 664 Enable support for encrypted partitions. GRUB should automatically 665 unlock the correct encrypted partition and look for filesystems. 666 ''; 667 }; 668 669 forceInstall = mkOption { 670 default = false; 671 type = types.bool; 672 description = lib.mdDoc '' 673 Whether to try and forcibly install GRUB even if problems are 674 detected. It is not recommended to enable this unless you know what 675 you are doing. 676 ''; 677 }; 678 679 forcei686 = mkOption { 680 default = false; 681 type = types.bool; 682 description = lib.mdDoc '' 683 Whether to force the use of a ia32 boot loader on x64 systems. Required 684 to install and run NixOS on 64bit x86 systems with 32bit (U)EFI. 685 ''; 686 }; 687 688 }; 689 690 }; 691 692 693 ###### implementation 694 695 config = mkMerge [ 696 697 { boot.loader.grub.splashImage = mkDefault defaultSplash; } 698 699 (mkIf (cfg.splashImage == defaultSplash) { 700 boot.loader.grub.backgroundColor = mkDefault "#2F302F"; 701 boot.loader.grub.splashMode = mkDefault "normal"; 702 }) 703 704 (mkIf cfg.enable { 705 706 boot.loader.grub.devices = optional (cfg.device != "") cfg.device; 707 708 boot.loader.grub.mirroredBoots = optionals (cfg.devices != [ ]) [ 709 { path = "/boot"; inherit (cfg) devices; inherit (efi) efiSysMountPoint; } 710 ]; 711 712 boot.loader.supportsInitrdSecrets = true; 713 714 system.systemBuilderArgs.configurationName = cfg.configurationName; 715 system.systemBuilderCommands = '' 716 echo -n "$configurationName" > $out/configuration-name 717 ''; 718 719 system.build.installBootLoader = 720 let 721 install-grub-pl = pkgs.substituteAll { 722 src = ./install-grub.pl; 723 utillinux = pkgs.util-linux; 724 btrfsprogs = pkgs.btrfs-progs; 725 inherit (config.system.nixos) distroName; 726 }; 727 perl = pkgs.perl.withPackages (p: with p; [ 728 FileSlurp FileCopyRecursive 729 XMLLibXML XMLSAX XMLSAXBase 730 ListCompare JSON 731 ]); 732 in pkgs.writeScript "install-grub.sh" ('' 733 #!${pkgs.runtimeShell} 734 set -e 735 ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"} 736 '' + flip concatMapStrings cfg.mirroredBoots (args: '' 737 ${perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@ 738 '') + cfg.extraInstallCommands); 739 740 system.build.grub = grub; 741 742 # Common attribute for boot loaders so only one of them can be 743 # set at once. 744 system.boot.loader.id = "grub"; 745 746 environment.systemPackages = optional (grub != null) grub; 747 748 boot.loader.grub.extraPrepareConfig = 749 concatStrings (mapAttrsToList (n: v: '' 750 ${pkgs.coreutils}/bin/install -Dp "${v}" "${efi.efiSysMountPoint}/"${escapeShellArg n} 751 '') config.boot.loader.grub.extraFiles); 752 753 assertions = [ 754 { 755 assertion = cfg.mirroredBoots != [ ]; 756 message = "You must set the option boot.loader.grub.devices or " 757 + "'boot.loader.grub.mirroredBoots' to make the system bootable."; 758 } 759 { 760 assertion = cfg.efiSupport || all (c: c < 2) (mapAttrsToList (n: c: if n == "nodev" then 0 else c) bootDeviceCounters); 761 message = "You cannot have duplicated devices in mirroredBoots"; 762 } 763 { 764 assertion = cfg.efiInstallAsRemovable -> cfg.efiSupport; 765 message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn on boot.loader.grub.efiSupport"; 766 } 767 { 768 assertion = cfg.efiInstallAsRemovable -> !config.boot.loader.efi.canTouchEfiVariables; 769 message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn off boot.loader.efi.canTouchEfiVariables"; 770 } 771 { 772 assertion = !(options.boot.loader.grub.version.isDefined && cfg.version == 1); 773 message = "Support for version 0.9x of GRUB was removed after being unsupported upstream for around a decade"; 774 } 775 ] ++ flip concatMap cfg.mirroredBoots (args: [ 776 { 777 assertion = args.devices != [ ]; 778 message = "A boot path cannot have an empty devices string in ${args.path}"; 779 } 780 { 781 assertion = hasPrefix "/" args.path; 782 message = "Boot paths must be absolute, not ${args.path}"; 783 } 784 { 785 assertion = if args.efiSysMountPoint == null then true else hasPrefix "/" args.efiSysMountPoint; 786 message = "EFI paths must be absolute, not ${args.efiSysMountPoint}"; 787 } 788 ] ++ forEach args.devices (device: { 789 assertion = device == "nodev" || hasPrefix "/" device; 790 message = "GRUB devices must be absolute paths, not ${device} in ${args.path}"; 791 })); 792 }) 793 794 (mkIf options.boot.loader.grub.version.isDefined { 795 warnings = [ '' 796 The boot.loader.grub.version option does not have any effect anymore, please remove it from your configuration. 797 '' ]; 798 }) 799 ]; 800 801 802 imports = 803 [ (mkRemovedOptionModule [ "boot" "loader" "grub" "bootDevice" ] "") 804 (mkRenamedOptionModule [ "boot" "copyKernels" ] [ "boot" "loader" "grub" "copyKernels" ]) 805 (mkRenamedOptionModule [ "boot" "extraGrubEntries" ] [ "boot" "loader" "grub" "extraEntries" ]) 806 (mkRenamedOptionModule [ "boot" "extraGrubEntriesBeforeNixos" ] [ "boot" "loader" "grub" "extraEntriesBeforeNixOS" ]) 807 (mkRenamedOptionModule [ "boot" "grubDevice" ] [ "boot" "loader" "grub" "device" ]) 808 (mkRenamedOptionModule [ "boot" "bootMount" ] [ "boot" "loader" "grub" "bootDevice" ]) 809 (mkRenamedOptionModule [ "boot" "grubSplashImage" ] [ "boot" "loader" "grub" "splashImage" ]) 810 (mkRemovedOptionModule [ "boot" "loader" "grub" "trustedBoot" ] '' 811 Support for Trusted GRUB has been removed, because the project 812 has been retired upstream. 813 '') 814 (mkRemovedOptionModule [ "boot" "loader" "grub" "extraInitrd" ] '' 815 This option has been replaced with the bootloader agnostic 816 boot.initrd.secrets option. To migrate to the initrd secrets system, 817 extract the extraInitrd archive into your main filesystem: 818 819 # zcat /boot/extra_initramfs.gz | cpio -idvmD /etc/secrets/initrd 820 /path/to/secret1 821 /path/to/secret2 822 823 then replace boot.loader.grub.extraInitrd with boot.initrd.secrets: 824 825 boot.initrd.secrets = { 826 "/path/to/secret1" = "/etc/secrets/initrd/path/to/secret1"; 827 "/path/to/secret2" = "/etc/secrets/initrd/path/to/secret2"; 828 }; 829 830 See the boot.initrd.secrets option documentation for more information. 831 '') 832 ]; 833 834}