Merge pull request #5994 from ts468/grub

Add 'target' parameter for GRUB installation chain

+163 -12
+29 -5
nixos/modules/system/boot/loader/grub/grub.nix
··· 6 7 cfg = config.boot.loader.grub; 8 9 realGrub = if cfg.version == 1 then pkgs.grub 10 else pkgs.grub2.override { zfsSupport = cfg.zfsSupport; }; 11 ··· 16 then null 17 else realGrub; 18 19 f = x: if x == null then "" else "" + x; 20 21 grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML 22 { splashImage = f config.boot.loader.grub.splashImage; 23 grub = f grub; 24 shell = "${pkgs.stdenv.shell}"; 25 fullVersion = (builtins.parseDrvName realGrub.name).version; 26 inherit (cfg) 27 version extraConfig extraPerEntryConfig extraEntries 28 extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout 29 - default devices fsIdentifier; 30 - path = (makeSearchPath "bin" [ 31 pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfsProgs 32 - pkgs.utillinux 33 - ]) + ":" + (makeSearchPath "sbin" [ 34 pkgs.mdadm pkgs.utillinux 35 ]); 36 }); ··· 231 type = types.bool; 232 description = '' 233 Whether grub should be build against libzfs. 234 ''; 235 }; 236 ··· 269 if cfg.devices == [] then 270 throw "You must set the option ‘boot.loader.grub.device’ to make the system bootable." 271 else 272 - "PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ])} " + 273 (if cfg.enableCryptodisk then "GRUB_ENABLE_CRYPTODISK=y " else "") + 274 "${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}"; 275
··· 6 7 cfg = config.boot.loader.grub; 8 9 + efi = config.boot.loader.efi; 10 + 11 realGrub = if cfg.version == 1 then pkgs.grub 12 else pkgs.grub2.override { zfsSupport = cfg.zfsSupport; }; 13 ··· 18 then null 19 else realGrub; 20 21 + grubEfi = 22 + # EFI version of Grub v2 23 + if (cfg.devices != ["nodev"]) && cfg.efiSupport && (cfg.version == 2) 24 + then pkgs.grub2.override { zfsSupport = cfg.zfsSupport; efiSupport = cfg.efiSupport; } 25 + else null; 26 + 27 f = x: if x == null then "" else "" + x; 28 29 grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML 30 { splashImage = f config.boot.loader.grub.splashImage; 31 grub = f grub; 32 + grubTarget = f grub.grubTarget; 33 shell = "${pkgs.stdenv.shell}"; 34 fullVersion = (builtins.parseDrvName realGrub.name).version; 35 + grubEfi = f grubEfi; 36 + grubTargetEfi = if cfg.efiSupport && (cfg.version == 2) then f grubEfi.grubTarget else ""; 37 + inherit (efi) efiSysMountPoint canTouchEfiVariables; 38 inherit (cfg) 39 version extraConfig extraPerEntryConfig extraEntries 40 extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout 41 + default devices fsIdentifier efiSupport; 42 + path = (makeSearchPath "bin" ([ 43 pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfsProgs 44 + pkgs.utillinux ] ++ (if cfg.efiSupport && (cfg.version == 2) then [pkgs.efibootmgr ] else []) 45 + )) + ":" + (makeSearchPath "sbin" [ 46 pkgs.mdadm pkgs.utillinux 47 ]); 48 }); ··· 243 type = types.bool; 244 description = '' 245 Whether grub should be build against libzfs. 246 + ZFS support is only available for GRUB v2. 247 + This option is ignored for GRUB v1. 248 + ''; 249 + }; 250 + 251 + efiSupport = mkOption { 252 + default = false; 253 + type = types.bool; 254 + description = '' 255 + Whether grub should be build with EFI support. 256 + EFI support is only available for GRUB v2. 257 + This option is ignored for GRUB v1. 258 ''; 259 }; 260 ··· 293 if cfg.devices == [] then 294 throw "You must set the option ‘boot.loader.grub.device’ to make the system bootable." 295 else 296 + "PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])} " + 297 (if cfg.enableCryptodisk then "GRUB_ENABLE_CRYPTODISK=y " else "") + 298 "${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}"; 299
+113 -7
nixos/modules/system/boot/loader/grub/install-grub.pl
··· 7 use File::stat; 8 use File::Copy; 9 use File::Slurp; 10 use POSIX; 11 use Cwd; 12 ··· 39 40 my $grub = get("grub"); 41 my $grubVersion = int(get("version")); 42 my $extraConfig = get("extraConfig"); 43 my $extraPrepareConfig = get("extraPrepareConfig"); 44 my $extraPerEntryConfig = get("extraPerEntryConfig"); ··· 50 my $timeout = int(get("timeout")); 51 my $defaultEntry = int(get("default")); 52 my $fsIdentifier = get("fsIdentifier"); 53 $ENV{'PATH'} = get("path"); 54 55 die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2; ··· 103 104 # Skip the read-only bind-mount on /nix/store. 105 next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions) && (grep { $_ eq "ro" } @mountOptions); 106 107 # Ensure this matches the intended directory 108 next unless PathInMount($dir, $mountPoint); ··· 402 } 403 404 405 - # Install GRUB if the version changed from the last time we installed 406 - # it. FIXME: shouldn't we reinstall if ‘devices’ changed? 407 - my $prevVersion = readFile("/boot/grub/version") // ""; 408 - if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1" || get("fullVersion") ne $prevVersion) { 409 foreach my $dev ($dom->findnodes('/expr/attrs/attr[@name = "devices"]/list/string/@value')) { 410 $dev = $dev->findvalue(".") or die; 411 next if $dev eq "nodev"; 412 print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n"; 413 - system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0 414 - or die "$0: installation of GRUB on $dev failed\n"; 415 } 416 - writeFile("/boot/grub/version", get("fullVersion")); 417 }
··· 7 use File::stat; 8 use File::Copy; 9 use File::Slurp; 10 + require List::Compare; 11 use POSIX; 12 use Cwd; 13 ··· 40 41 my $grub = get("grub"); 42 my $grubVersion = int(get("version")); 43 + my $grubTarget = get("grubTarget"); 44 my $extraConfig = get("extraConfig"); 45 my $extraPrepareConfig = get("extraPrepareConfig"); 46 my $extraPerEntryConfig = get("extraPerEntryConfig"); ··· 52 my $timeout = int(get("timeout")); 53 my $defaultEntry = int(get("default")); 54 my $fsIdentifier = get("fsIdentifier"); 55 + my $grubEfi = get("grubEfi"); 56 + my $grubTargetEfi = get("grubTargetEfi"); 57 + my $canTouchEfiVariables = get("canTouchEfiVariables"); 58 + my $efiSysMountPoint = get("efiSysMountPoint"); 59 $ENV{'PATH'} = get("path"); 60 61 die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2; ··· 109 110 # Skip the read-only bind-mount on /nix/store. 111 next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions) && (grep { $_ eq "ro" } @mountOptions); 112 + # Skip mount point generated by systemd-efi-boot-generator? 113 + next if $fsType eq "autofs"; 114 115 # Ensure this matches the intended directory 116 next unless PathInMount($dir, $mountPoint); ··· 410 } 411 412 413 + # 414 + # Install GRUB if the parameters changed from the last time we installed it. 415 + # 416 + 417 + struct(GrubState => { 418 + version => '$', 419 + efi => '$', 420 + devices => '$', 421 + efiMountPoint => '$', 422 + }); 423 + sub readGrubState { 424 + my $defaultGrubState = GrubState->new(version => "", efi => "", devices => "", efiMountPoint => "" ); 425 + open FILE, "</boot/grub/state" or return $defaultGrubState; 426 + local $/ = "\n"; 427 + my $version = <FILE>; 428 + chomp($version); 429 + my $efi = <FILE>; 430 + chomp($efi); 431 + my $devices = <FILE>; 432 + chomp($devices); 433 + my $efiMountPoint = <FILE>; 434 + chomp($efiMountPoint); 435 + close FILE; 436 + my $grubState = GrubState->new(version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint ); 437 + return $grubState 438 + } 439 + 440 + sub getDeviceTargets { 441 + my @devices = (); 442 foreach my $dev ($dom->findnodes('/expr/attrs/attr[@name = "devices"]/list/string/@value')) { 443 $dev = $dev->findvalue(".") or die; 444 + push(@devices, $dev); 445 + } 446 + return @devices; 447 + } 448 + 449 + # check whether to install GRUB EFI or not 450 + sub getEfiTarget { 451 + if (($grub ne "") && ($grubEfi ne "")) { 452 + # EFI can only be installed when target is set; 453 + # A target is also required then for non-EFI grub 454 + if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die } 455 + else { return "both" } 456 + } elsif (($grub ne "") && ($grubEfi eq "")) { 457 + # TODO: It would be safer to disallow non-EFI grub installation if no taget is given. 458 + # If no target is given, then grub auto-detects the target which can lead to errors. 459 + # E.g. it seems as if grub would auto-detect a EFI target based on the availability 460 + # of a EFI partition. 461 + # However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386 462 + # architectures in NixOS. That would have to be fixed in the nixos modules first. 463 + return "no" 464 + } elsif (($grub eq "") && ($grubEfi ne "")) { 465 + # EFI can only be installed when target is set; 466 + if ($grubTargetEfi eq "") { die } 467 + else {return "only" } 468 + } else { 469 + # at least one grub target has to be given 470 + die 471 + } 472 + } 473 + 474 + my @deviceTargets = getDeviceTargets(); 475 + my $efiTarget = getEfiTarget(); 476 + my $prevGrubState = readGrubState(); 477 + my @prevDeviceTargets = split/:/, $prevGrubState->devices; 478 + 479 + my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference() ); 480 + my $versionDiffer = (get("fullVersion") eq \$prevGrubState->version); 481 + my $efiDiffer = ($efiTarget eq \$prevGrubState->efi); 482 + my $efiMountPointDiffer = ($efiSysMountPoint eq \$prevGrubState->efiMountPoint); 483 + my $requireNewInstall = $devicesDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1"); 484 + 485 + 486 + # install non-EFI GRUB 487 + if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) { 488 + foreach my $dev (@deviceTargets) { 489 next if $dev eq "nodev"; 490 print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n"; 491 + if ($grubTarget eq "") { 492 + system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0 493 + or die "$0: installation of GRUB on $dev failed\n"; 494 + } else { 495 + system("$grub/sbin/grub-install", "--recheck", "--target=$grubTarget", Cwd::abs_path($dev)) == 0 496 + or die "$0: installation of GRUB on $dev failed\n"; 497 + } 498 } 499 + } 500 + 501 + 502 + # install EFI GRUB 503 + if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) { 504 + print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n"; 505 + if ($canTouchEfiVariables eq "true") { 506 + system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--efi-directory=$efiSysMountPoint") == 0 507 + or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n"; 508 + } else { 509 + system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--efi-directory=$efiSysMountPoint", "--no-nvram") == 0 510 + or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n"; 511 + } 512 + } 513 + 514 + 515 + # update GRUB state file 516 + if ($requireNewInstall != 0) { 517 + open FILE, ">/boot/grub/state" or die "cannot create /boot/grub/state: $!\n"; 518 + print FILE get("fullVersion"), "\n" or die; 519 + print FILE $efiTarget, "\n" or die; 520 + print FILE join( ":", @deviceTargets ), "\n" or die; 521 + print FILE $efiSysMountPoint, "\n" or die; 522 + close FILE or die; 523 }
+13
pkgs/tools/misc/grub/2.0x.nix
··· 7 8 with stdenv.lib; 9 let 10 efiSystems = { 11 "i686-linux".target = "i386"; 12 "x86_64-linux".target = "x86_64"; 13 }; 14 15 canEfi = any (system: stdenv.system == system) (mapAttrsToList (name: _: name) efiSystems); 16 17 version = "2.02-git-1de3a4"; 18 ··· 79 80 configureFlags = optional zfsSupport "--enable-libzfs" 81 ++ optionals efiSupport [ "--with-platform=efi" "--target=${efiSystems.${stdenv.system}.target}" "--program-prefix=" ]; 82 83 doCheck = false; 84 enableParallelBuilding = true;
··· 7 8 with stdenv.lib; 9 let 10 + pcSystems = { 11 + "i686-linux".target = "i386"; 12 + "x86_64-linux".target = "i386"; 13 + }; 14 + 15 efiSystems = { 16 "i686-linux".target = "i386"; 17 "x86_64-linux".target = "x86_64"; 18 }; 19 20 canEfi = any (system: stdenv.system == system) (mapAttrsToList (name: _: name) efiSystems); 21 + inPCSystems = any (system: stdenv.system == system) (mapAttrsToList (name: _: name) pcSystems); 22 23 version = "2.02-git-1de3a4"; 24 ··· 85 86 configureFlags = optional zfsSupport "--enable-libzfs" 87 ++ optionals efiSupport [ "--with-platform=efi" "--target=${efiSystems.${stdenv.system}.target}" "--program-prefix=" ]; 88 + 89 + # save target that grub is compiled for 90 + grubTarget = if efiSupport 91 + then "${efiSystems.${stdenv.system}.target}-efi" 92 + else if inPCSystems 93 + then "${pcSystems.${stdenv.system}.target}-pc" 94 + else ""; 95 96 doCheck = false; 97 enableParallelBuilding = true;
+8
pkgs/top-level/perl-packages.nix
··· 227 }; 228 }; 229 230 ArchiveCpio = buildPerlPackage { 231 name = "Archive-Cpio-0.09"; 232 src = fetchurl {
··· 227 }; 228 }; 229 230 + ListCompare = buildPerlPackage { 231 + name = "List-Compare-1.18"; 232 + src = fetchurl { 233 + url = mirror://cpan/authors/id/J/JK/JKEENAN/List-Compare-0.39.tar.gz; 234 + sha256 = "1v4gn176faanzf1kr9axdp1220da7nkvz0d66mnk34nd0skjjxcl"; 235 + }; 236 + }; 237 + 238 ArchiveCpio = buildPerlPackage { 239 name = "Archive-Cpio-0.09"; 240 src = fetchurl {