nixos/udev: compress all firmware if supported

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

+30 -3
+6 -1
nixos/modules/services/hardware/udev.nix
··· 171 mv etc/udev/hwdb.bin $out 172 ''; 173 174 # Udev has a 512-character limit for ENV{PATH}, so create a symlink 175 # tree to work around this. 176 udevPath = pkgs.buildEnv { ··· 267 ''; 268 apply = list: pkgs.buildEnv { 269 name = "firmware"; 270 - paths = list; 271 pathsToLink = [ "/lib/firmware" ]; 272 ignoreCollisions = true; 273 };
··· 171 mv etc/udev/hwdb.bin $out 172 ''; 173 174 + compressFirmware = if config.boot.kernelPackages.kernelAtLeast "5.3" then 175 + pkgs.compressFirmwareXz 176 + else 177 + id; 178 + 179 # Udev has a 512-character limit for ENV{PATH}, so create a symlink 180 # tree to work around this. 181 udevPath = pkgs.buildEnv { ··· 272 ''; 273 apply = list: pkgs.buildEnv { 274 name = "firmware"; 275 + paths = map compressFirmware list; 276 pathsToLink = [ "/lib/firmware" ]; 277 ignoreCollisions = true; 278 };
+16
pkgs/build-support/kernel/compress-firmware-xz.nix
···
··· 1 + { runCommand }: 2 + 3 + firmware: 4 + 5 + runCommand "${firmware.name}-xz" {} '' 6 + mkdir -p $out/lib 7 + (cd ${firmware} && find lib/firmware -type d -print0) | 8 + (cd $out && xargs -0 mkdir -v --) 9 + (cd ${firmware} && find lib/firmware -type f -print0) | 10 + (cd $out && xargs -0tP "$NIX_BUILD_CORES" -n1 \ 11 + sh -c 'xz -9c -T1 -C crc32 --lzma2=dict=2MiB "${firmware}/$1" > "$1.xz"' --) 12 + (cd ${firmware} && find lib/firmware -type l) | while read link; do 13 + target="$(readlink "${firmware}/$link")" 14 + ln -vs -- "''${target/^${firmware}/$out}.xz" "$out/$link.xz" 15 + done 16 + ''
+6 -2
pkgs/build-support/kernel/modules-closure.sh
··· 81 for i in $(modinfo -b $kernel --set-version "$version" -F firmware $module | grep -v '^name:'); do 82 mkdir -p "$out/lib/firmware/$(dirname "$i")" 83 echo "firmware for $module: $i" 84 - cp "$firmware/lib/firmware/$i" "$out/lib/firmware/$i" 2>/dev/null \ 85 - || echo "WARNING: missing firmware $i for module $module" 86 done 87 done 88
··· 81 for i in $(modinfo -b $kernel --set-version "$version" -F firmware $module | grep -v '^name:'); do 82 mkdir -p "$out/lib/firmware/$(dirname "$i")" 83 echo "firmware for $module: $i" 84 + for name in "$i" "$i.xz" ""; do 85 + [ -z "$name" ] && echo "WARNING: missing firmware $i for module $module" 86 + if cp "$firmware/lib/firmware/$name" "$out/lib/firmware/$name" 2>/dev/null; then 87 + break 88 + fi 89 + done 90 done 91 done 92
+2
pkgs/top-level/all-packages.nix
··· 808 sanitizers = [ ]; 809 }; 810 811 makeModulesClosure = { kernel, firmware, rootModules, allowMissing ? false }: 812 callPackage ../build-support/kernel/modules-closure.nix { 813 inherit kernel firmware rootModules allowMissing;
··· 808 sanitizers = [ ]; 809 }; 810 811 + compressFirmwareXz = callPackage ../build-support/kernel/compress-firmware-xz.nix { }; 812 + 813 makeModulesClosure = { kernel, firmware, rootModules, allowMissing ? false }: 814 callPackage ../build-support/kernel/modules-closure.nix { 815 inherit kernel firmware rootModules allowMissing;