python312Packages.publicsuffixlist: 1.0.2.20250124 -> 1.0.2.20250127 (#378379)
[NixPkgs.git] / pkgs / os-specific / linux / kernel / manual-config.nix
blob8ef763d872143538bd6a1ee1f5ab095ded19d052
1 { lib, stdenv, buildPackages, runCommand, nettools, bc, bison, flex, perl, rsync, gmp, libmpc, mpfr, openssl
2 , cpio, elfutils, hexdump, zstd, python3Minimal, zlib, pahole, kmod, ubootTools
3 , fetchpatch
4 , rustc, rust-bindgen, rustPlatform
5 }:
7 let
8   lib_ = lib;
9   stdenv_ = stdenv;
11   readConfig = configfile: import (runCommand "config.nix" {} ''
12     echo "{" > "$out"
13     while IFS='=' read key val; do
14       [ "x''${key#CONFIG_}" != "x$key" ] || continue
15       no_firstquote="''${val#\"}";
16       echo '  "'"$key"'" = "'"''${no_firstquote%\"}"'";' >> "$out"
17     done < "${configfile}"
18     echo "}" >> $out
19   '').outPath;
20 in lib.makeOverridable ({
21   # The kernel version
22   version,
23   # The kernel pname (should be set for variants)
24   pname ? "linux",
25   # Position of the Linux build expression
26   pos ? null,
27   # Additional kernel make flags
28   extraMakeFlags ? [],
29   # The name of the kernel module directory
30   # Needs to be X.Y.Z[-extra], so pad with zeros if needed.
31   modDirVersion ? null /* derive from version */,
32   # The kernel source (tarball, git checkout, etc.)
33   src,
34   # a list of { name=..., patch=..., extraConfig=...} patches
35   kernelPatches ? [],
36   # The kernel .config file
37   configfile,
38   # Manually specified nixexpr representing the config
39   # If unspecified, this will be autodetected from the .config
40   config ? lib.optionalAttrs allowImportFromDerivation (readConfig configfile),
41   # Custom seed used for CONFIG_GCC_PLUGIN_RANDSTRUCT if enabled. This is
42   # automatically extended with extra per-version and per-config values.
43   randstructSeed ? "",
44   # Extra meta attributes
45   extraMeta ? {},
47   # for module compatibility
48   isZen      ? false,
49   isLibre    ? false,
50   isHardened ? false,
52   # Whether to utilize the controversial import-from-derivation feature to parse the config
53   allowImportFromDerivation ? false,
54   # ignored
55   features ? null, lib ? lib_, stdenv ? stdenv_,
58 let
59   # Provide defaults. Note that we support `null` so that callers don't need to use optionalAttrs,
60   # which can lead to unnecessary strictness and infinite recursions.
61   modDirVersion_ = if modDirVersion == null then lib.versions.pad 3 version else modDirVersion;
63 let
64   # Shadow the un-defaulted parameter; don't want null.
65   modDirVersion = modDirVersion_;
66   inherit (lib)
67     hasAttr getAttr optional optionals optionalString optionalAttrs maintainers platforms;
69   drvAttrs = config_: kernelConf: kernelPatches: configfile:
70     let
71       # Folding in `ubootTools` in the default nativeBuildInputs is problematic, as
72       # it makes updating U-Boot cumbersome, since it will go above the current
73       # threshold of rebuilds
74       #
75       # To prevent these needless rounds of staging for U-Boot builds, we can
76       # limit the inclusion of ubootTools to target platforms where uImage *may*
77       # be produced.
78       #
79       # This command lists those (kernel-named) platforms:
80       #     .../linux $ grep -l uImage ./arch/*/Makefile | cut -d'/' -f3 | sort
81       #
82       # This is still a guesstimation, but since none of our cached platforms
83       # coincide in that list, this gives us "perfect" decoupling here.
84       linuxPlatformsUsingUImage = [
85         "arc"
86         "arm"
87         "csky"
88         "mips"
89         "powerpc"
90         "sh"
91         "sparc"
92         "xtensa"
93       ];
94       needsUbootTools =
95         lib.elem stdenv.hostPlatform.linuxArch linuxPlatformsUsingUImage
96       ;
98       config = let attrName = attr: "CONFIG_" + attr; in {
99         isSet = attr: hasAttr (attrName attr) config;
101         getValue = attr: if config.isSet attr then getAttr (attrName attr) config else null;
103         isYes = attr: (config.getValue attr) == "y";
105         isNo = attr: (config.getValue attr) == "n";
107         isModule = attr: (config.getValue attr) == "m";
109         isEnabled = attr: (config.isModule attr) || (config.isYes attr);
111         isDisabled = attr: (!(config.isSet attr)) || (config.isNo attr);
112       } // config_;
114       isModular = config.isYes "MODULES";
115       withRust = config.isYes "RUST";
117       buildDTBs = kernelConf.DTB or false;
119       # Dependencies that are required to build kernel modules
120       moduleBuildDependencies = [
121         pahole
122         perl
123         elfutils
124         # module makefiles often run uname commands to find out the kernel version
125         (buildPackages.deterministic-uname.override { inherit modDirVersion; })
126       ]
127       ++ optional (lib.versionAtLeast version "5.13") zstd
128       ++ optionals withRust [ rustc rust-bindgen ]
129       ;
131     in (optionalAttrs isModular { outputs = [ "out" "dev" ]; }) // {
132       passthru = rec {
133         inherit version modDirVersion config kernelPatches configfile
134           moduleBuildDependencies stdenv;
135         inherit isZen isHardened isLibre withRust;
136         isXen = lib.warn "The isXen attribute is deprecated. All Nixpkgs kernels that support it now have Xen enabled." true;
137         baseVersion = lib.head (lib.splitString "-rc" version);
138         kernelOlder = lib.versionOlder baseVersion;
139         kernelAtLeast = lib.versionAtLeast baseVersion;
140       };
142       inherit src;
144       depsBuildBuild = [ buildPackages.stdenv.cc ];
145       nativeBuildInputs = [
146         bison
147         flex
148         perl
149         bc
150         nettools
151         openssl
152         rsync
153         gmp
154         libmpc
155         mpfr
156         elfutils
157         zstd
158         python3Minimal
159         kmod
160         hexdump
161       ] ++ optional  needsUbootTools ubootTools
162         ++ optionals (lib.versionAtLeast version "5.2")  [ cpio pahole zlib ]
163         ++ optionals withRust [ rustc rust-bindgen ];
165       RUST_LIB_SRC = lib.optionalString withRust rustPlatform.rustLibSrc;
167       # avoid leaking Rust source file names into the final binary, which adds
168       # a false dependency on rust-lib-src on targets with uncompressed kernels
169       KRUSTFLAGS = lib.optionalString withRust "--remap-path-prefix ${rustPlatform.rustLibSrc}=/";
171       patches =
172         map (p: p.patch) kernelPatches
173         # Required for deterministic builds along with some postPatch magic.
174         ++ optional (lib.versionOlder version "5.19") ./randstruct-provide-seed.patch
175         ++ optional (lib.versionAtLeast version "5.19") ./randstruct-provide-seed-5.19.patch
176         # Linux 5.12 marked certain PowerPC-only symbols as GPL, which breaks
177         # OpenZFS; this was fixed in Linux 5.19 so we backport the fix
178         # https://github.com/openzfs/zfs/pull/13367
179         ++ optional (lib.versionAtLeast version "5.12" &&
180                      lib.versionOlder version "5.19" &&
181                      stdenv.hostPlatform.isPower)
182           (fetchpatch {
183             url = "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/patch/?id=d9e5c3e9e75162f845880535957b7fd0b4637d23";
184             hash = "sha256-bBOyJcP6jUvozFJU0SPTOf3cmnTQ6ZZ4PlHjiniHXLU=";
185           });
187       postPatch = ''
188         # Ensure that depmod gets resolved through PATH
189         sed -i Makefile -e 's|= /sbin/depmod|= depmod|'
191         # Some linux-hardened patches now remove certain files in the scripts directory, so the file may not exist.
192         [[ -f scripts/ld-version.sh ]] && patchShebangs scripts/ld-version.sh
194         # Set randstruct seed to a deterministic but diversified value. Note:
195         # we could have instead patched gen-random-seed.sh to take input from
196         # the buildFlags, but that would require also patching the kernel's
197         # toplevel Makefile to add a variable export. This would be likely to
198         # cause future patch conflicts.
199         for file in scripts/gen-randstruct-seed.sh scripts/gcc-plugins/gen-random-seed.sh; do
200           if [ -f "$file" ]; then
201             substituteInPlace "$file" \
202               --replace NIXOS_RANDSTRUCT_SEED \
203               $(echo ${randstructSeed}${src} ${placeholder "configfile"} | sha256sum | cut -d ' ' -f 1 | tr -d '\n')
204             break
205           fi
206         done
208         patchShebangs scripts
210         # also patch arch-specific install scripts
211         for i in $(find arch -name install.sh); do
212             patchShebangs "$i"
213         done
215         # unset $src because the build system tries to use it and spams a bunch of warnings
216         # see: https://github.com/torvalds/linux/commit/b1992c3772e69a6fd0e3fc81cd4d2820c8b6eca0
217         unset src
218       '';
220       configurePhase = ''
221         runHook preConfigure
223         mkdir build
224         export buildRoot="$(pwd)/build"
226         echo "manual-config configurePhase buildRoot=$buildRoot pwd=$PWD"
228         if [ -f "$buildRoot/.config" ]; then
229           echo "Could not link $buildRoot/.config : file exists"
230           exit 1
231         fi
232         ln -sv ${configfile} $buildRoot/.config
234         # reads the existing .config file and prompts the user for options in
235         # the current kernel source that are not found in the file.
236         make $makeFlags "''${makeFlagsArray[@]}" oldconfig
237         runHook postConfigure
239         make $makeFlags "''${makeFlagsArray[@]}" prepare
240         actualModDirVersion="$(cat $buildRoot/include/config/kernel.release)"
241         if [ "$actualModDirVersion" != "${modDirVersion}" ]; then
242           echo "Error: modDirVersion ${modDirVersion} specified in the Nix expression is wrong, it should be: $actualModDirVersion"
243           exit 1
244         fi
246         buildFlagsArray+=("KBUILD_BUILD_TIMESTAMP=$(date -u -d @$SOURCE_DATE_EPOCH)")
248         cd $buildRoot
249       '';
251       buildFlags = [
252         "KBUILD_BUILD_VERSION=1-NixOS"
253         kernelConf.target
254         "vmlinux"  # for "perf" and things like that
255       ] ++ optional isModular "modules"
256         ++ optionals buildDTBs ["dtbs" "DTC_FLAGS=-@"]
257       ++ extraMakeFlags;
259       installFlags = [
260         "INSTALL_PATH=$(out)"
261       ] ++ (optional isModular "INSTALL_MOD_PATH=$(out)")
262       ++ optionals buildDTBs ["dtbs_install" "INSTALL_DTBS_PATH=$(out)/dtbs"];
264       preInstall = let
265         # All we really need to do here is copy the final image and System.map to $out,
266         # and use the kernel's modules_install, firmware_install, dtbs_install, etc. targets
267         # for the rest. Easy, right?
268         #
269         # Unfortunately for us, the obvious way of getting the built image path,
270         # make -s image_name, does not work correctly, because some architectures
271         # (*cough* aarch64 *cough*) change KBUILD_IMAGE on the fly in their install targets,
272         # so we end up attempting to install the thing we didn't actually build.
273         #
274         # Thankfully, there's a way out that doesn't involve just hardcoding everything.
275         #
276         # The kernel has an install target, which runs a pretty simple shell script
277         # (located at scripts/install.sh or arch/$arch/boot/install.sh, depending on
278         # which kernel version you're looking at) that tries to do something sensible.
279         #
280         # (it would be great to hijack this script immediately, as it has all the
281         #   information we need passed to it and we don't need it to try and be smart,
282         #   but unfortunately, the exact location of the scripts differs between kernel
283         #   versions, and they're seemingly not considered to be public API at all)
284         #
285         # One of the ways it tries to discover what "something sensible" actually is
286         # is by delegating to what's supposed to be a user-provided install script
287         # located at ~/bin/installkernel.
288         #
289         # (the other options are:
290         #   - a distribution-specific script at /sbin/installkernel,
291         #        which we can't really create in the sandbox easily
292         #   - an architecture-specific script at arch/$arch/boot/install.sh,
293         #        which attempts to guess _something_ and usually guesses very wrong)
294         #
295         # More specifically, the install script exec's into ~/bin/installkernel, if one
296         # exists, with the following arguments:
297         #
298         # $1: $KERNELRELEASE - full kernel version string
299         # $2: $KBUILD_IMAGE - the final image path
300         # $3: System.map - path to System.map file, seemingly hardcoded everywhere
301         # $4: $INSTALL_PATH - path to the destination directory as specified in installFlags
302         #
303         # $2 is exactly what we want, so hijack the script and use the knowledge given to it
304         # by the makefile overlords for our own nefarious ends.
305         #
306         # Note that the makefiles specifically look in ~/bin/installkernel, and
307         # writeShellScriptBin writes the script to <store path>/bin/installkernel,
308         # so HOME needs to be set to just the store path.
309         #
310         # FIXME: figure out a less roundabout way of doing this.
311         installkernel = buildPackages.writeShellScriptBin "installkernel" ''
312           cp -av $2 $4
313           cp -av $3 $4
314         '';
315       in ''
316         installFlagsArray+=("-j$NIX_BUILD_CORES")
317         export HOME=${installkernel}
318       '';
320       # Some image types need special install targets (e.g. uImage is installed with make uinstall on arm)
321       installTargets = [
322         (kernelConf.installTarget or (
323           /**/ if kernelConf.target == "uImage" && stdenv.hostPlatform.linuxArch == "arm" then "uinstall"
324           else if kernelConf.target == "zImage" || kernelConf.target == "Image.gz" || kernelConf.target == "vmlinuz.efi" then "zinstall"
325           else "install"))
326       ];
328       postInstall = optionalString isModular ''
329         mkdir -p $dev
330         cp vmlinux $dev/
331         if [ -z "''${dontStrip-}" ]; then
332           installFlagsArray+=("INSTALL_MOD_STRIP=1")
333         fi
334         make modules_install $makeFlags "''${makeFlagsArray[@]}" \
335           $installFlags "''${installFlagsArray[@]}"
336         unlink $out/lib/modules/${modDirVersion}/build
337         rm -f $out/lib/modules/${modDirVersion}/source
339         mkdir -p $dev/lib/modules/${modDirVersion}/{build,source}
341         # To save space, exclude a bunch of unneeded stuff when copying.
342         (cd .. && rsync --archive --prune-empty-dirs \
343             --exclude='/build/' \
344             * $dev/lib/modules/${modDirVersion}/source/)
346         cd $dev/lib/modules/${modDirVersion}/source
348         cp $buildRoot/{.config,Module.symvers} $dev/lib/modules/${modDirVersion}/build
349         make modules_prepare $makeFlags "''${makeFlagsArray[@]}" O=$dev/lib/modules/${modDirVersion}/build
351         # For reproducibility, removes accidental leftovers from a `cc1` call
352         # from a `try-run` call from the Makefile
353         rm -f $dev/lib/modules/${modDirVersion}/build/.[0-9]*.d
355         # Keep some extra files on some arches (powerpc, aarch64)
356         for f in arch/powerpc/lib/crtsavres.o arch/arm64/kernel/ftrace-mod.o; do
357           if [ -f "$buildRoot/$f" ]; then
358             cp $buildRoot/$f $dev/lib/modules/${modDirVersion}/build/$f
359           fi
360         done
362         # !!! No documentation on how much of the source tree must be kept
363         # If/when kernel builds fail due to missing files, you can add
364         # them here. Note that we may see packages requiring headers
365         # from drivers/ in the future; it adds 50M to keep all of its
366         # headers on 3.10 though.
368         chmod u+w -R ..
369         arch=$(cd $dev/lib/modules/${modDirVersion}/build/arch; ls)
371         # Remove unused arches
372         for d in $(cd arch/; ls); do
373           if [ "$d" = "$arch" ]; then continue; fi
374           if [ "$arch" = arm64 ] && [ "$d" = arm ]; then continue; fi
375           rm -rf arch/$d
376         done
378         # Remove all driver-specific code (50M of which is headers)
379         rm -fR drivers
381         # Keep all headers
382         find .  -type f -name '*.h' -print0 | xargs -0 -r chmod u-w
384         # Keep linker scripts (they are required for out-of-tree modules on aarch64)
385         find .  -type f -name '*.lds' -print0 | xargs -0 -r chmod u-w
387         # Keep root and arch-specific Makefiles
388         chmod u-w Makefile arch/"$arch"/Makefile*
390         # Keep whole scripts dir
391         chmod u-w -R scripts
393         # Delete everything not kept
394         find . -type f -perm -u=w -print0 | xargs -0 -r rm
396         # Delete empty directories
397         find -empty -type d -delete
398       '';
400       requiredSystemFeatures = [ "big-parallel" ];
402       meta = {
403         # https://github.com/NixOS/nixpkgs/pull/345534#issuecomment-2391238381
404         broken = withRust && lib.versionOlder version "6.12";
406         description =
407           "The Linux kernel" +
408           (if kernelPatches == [] then "" else
409             " (with patches: "
410             + lib.concatStringsSep ", " (map (x: x.name) kernelPatches)
411             + ")");
412         license = lib.licenses.gpl2Only;
413         homepage = "https://www.kernel.org/";
414         maintainers = lib.teams.linux-kernel.members ++ [
415           maintainers.thoughtpolice
416         ];
417         platforms = platforms.linux;
418         badPlatforms =
419           lib.optionals (lib.versionOlder version "4.15") [ "riscv32-linux" "riscv64-linux" ] ++
420           lib.optional (lib.versionOlder version "5.19") "loongarch64-linux";
421         timeout = 14400; # 4 hours
422       } // extraMeta;
423     };
425   # Absolute paths for compilers avoid any PATH-clobbering issues.
426   commonMakeFlags = [
427     "ARCH=${stdenv.hostPlatform.linuxArch}"
428     "CROSS_COMPILE=${stdenv.cc.targetPrefix}"
429   ] ++ lib.optionals (stdenv.isx86_64 && stdenv.cc.bintools.isLLVM) [
430     # The wrapper for ld.lld breaks linking the kernel. We use the
431     # unwrapped linker as workaround. See:
432     #
433     # https://github.com/NixOS/nixpkgs/issues/321667
434     "LD=${stdenv.cc.bintools.bintools}/bin/${stdenv.cc.targetPrefix}ld"
435   ] ++ (stdenv.hostPlatform.linux-kernel.makeFlags or [])
436     ++ extraMakeFlags;
439 stdenv.mkDerivation (
440   builtins.foldl' lib.recursiveUpdate {} [
441     (drvAttrs config stdenv.hostPlatform.linux-kernel kernelPatches configfile)
442     {
443       inherit pname version;
445       enableParallelBuilding = true;
447       hardeningDisable = [ "bindnow" "format" "fortify" "stackprotector" "pic" "pie" ];
449       makeFlags = [
450         "O=$(buildRoot)"
451       ] ++ commonMakeFlags;
453       passthru = { inherit commonMakeFlags; };
455       karch = stdenv.hostPlatform.linuxArch;
456     }
457     (optionalAttrs (pos != null) { inherit pos; })
458   ]