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