From 5554b79c2ded72025f6b07cfe7bbaf4ec6e928b6 Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Thu, 17 Sep 2026 07:38:47 -0300 Subject: [PATCH] fix(xcat-core): the initrd firmware step reads the root image only The step that fills lib/firmware in the Ubuntu netboot initrd asked modinfo about $rootimg_dir/$module and looked a firmware name up under lib/firmware. The copy step beside it takes a module from $customdir or $pathtofiles first, and the kernel looks a firmware name up under updates/, updates/, / and lib/firmware. So a custom driver reached the initrd with no firmware, even when the root image carried it, and a firmware override was left out of the initrd altogether. initrd_firmware_files now takes the module directories the copy step searches and the kernel release. It resolves each module in that order before asking modinfo, and keeps every firmware file that exists in the four directories the kernel searches, so the override still wins on the node. ubuntu_genimage_initrd_firmware.t covers both: its two new cases are red on the commit before this one. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .../share/xcat/netboot/ubuntu/genimage | 44 +++++++++++++++---- .../unit/ubuntu_genimage_initrd_firmware.t | 3 ++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/xCAT-server/share/xcat/netboot/ubuntu/genimage b/xCAT-server/share/xcat/netboot/ubuntu/genimage index 9fe0803b4..970a97d59 100755 --- a/xCAT-server/share/xcat/netboot/ubuntu/genimage +++ b/xCAT-server/share/xcat/netboot/ubuntu/genimage @@ -1829,7 +1829,9 @@ EOMS # 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)) { + 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"); } @@ -1904,13 +1906,16 @@ sub isnetdriver { 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) = @_; + my ($rootimg_dir, $filestoadd, $kernelver, @module_dirs) = @_; return () unless (-d "$rootimg_dir/lib/firmware"); @@ -1918,9 +1923,18 @@ sub initrd_firmware_files { foreach my $entry (@$filestoadd) { my $module = ref($entry) ? $entry->[0] : $entry; next unless ($module =~ /\.ko(?:\.(?:gz|xz|zst))?$/); - next unless (-f "$rootimg_dir/$module"); - my $out = `modinfo -F firmware "$rootimg_dir/$module" 2>/dev/null`; + # 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; @@ -1931,14 +1945,26 @@ sub initrd_firmware_files { } } + # 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/$candidate"); - push @files, $candidate; - last; + # 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; diff --git a/xCAT-test/unit/ubuntu_genimage_initrd_firmware.t b/xCAT-test/unit/ubuntu_genimage_initrd_firmware.t index 0d162dd68..d01242385 100755 --- a/xCAT-test/unit/ubuntu_genimage_initrd_firmware.t +++ b/xCAT-test/unit/ubuntu_genimage_initrd_firmware.t @@ -103,6 +103,9 @@ close($custom); use File::Copy; use File::Path qw(mkpath); our $rootimg_dir; + our $customdir; + our $pathtofiles; + our $kernelver; our @filestoadd; sub xdie { die @_ } }