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}