8aa8e0ce7f
This should be a significant disk space saving for most NixOS installations. This method is a bit more complicated than doing it in the postInstall for the firmware derivations, but this way it's automatic, so each firmware package doesn't have to separately implement its compression. Currently, only xz compression is supported, but it's likely that future versions of Linux will additionally support zstd, so I've written the code in such a way that it would be very easy to implement zstd compression for those kernels when they arrive, falling back to xz for older (current) kernels. I chose the highest possible level of compression (xz -9) because even at this level, decompression time is negligible. Here's how long it took to decompress every firmware file my laptop uses: i915/kbl_dmc_ver1_04.bin 2ms regulatory.db 4ms regulatory.db.p7s 3ms iwlwifi-7265D-29.ucode 62ms 9d71-GOOGLE-EVEMAX-0-tplg.bin 22ms intel/dsp_fw_kbl.bin 65ms dsp_lib_dsm_core_spt_release.bin 6ms intel/ibt-hw-37.8.10-fw-22.50.19.14.f.bseq 7ms And since booting NixOS is a parallel process, it's unlikely (but difficult to measure) that the time to user interaction was held up at all by most of these. Fixes (partially?) #148197
102 lines
3.7 KiB
Bash
102 lines
3.7 KiB
Bash
source $stdenv/setup
|
|
|
|
# When no modules are built, the $out/lib/modules directory will not
|
|
# exist. Because the rest of the script assumes it does exist, we
|
|
# handle this special case first.
|
|
if ! test -d "$kernel/lib/modules"; then
|
|
if test -z "$rootModules" || test -n "$allowMissing"; then
|
|
mkdir -p "$out"
|
|
exit 0
|
|
else
|
|
echo "Required modules: $rootModules"
|
|
echo "Can not derive a closure of kernel modules because no modules were provided."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
version=$(cd $kernel/lib/modules && ls -d *)
|
|
|
|
echo "kernel version is $version"
|
|
|
|
# Determine the dependencies of each root module.
|
|
mkdir -p $out/lib/modules/"$version"
|
|
touch closure
|
|
for module in $rootModules; do
|
|
echo "root module: $module"
|
|
modprobe --config no-config -d $kernel --set-version "$version" --show-depends "$module" \
|
|
| while read cmd module args; do
|
|
case "$cmd" in
|
|
builtin)
|
|
touch found
|
|
echo "$module" >>closure
|
|
echo " builtin dependency: $module";;
|
|
insmod)
|
|
touch found
|
|
if ! test -e "$module"; then
|
|
echo " dependency not found: $module"
|
|
exit 1
|
|
fi
|
|
target=$(echo "$module" | sed "s^$NIX_STORE.*/lib/modules/^$out/lib/modules/^")
|
|
if test -e "$target"; then
|
|
echo " dependency already copied: $module"
|
|
continue
|
|
fi
|
|
echo "$module" >>closure
|
|
echo " copying dependency: $module"
|
|
mkdir -p $(dirname $target)
|
|
cp "$module" "$target"
|
|
# If the kernel is compiled with coverage instrumentation, it
|
|
# contains the paths of the *.gcda coverage data output files
|
|
# (which it doesn't actually use...). Get rid of them to prevent
|
|
# the whole kernel from being included in the initrd.
|
|
nuke-refs "$target"
|
|
echo "$target" >> $out/insmod-list;;
|
|
*)
|
|
echo " unexpected modprobe output: $cmd $module"
|
|
exit 1;;
|
|
esac
|
|
done || test -n "$allowMissing"
|
|
if ! test -e found; then
|
|
echo " not found"
|
|
if test -z "$allowMissing"; then
|
|
exit 1
|
|
fi
|
|
else
|
|
rm found
|
|
fi
|
|
done
|
|
|
|
mkdir -p $out/lib/firmware
|
|
for module in $(cat closure); do
|
|
# for builtin modules, modinfo will reply with a wrong output looking like:
|
|
# $ modinfo -F firmware unix
|
|
# name: unix
|
|
#
|
|
# There is a pending attempt to fix this:
|
|
# https://github.com/NixOS/nixpkgs/pull/96153
|
|
# https://lore.kernel.org/linux-modules/20200823215433.j5gc5rnsmahpf43v@blumerang/T/#u
|
|
#
|
|
# For now, the workaround is just to filter out the extraneous lines out
|
|
# of its output.
|
|
for i in $(modinfo -b $kernel --set-version "$version" -F firmware $module | grep -v '^name:'); do
|
|
mkdir -p "$out/lib/firmware/$(dirname "$i")"
|
|
echo "firmware for $module: $i"
|
|
for name in "$i" "$i.xz" ""; do
|
|
[ -z "$name" ] && echo "WARNING: missing firmware $i for module $module"
|
|
if cp "$firmware/lib/firmware/$name" "$out/lib/firmware/$name" 2>/dev/null; then
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
done
|
|
|
|
# copy module ordering hints for depmod
|
|
cp $kernel/lib/modules/"$version"/modules.order $out/lib/modules/"$version"/.
|
|
cp $kernel/lib/modules/"$version"/modules.builtin $out/lib/modules/"$version"/.
|
|
|
|
depmod -b $out -a $version
|
|
|
|
# remove original hints from final derivation
|
|
rm $out/lib/modules/"$version"/modules.order
|
|
rm $out/lib/modules/"$version"/modules.builtin
|