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