summary refs log tree commit diff
path: root/nixos/modules/system/boot/loader
diff options
context:
space:
mode:
authorWilliam A. Kennington III <william@wkennington.com>2015-02-08 14:18:13 -0800
committerWilliam A. Kennington III <william@wkennington.com>2015-02-08 14:18:13 -0800
commit2a0754ccbc0e2c4f01803ab1824930c6276a331a (patch)
treeea07080349d97672b625b3f0d660324b203baad2 /nixos/modules/system/boot/loader
parentac99fefae1e9300a24bbe76ba2cf6f5d390b6a20 (diff)
parent37673708661542797d4ad6de2c74e3ce6863b2cb (diff)
downloadnixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.tar
nixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.tar.gz
nixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.tar.bz2
nixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.tar.lz
nixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.tar.xz
nixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.tar.zst
nixlib-2a0754ccbc0e2c4f01803ab1824930c6276a331a.zip
Merge pull request #5994 from ts468/grub
Add 'target' parameter for GRUB installation chain
Diffstat (limited to 'nixos/modules/system/boot/loader')
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix34
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl120
2 files changed, 142 insertions, 12 deletions
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index b16a725aba8e..ed2249a4ba7a 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -6,6 +6,8 @@ let
 
   cfg = config.boot.loader.grub;
 
+  efi = config.boot.loader.efi;
+
   realGrub = if cfg.version == 1 then pkgs.grub
     else pkgs.grub2.override { zfsSupport = cfg.zfsSupport; };
 
@@ -16,21 +18,31 @@ let
     then null
     else realGrub;
 
+  grubEfi =
+    # EFI version of Grub v2
+    if (cfg.devices != ["nodev"]) && cfg.efiSupport && (cfg.version == 2)
+    then pkgs.grub2.override { zfsSupport = cfg.zfsSupport; efiSupport = cfg.efiSupport; }
+    else null;
+
   f = x: if x == null then "" else "" + x;
 
   grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML
     { splashImage = f config.boot.loader.grub.splashImage;
       grub = f grub;
+      grubTarget = f grub.grubTarget;
       shell = "${pkgs.stdenv.shell}";
       fullVersion = (builtins.parseDrvName realGrub.name).version;
+      grubEfi = f grubEfi;
+      grubTargetEfi = if cfg.efiSupport && (cfg.version == 2) then f grubEfi.grubTarget else "";
+      inherit (efi) efiSysMountPoint canTouchEfiVariables;
       inherit (cfg)
         version extraConfig extraPerEntryConfig extraEntries
         extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout
-        default devices fsIdentifier;
-      path = (makeSearchPath "bin" [
+        default devices fsIdentifier efiSupport;
+      path = (makeSearchPath "bin" ([
         pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfsProgs
-        pkgs.utillinux
-      ]) + ":" + (makeSearchPath "sbin" [
+        pkgs.utillinux ] ++ (if cfg.efiSupport && (cfg.version == 2) then [pkgs.efibootmgr ] else [])
+      )) + ":" + (makeSearchPath "sbin" [
         pkgs.mdadm pkgs.utillinux
       ]);
     });
@@ -231,6 +243,18 @@ in
         type = types.bool;
         description = ''
           Whether grub should be build against libzfs.
+          ZFS support is only available for GRUB v2.
+          This option is ignored for GRUB v1.
+        '';
+      };
+
+      efiSupport = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether grub should be build with EFI support.
+          EFI support is only available for GRUB v2.
+          This option is ignored for GRUB v1.
         '';
       };
 
@@ -269,7 +293,7 @@ in
         if cfg.devices == [] then
           throw "You must set the option ‘boot.loader.grub.device’ to make the system bootable."
         else
-          "PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ])} " +
+          "PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])} " +
           (if cfg.enableCryptodisk then "GRUB_ENABLE_CRYPTODISK=y " else "") +
           "${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}";
 
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index ffee0271e93b..74a934c67931 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -7,6 +7,7 @@ use File::Path;
 use File::stat;
 use File::Copy;
 use File::Slurp;
+require List::Compare;
 use POSIX;
 use Cwd;
 
@@ -39,6 +40,7 @@ sub runCommand {
 
 my $grub = get("grub");
 my $grubVersion = int(get("version"));
+my $grubTarget = get("grubTarget");
 my $extraConfig = get("extraConfig");
 my $extraPrepareConfig = get("extraPrepareConfig");
 my $extraPerEntryConfig = get("extraPerEntryConfig");
@@ -50,6 +52,10 @@ my $copyKernels = get("copyKernels") eq "true";
 my $timeout = int(get("timeout"));
 my $defaultEntry = int(get("default"));
 my $fsIdentifier = get("fsIdentifier");
+my $grubEfi = get("grubEfi");
+my $grubTargetEfi = get("grubTargetEfi");
+my $canTouchEfiVariables = get("canTouchEfiVariables");
+my $efiSysMountPoint = get("efiSysMountPoint");
 $ENV{'PATH'} = get("path");
 
 die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
@@ -103,6 +109,8 @@ sub GetFs {
 
         # Skip the read-only bind-mount on /nix/store.
         next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions) && (grep { $_ eq "ro" } @mountOptions);
+        # Skip mount point generated by systemd-efi-boot-generator?
+        next if $fsType eq "autofs";
 
         # Ensure this matches the intended directory
         next unless PathInMount($dir, $mountPoint);
@@ -402,16 +410,114 @@ foreach my $fn (glob "/boot/kernels/*") {
 }
 
 
-# Install GRUB if the version changed from the last time we installed
-# it.  FIXME: shouldn't we reinstall if ‘devices’ changed?
-my $prevVersion = readFile("/boot/grub/version") // "";
-if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1" || get("fullVersion") ne $prevVersion) {
+#
+# Install GRUB if the parameters changed from the last time we installed it.
+#
+
+struct(GrubState => {
+    version => '$',
+    efi => '$',
+    devices => '$',
+    efiMountPoint => '$',
+});
+sub readGrubState {
+    my $defaultGrubState = GrubState->new(version => "", efi => "", devices => "", efiMountPoint => "" );
+    open FILE, "</boot/grub/state" or return $defaultGrubState;
+    local $/ = "\n";
+    my $version = <FILE>;
+    chomp($version);
+    my $efi = <FILE>;
+    chomp($efi);
+    my $devices = <FILE>;
+    chomp($devices);
+    my $efiMountPoint = <FILE>;
+    chomp($efiMountPoint);
+    close FILE;
+    my $grubState = GrubState->new(version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint );
+    return $grubState
+}
+
+sub getDeviceTargets {
+    my @devices = ();
     foreach my $dev ($dom->findnodes('/expr/attrs/attr[@name = "devices"]/list/string/@value')) {
         $dev = $dev->findvalue(".") or die;
+        push(@devices, $dev);
+    }
+    return @devices;
+}
+
+# check whether to install GRUB EFI or not
+sub getEfiTarget {
+    if (($grub ne "") && ($grubEfi ne "")) {
+        # EFI can only be installed when target is set;
+        # A target is also required then for non-EFI grub
+        if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die }
+        else { return "both" }
+    } elsif (($grub ne "") && ($grubEfi eq "")) {
+        # TODO: It would be safer to disallow non-EFI grub installation if no taget is given.
+        #       If no target is given, then grub auto-detects the target which can lead to errors.
+        #       E.g. it seems as if grub would auto-detect a EFI target based on the availability
+        #       of a EFI partition.
+        #       However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386
+        #       architectures in NixOS. That would have to be fixed in the nixos modules first.
+        return "no"
+    } elsif (($grub eq "") && ($grubEfi ne "")) {
+        # EFI can only be installed when target is set;
+        if ($grubTargetEfi eq "") { die }
+        else {return "only" }
+    } else {
+        # at least one grub target has to be given
+        die
+    }
+}
+
+my @deviceTargets = getDeviceTargets();
+my $efiTarget = getEfiTarget();
+my $prevGrubState = readGrubState();
+my @prevDeviceTargets = split/:/, $prevGrubState->devices;
+
+my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference() );
+my $versionDiffer = (get("fullVersion") eq \$prevGrubState->version);
+my $efiDiffer = ($efiTarget eq \$prevGrubState->efi);
+my $efiMountPointDiffer = ($efiSysMountPoint eq \$prevGrubState->efiMountPoint);
+my $requireNewInstall = $devicesDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1");
+
+
+# install non-EFI GRUB
+if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
+    foreach my $dev (@deviceTargets) {
         next if $dev eq "nodev";
         print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
-        system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0
-            or die "$0: installation of GRUB on $dev failed\n";
+        if ($grubTarget eq "") {
+            system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0
+                or die "$0: installation of GRUB on $dev failed\n";
+        } else {
+            system("$grub/sbin/grub-install", "--recheck", "--target=$grubTarget", Cwd::abs_path($dev)) == 0
+                or die "$0: installation of GRUB on $dev failed\n";
+        }
     }
-    writeFile("/boot/grub/version", get("fullVersion"));
+}
+
+
+# install EFI GRUB
+if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) {
+    print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n";
+    if ($canTouchEfiVariables eq "true") {
+        system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--efi-directory=$efiSysMountPoint") == 0
+                or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
+    } else {
+        system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--efi-directory=$efiSysMountPoint", "--no-nvram") == 0
+                or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
+    }
+}
+
+
+# update GRUB state file
+if ($requireNewInstall != 0) {
+    open FILE, ">/boot/grub/state" or die "cannot create /boot/grub/state: $!\n";
+    print FILE get("fullVersion"), "\n" or die;
+    print FILE $efiTarget, "\n" or die;
+    print FILE join( ":", @deviceTargets ), "\n" or die;
+    print FILE $efiSysMountPoint, "\n" or die;
+    close FILE or die;
 }