Add target parameter to grub installation chain

This commit is contained in:
Thomas Strobel 2015-01-14 10:30:57 +01:00
parent 95632fdbf5
commit 3767370866
4 changed files with 163 additions and 12 deletions

View file

@ -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}";

View file

@ -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;
}

View file

@ -7,12 +7,18 @@
with stdenv.lib;
let
pcSystems = {
"i686-linux".target = "i386";
"x86_64-linux".target = "i386";
};
efiSystems = {
"i686-linux".target = "i386";
"x86_64-linux".target = "x86_64";
};
canEfi = any (system: stdenv.system == system) (mapAttrsToList (name: _: name) efiSystems);
inPCSystems = any (system: stdenv.system == system) (mapAttrsToList (name: _: name) pcSystems);
prefix = "grub${if efiSupport then "-efi" else ""}${optionalString zfsSupport "-zfs"}";
@ -82,6 +88,13 @@ stdenv.mkDerivation rec {
configureFlags = optional zfsSupport "--enable-libzfs"
++ optionals efiSupport [ "--with-platform=efi" "--target=${efiSystems.${stdenv.system}.target}" "--program-prefix=" ];
# save target that grub is compiled for
grubTarget = if efiSupport
then "${efiSystems.${stdenv.system}.target}-efi"
else if inPCSystems
then "${pcSystems.${stdenv.system}.target}-pc"
else "";
doCheck = false;
enableParallelBuilding = true;

View file

@ -227,6 +227,14 @@ let self = _self // overrides; _self = with self; {
};
};
ListCompare = buildPerlPackage {
name = "List-Compare-1.18";
src = fetchurl {
url = mirror://cpan/authors/id/J/JK/JKEENAN/List-Compare-0.39.tar.gz;
sha256 = "1v4gn176faanzf1kr9axdp1220da7nkvz0d66mnk34nd0skjjxcl";
};
};
ArchiveCpio = buildPerlPackage {
name = "Archive-Cpio-0.09";
src = fetchurl {