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