Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for booting as Xen dom0 on EFI systems #60485

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
143 changes: 111 additions & 32 deletions nixos/modules/system/boot/loader/grub/install-grub.pl
Expand Up @@ -229,7 +229,7 @@ sub GrubFs {
}
my $grubBoot = GrubFs($bootPath);
my $grubStore;
if ($copyKernels == 0) {
unless ($copyKernels) {
$grubStore = GrubFs($storePath);
}
my $extraInitrdPath;
Expand All @@ -255,7 +255,7 @@ sub GrubFs {
}

else {
if ($copyKernels == 0) {
unless ($copyKernels) {
$conf .= "
" . $grubStore->search;
}
Expand Down Expand Up @@ -339,67 +339,143 @@ sub GrubFs {
my %copied;
mkpath("$bootPath/kernels", 0, 0755) if $copyKernels;

sub copyToKernelsDir {
my ($path) = @_;
return $grubStore->path . substr($path, length("/nix/store")) unless $copyKernels;
$path =~ /\/nix\/store\/(.*)/ or die;
my $name = $1; $name =~ s/\//-/g;
my $dst = "$bootPath/kernels/$name";
my $efiTarget = getEfiTarget();

sub prefixGrubDevPath {
my ($subpath) = @_;
return ($grubBoot->path eq "/" ? "/" : $grubBoot->path) . $subpath;
}

sub atomicCopy {
my ($src, $dst) = @_;
# Don't copy the file if $dst already exists. This means that we
# have to create $dst atomically to prevent partially copied
# kernels or initrd if this script is ever interrupted.
if (! -e $dst) {
my $tmp = "$dst.tmp";
copy $path, $tmp or die "cannot copy $path to $tmp\n";
copy $src, $tmp or die "cannot copy $src to $tmp\n";
rename $tmp, $dst or die "cannot rename $tmp to $dst\n";
}
}

sub copyStoreFileToBootLocation {
my ($storePath, $subpath, $dstName) = @_;
$storePath =~ /\/nix\/store\/(.*)/ or die;
my $dst = "$bootPath/$subpath/$dstName";
atomicCopy($storePath, $dst);
return prefixGrubDevPath("$subpath/$dstName");
}

sub copyToKernelsDir {
my ($path) = @_;
return $grubStore->path . substr($path, length("/nix/store")) unless $copyKernels;
$path =~ /\/nix\/store\/(.*)/ or die;
my $name = $1; $name =~ s/\//-/g;
my $dst = "$bootPath/kernels/$name";
atomicCopy($path, $dst);
$copied{$dst} = 1;
return ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$name";
return prefixGrubDevPath("kernels/$name");
}

sub addEntry {
my ($name, $path) = @_;
return unless -e "$path/kernel" && -e "$path/initrd";
sub createXenEfiDirectory {
my ($path) = @_;

# Construct directory name from the profile/generation hash
my $systemPath = Cwd::abs_path($path); ($systemPath) = $systemPath =~ m/\/nix\/store\/(.*)/g; $systemPath =~ s/\//-/g;
my $systemHash = (split /-/, $systemPath)[0];
my $xenDirSubpath = "kernels/$systemHash-xenEfi";
my $xenDir = "$bootPath/$xenDirSubpath";

my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
if ($extraInitrd) {
$initrd .= " " .$extraInitrdPath->path;
# Ensure the directory exists
if (! -e $xenDir) {
mkdir $xenDir or die "cannot create Xen EFI directory $xenDir\n";
}
my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
$copied{$xenDir} = 1;

return $xenDirSubpath;
}

# FIXME: $confName
sub addEntry {
my ($name, $path) = @_;
return unless -e "$path/kernel" && -e "$path/initrd";

my $kernelParams =
"systemConfig=" . Cwd::abs_path($path) . " " .
"init=" . Cwd::abs_path("$path/init") . " " .
readFile("$path/kernel-params");
my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : "";

my $entry = undef;
unless (-e "$path/xen.gz" || -e "$path/xen.efi") {
# Construct the grub commands used to boot to Linux directly
my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));

$entry .= " " . ($grubVersion == 1 ? "kernel" : "linux") . " $kernel $kernelParams\n";
$entry .= " initrd $initrd\n";
} else {
# Construct the grub commands used to boot to Linux under Xen
my $xenParams = -e "$path/xen-params" ? readFile("$path/xen-params") : "";

if (($efiTarget eq "only" || $efiTarget eq "both") && -e "$path/xen.efi" && -e "$path/xen.gz") {
unless ($copyKernels) {
die "config.boot.loader.grub.copyKernels must be true in order to make use of Xen under EFI";
}

# Create a unique boot subdirectory to hold the Xen EFI binary,
# configuration file, kernel, and initrd
my $xenDirSubpath = createXenEfiDirectory($path);

# Copy xen.efi, xen.cfg, the kernel, and the initrd into place, with the correct names
my $xenEfi = copyStoreFileToBootLocation(Cwd::abs_path("$path/xen.efi"), $xenDirSubpath, "xen.efi");
my $xenCfg = copyStoreFileToBootLocation(Cwd::abs_path("$path/xen.cfg"), $xenDirSubpath, "xen.cfg");
my $kernel = copyStoreFileToBootLocation(Cwd::abs_path("$path/kernel"), $xenDirSubpath, "kernel");
my $initrd = copyStoreFileToBootLocation(Cwd::abs_path("$path/initrd"), $xenDirSubpath, "initrd");

if ($efiTarget eq "only") {
# EFI-only Xen booting
$entry .= " chainloader $xenEfi\n";
} else {
# BIOS + EFI Xen booting
my $xen = copyStoreFileToBootLocation(Cwd::abs_path("$path/xen.gz"), $xenDirSubpath, "xen.gz");

$entry .= " if [ \"\${grub_platform}\" = \"efi\" ]; then\n";
$entry .= " chainloader $xenEfi\n";
$entry .= " else\n";
$entry .= " multiboot $xen $xenParams\n";
$entry .= " module $kernel $kernelParams\n";
$entry .= " module $initrd\n";
$entry .= " fi\n";
}
} elsif (-e "$path/xen.gz") {
# BIOS-only Xen booting
my $xen = copyToKernelsDir(Cwd::abs_path("$path/xen.gz"));
my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
$entry .= " " . ($grubVersion == 1 ? "kernel" : "multiboot") . " $xen $xenParams\n";
$entry .= " module $kernel $kernelParams\n";
$entry .= " module $initrd\n";
}
}
# Build the full menu entry
if ($grubVersion == 1) {
$conf .= "title $name\n";
$conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig;
$conf .= " kernel $xen $xenParams\n" if $xen;
$conf .= " " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
$conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n\n";
$conf .= $entry . "\n";
} else {
$conf .= "menuentry \"$name\" {\n";
$conf .= $grubBoot->search . "\n";
if ($copyKernels == 0) {
unless ($copyKernels) {
$conf .= $grubStore->search . "\n";
}
if ($extraInitrd) {
$conf .= $extraInitrdPath->search . "\n";
}
$conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig;
$conf .= " multiboot $xen $xenParams\n" if $xen;
$conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
$conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n";
$conf .= $entry;
$conf .= "}\n\n";
}
}


# Add default entries.
$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS;

Expand Down Expand Up @@ -513,8 +589,6 @@ sub getEfiTarget {
}
}

my $efiTarget = getEfiTarget();

# Append entries detected by os-prober
if (get("useOSProber") eq "true") {
my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi;
Expand All @@ -528,8 +602,13 @@ sub getEfiTarget {
# Remove obsolete files from $bootPath/kernels.
foreach my $fn (glob "$bootPath/kernels/*") {
next if defined $copied{$fn};
print STDERR "removing obsolete file $fn\n";
unlink $fn;
if (-d $fn) {
print STDERR "removing obsolete folder $fn\n";
rmtree($fn);
} else {
print STDERR "removing obsolete file $fn\n";
unlink $fn;
}
}


Expand Down