#1080340 initramfs-tools does not use include firmware named in the devicetree

#1080340#5
Date:
2024-09-02 13:47:17 UTC
From:
To:
Dear Maintainer,

Currently initramfs-tools relies on modinfo to identify firmware that must
be included in the initramfs. This is incomplete on systems where the name
of the firmware to load is provided by the devicetree rather than being
hardcoded and recorded in the MODULE_FIRMWARE() macro.

As a concrete example, recent Windows-on-Snapdragon laptops (Thinkpad X13s for
example) require firmware for the remoteproc drivers. If this firmware is not
available when the driver comes up then significant functionality (battery
status, external display, etc) is disabled. The combination of my MODULES=most
setup (plus encrypted rootfs and a few explicitly named modules) is exactly
that.

I disliked the idea that having a richer initramfs (including the remoteproc
drivers) can kills functionality so rather than try and blocklist things I
instead added an initramfs hook to scan the current devicetree and include any
firmware that might be needed.

~~~
#!/bin/sh -e

# Copyright (C) Linaro Ltd, 2024
# SPDX-License-Identifier: GPL-2.0-or-later

# Add firmware whose names are dictated by the devicetree. Such firmware cannot
# be described in a MODULE_FIRMWARE() meaning modinfo based discovery tools are
# insufficient to handle these cases.

# No prereqs
if [ "$1" = "prereqs" ]; then exit 0; fi

. /usr/share/initramfs-tools/hook-functions

# Only run if this system is booted with a devicetree
if [ ! -d /sys/firmware/devicetree ]; then exit 0; fi

# Scan for appropriately named devicetree nodes and add any firmware
# we discover this way to the initramfs. Sadly we cannot tell the difference
# between boot-critical firmware and any other so we have to be over-zealous.
# However the impact on initramfs size should be acceptable because we only
# scan the devicetree of the booted system.
for node in $(find /sys/firmware/devicetree -name firmware-name); do
        firmware="$(cat "${node}")"
        if ! add_firmware "${firmware}"; then
                echo "W: Possible missing firmware /lib/firmware/${firmware}
found in devicetree" >&2
        fi
done
~~~

It occurred to me that adding any devicetree specified firmware is a fairly
sensible default. The above code is harmless on ACPI-based systems and although
it's slightly over-zealous it shouldn't bloat the initramfs since too much
since it only scans the devicetree the system actually uses.

#1080340#10
Date:
2024-10-07 16:02:18 UTC
From:
To:
various of my devices not coming up.

Worked first time.

Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>

#1080340#15
Date:
2025-11-26 22:47:16 UTC
From:
To:
Hi,

I found that the hook is also necessary on the t14s.  However, I needed to tweak it slightly.

My devicetree (from kernel 6.18-rc7) has a couple of firmware-files entries with 2 filenames, like this:

                remoteproc@6800000 {
                        compatible = "qcom,x1e80100-adsp-pas";
[...]
                        status = "okay";
                        firmware-name = "qcom/x1e80100/LENOVO/21N1/qcadsp8380.mbn", "qcom/x1e80100/LENOVO/21N1/adsp_dtbs.elf";

sysfs represents it as a file containing two null-terminated strings:

# hexdump -C /sys/firmware/devicetree/base/soc\@0/remoteproc\@6800000/firmware-name
00000000  71 63 6f 6d 2f 78 31 65  38 30 31 30 30 2f 4c 45  |qcom/x1e80100/LE|
00000010  4e 4f 56 4f 2f 32 31 4e  31 2f 71 63 61 64 73 70  |NOVO/21N1/qcadsp|
00000020  38 33 38 30 2e 6d 62 6e  00 71 63 6f 6d 2f 78 31  |8380.mbn.qcom/x1|
00000030  65 38 30 31 30 30 2f 4c  45 4e 4f 56 4f 2f 32 31  |e80100/LENOVO/21|
00000040  4e 31 2f 61 64 73 70 5f  64 74 62 73 2e 65 6c 66  |N1/adsp_dtbs.elf|
00000050  00                                                |.|

Using "cat" dropped the null, causing the two paths to be concatenated together.
Ultimately, it produced a warning for the nonexistent file
"qcom/x1e80100/LENOVO/21N1/qcadsp8380.mbnqcom/x1e80100/LENOVO/21N1/adsp_dtbs.elf".

I changed the script's loop to use "strings", instead of "cat":

for node in $(find /sys/firmware/devicetree -name firmware-name); do
  for firmware in $(strings "${node}"); do
    if ! add_firmware "${firmware}"; then
      echo "W: Possible missing firmware /lib/firmware/${firmware}
      found in devicetree" >&2
    fi
  done
done

And now it works great.

Thanks,
Mark