mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-24 16:54:03 +00:00
Merge pull request #7843 from VersatusHPC/fix/ubuntu-netboot-initrd-firmware
fix(xcat-core): grub2 cannot load the Ubuntu 26.04 ppc64el netboot initrd
This commit is contained in:
@@ -1827,8 +1827,13 @@ EOMS
|
||||
}
|
||||
}
|
||||
|
||||
if (-d "$rootimg_dir/lib/firmware/") {
|
||||
system("cp -r $rootimg_dir/lib/firmware/* /tmp/xcatinitrd.$$/lib/firmware");
|
||||
# The whole firmware tree is 666 MB on Ubuntu 26.04, and grub2 on a pseries node cannot
|
||||
# load an initrd of that size. The initrd only has to reach the network.
|
||||
foreach my $firmware (
|
||||
initrd_firmware_files($rootimg_dir, \@filestoadd, $kernelver, $customdir, $pathtofiles))
|
||||
{
|
||||
copy_initrd_file("$rootimg_dir/lib/firmware/$firmware",
|
||||
"/tmp/xcatinitrd.$$/lib/firmware/$firmware");
|
||||
}
|
||||
|
||||
if (-d "$rootimg_dir/lib/modules/$kernelver/") {
|
||||
@@ -1892,6 +1897,79 @@ sub isnetdriver {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 initrd_firmware_files
|
||||
|
||||
Descriptions: Select the firmware that the drivers in the netboot initrd ask for.
|
||||
Arguments:
|
||||
rootimg_dir - the root image directory
|
||||
filestoadd - reference to the list of files that go into the initrd. A module
|
||||
is either a plain path or a [ source, destination ] pair.
|
||||
kernelver - the kernel release, which names one of the firmware directories
|
||||
module_dirs - directories searched for a module before the root image, in the
|
||||
same order the copy step searches them
|
||||
Returns: the firmware files that exist, as paths below lib/firmware
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub initrd_firmware_files {
|
||||
my ($rootimg_dir, $filestoadd, $kernelver, @module_dirs) = @_;
|
||||
|
||||
return () unless (-d "$rootimg_dir/lib/firmware");
|
||||
|
||||
my %wanted;
|
||||
foreach my $entry (@$filestoadd) {
|
||||
my $module = ref($entry) ? $entry->[0] : $entry;
|
||||
next unless ($module =~ /\.ko(?:\.(?:gz|xz|zst))?$/);
|
||||
|
||||
# Ask about the module the copy step takes, which is the custom one where there is
|
||||
# one. A custom driver is not in the root image at all.
|
||||
my $source;
|
||||
foreach my $dir (grep { defined && length } @module_dirs, $rootimg_dir) {
|
||||
next unless (-f "$dir/$module");
|
||||
$source = "$dir/$module";
|
||||
last;
|
||||
}
|
||||
next unless (defined $source);
|
||||
|
||||
my $out = `modinfo -F firmware "$source" 2>/dev/null`;
|
||||
if ($?) {
|
||||
print "Warning: cannot read the firmware of $module\n";
|
||||
next;
|
||||
}
|
||||
foreach my $name (split(/\n/, $out)) {
|
||||
$name =~ s/^\s+|\s+$//g;
|
||||
$wanted{$name} = 1 if (length $name);
|
||||
}
|
||||
}
|
||||
|
||||
# The kernel looks for a firmware name in these directories below lib/firmware, in this
|
||||
# order, and loads the first file it finds. Keep every one that exists, so an override
|
||||
# still wins on the node.
|
||||
my @dirs = ("updates", "");
|
||||
if (defined $kernelver and length $kernelver) {
|
||||
unshift @dirs, "updates/$kernelver";
|
||||
splice(@dirs, 2, 0, $kernelver);
|
||||
}
|
||||
|
||||
my @files;
|
||||
foreach my $name (sort keys %wanted) {
|
||||
foreach my $dir (@dirs) {
|
||||
my $prefix = length($dir) ? "$dir/" : "";
|
||||
|
||||
# Ubuntu keeps the firmware compressed and the kernel asks for the plain name.
|
||||
foreach my $candidate ($name, "$name.zst", "$name.xz", "$name.gz") {
|
||||
next unless (-e "$rootimg_dir/lib/firmware/$prefix$candidate");
|
||||
push @files, "$prefix$candidate";
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
return @files;
|
||||
}
|
||||
|
||||
sub find_rootimg_file {
|
||||
my ($file) = @_;
|
||||
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Path qw(make_path);
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# grub2 on a pseries node cannot load the netboot initrd when it carries the whole Ubuntu
|
||||
# firmware tree. Run genimage's firmware step over a root image that holds firmware no
|
||||
# driver in the initrd asks for, and read what the step put in the initrd.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs(File::Spec->catdir($FindBin::Bin, '..', '..'));
|
||||
my $genimage = File::Spec->catfile(
|
||||
$repo_root, 'xCAT-server', 'share', 'xcat', 'netboot', 'ubuntu', 'genimage');
|
||||
die "genimage not found at $genimage\n" unless -f $genimage;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $genimage or die $!; <$fh> };
|
||||
|
||||
# The step sits between the loop that copies the initrd files and the copy of the module
|
||||
# index. Both anchors hold whatever the firmware step itself looks like.
|
||||
my ($step) = $src =~ m{
|
||||
copy_initrd_file\(\$srcpath,\ "/tmp/xcatinitrd\.\$\$/\$_"\);\n
|
||||
\ {8}\}\n\ {4}\}\n
|
||||
(.*?)
|
||||
\n\ {4}if\ \(-d\ "\$rootimg_dir/lib/modules/\$kernelver/"\)\ \{
|
||||
}sx;
|
||||
die "genimage no longer has a firmware step between the initrd file loop and the module index\n"
|
||||
unless defined $step;
|
||||
|
||||
my ($copy_initrd_file) = $src =~ m{^(sub copy_initrd_file \{.*?\n\}\n)}ms;
|
||||
die "genimage no longer defines copy_initrd_file\n" unless defined $copy_initrd_file;
|
||||
|
||||
# The helper the step calls once the firmware copy is filtered. A genimage that copies the
|
||||
# tree whole has no such routine, and the step below then needs none.
|
||||
my ($helper) = $src =~ m{^(sub initrd_firmware_files \{.*?\n\}\n)}ms;
|
||||
|
||||
my $scratch = tempdir(CLEANUP => 1);
|
||||
my $rootimg = "$scratch/rootimg";
|
||||
my $initrd_dir = "$scratch/initrd";
|
||||
|
||||
my $rewritten = ($step =~ s{/tmp/xcatinitrd\.\$\$}{$initrd_dir}g);
|
||||
die "the firmware step no longer writes to /tmp/xcatinitrd.\$\$\n" unless $rewritten;
|
||||
|
||||
# modinfo reports the firmware of a module. Answer for the modules of this root image only.
|
||||
my $bin = "$scratch/bin";
|
||||
make_path($bin);
|
||||
open(my $fake, '>', "$bin/modinfo") or die $!;
|
||||
print $fake <<'SH';
|
||||
#!/bin/sh
|
||||
case "$*" in
|
||||
*mlx5_core*) echo mellanox/fw-a.mfa2 ;;
|
||||
*bnx2x*) echo bnx2x/bnx2x-e2.fw ;;
|
||||
*e1000e*) echo intel/absent-from-this-image.bin ;;
|
||||
*custom_nic*) echo custom/custom-nic.bin ;;
|
||||
*virtio_net*) : ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
SH
|
||||
close($fake);
|
||||
chmod 0755, "$bin/modinfo";
|
||||
$ENV{PATH} = "$bin:$ENV{PATH}";
|
||||
|
||||
foreach my $file (
|
||||
'lib/firmware/mellanox/fw-a.mfa2',
|
||||
'lib/firmware/updates/7.0.0/mellanox/fw-a.mfa2',
|
||||
'lib/firmware/custom/custom-nic.bin',
|
||||
'lib/firmware/bnx2x/bnx2x-e2.fw.zst',
|
||||
'lib/firmware/amdgpu/never-asked-for.bin',
|
||||
'lib/firmware/qcom/never-asked-for-either.bin',
|
||||
'lib/modules/7.0.0/kernel/drivers/net/virtio_net.ko',
|
||||
'lib/modules/7.0.0/kernel/drivers/net/mlx5_core.ko',
|
||||
'lib/modules/7.0.0/kernel/drivers/net/bnx2x.ko.zst',
|
||||
'lib/modules/7.0.0/kernel/drivers/net/e1000e.ko',
|
||||
'bin/busybox',
|
||||
)
|
||||
{
|
||||
my $full = "$rootimg/$file";
|
||||
($full =~ m{^(.*)/[^/]+$}) and make_path($1);
|
||||
open(my $fh, '>', $full) or die $!;
|
||||
print $fh "content of $file\n";
|
||||
close($fh);
|
||||
}
|
||||
make_path("$initrd_dir/lib/firmware");
|
||||
|
||||
# A driver the administrator put in the custom directory. genimage takes the module from there
|
||||
# and not from the root image, so that is the file its firmware has to be read from.
|
||||
my $customdir = "$scratch/custom";
|
||||
my $pathtofiles = "$scratch/pathtofiles";
|
||||
make_path("$customdir/lib/modules/7.0.0/kernel/drivers/net");
|
||||
make_path($pathtofiles);
|
||||
open(my $custom, '>', "$customdir/lib/modules/7.0.0/kernel/drivers/net/custom_nic.ko") or die $!;
|
||||
print $custom "content of a custom driver\n";
|
||||
close($custom);
|
||||
|
||||
{
|
||||
package Scratch;
|
||||
use strict;
|
||||
use warnings;
|
||||
use File::Basename;
|
||||
use File::Copy;
|
||||
use File::Path qw(mkpath);
|
||||
our $rootimg_dir;
|
||||
our $customdir;
|
||||
our $pathtofiles;
|
||||
our $kernelver;
|
||||
our @filestoadd;
|
||||
sub xdie { die @_ }
|
||||
}
|
||||
|
||||
## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
eval "package Scratch;\n$copy_initrd_file\n1" or die $@;
|
||||
if (defined $helper) {
|
||||
eval "package Scratch;\n$helper\n1" or die $@;
|
||||
}
|
||||
|
||||
$Scratch::rootimg_dir = $rootimg;
|
||||
$Scratch::customdir = $customdir;
|
||||
$Scratch::pathtofiles = $pathtofiles;
|
||||
$Scratch::kernelver = '7.0.0';
|
||||
@Scratch::filestoadd = (
|
||||
[ 'lib/modules/7.0.0/kernel/drivers/net/virtio_net.ko', 'lib/virtio_net.ko' ],
|
||||
[ 'lib/modules/7.0.0/kernel/drivers/net/mlx5_core.ko', 'lib/mlx5_core.ko' ],
|
||||
[ 'lib/modules/7.0.0/kernel/drivers/net/bnx2x.ko.zst', 'lib/bnx2x.ko' ],
|
||||
[ 'lib/modules/7.0.0/kernel/drivers/net/e1000e.ko', 'lib/e1000e.ko' ],
|
||||
[ 'lib/modules/7.0.0/kernel/drivers/net/custom_nic.ko', 'lib/custom_nic.ko' ],
|
||||
[ 'bin/busybox', 'bin/busybox' ],
|
||||
);
|
||||
eval "package Scratch;\nno strict 'vars';\n$step\n1" or die $@;
|
||||
|
||||
ok(-e "$initrd_dir/lib/firmware/mellanox/fw-a.mfa2",
|
||||
'the initrd keeps the firmware a driver in it asks for');
|
||||
ok(-e "$initrd_dir/lib/firmware/bnx2x/bnx2x-e2.fw.zst",
|
||||
'a compressed firmware file answers the plain name the driver asks for');
|
||||
ok(!-e "$initrd_dir/lib/firmware/amdgpu/never-asked-for.bin",
|
||||
'the initrd does not carry firmware no driver in it asks for');
|
||||
ok(!-e "$initrd_dir/lib/firmware/qcom/never-asked-for-either.bin",
|
||||
'the whole firmware tree does not reach the initrd');
|
||||
ok(!-e "$initrd_dir/lib/firmware/intel/absent-from-this-image.bin",
|
||||
'a firmware name the root image does not have is left out');
|
||||
ok(-e "$initrd_dir/lib/firmware/custom/custom-nic.bin",
|
||||
'the firmware of a driver taken from the custom directory reaches the initrd');
|
||||
ok(-e "$initrd_dir/lib/firmware/updates/7.0.0/mellanox/fw-a.mfa2",
|
||||
'a firmware override under updates/<kernel> reaches the initrd');
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user