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}