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
4 , rustc, rust-bindgen, rustPlatform
11 readConfig = configfile: import (runCommand "config.nix" {} ''
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}"
20 in lib.makeOverridable ({
23 # The kernel pname (should be set for variants)
25 # Position of the Linux build expression
27 # Additional kernel make flags
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.)
34 # a list of { name=..., patch=..., extraConfig=...} patches
36 # The kernel .config file
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.
44 # Extra meta attributes
47 # for module compatibility
52 # Whether to utilize the controversial import-from-derivation feature to parse the config
53 allowImportFromDerivation ? false,
55 features ? null, lib ? lib_, stdenv ? stdenv_,
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;
64 # Shadow the un-defaulted parameter; don't want null.
65 modDirVersion = modDirVersion_;
67 hasAttr getAttr optional optionals optionalString optionalAttrs maintainers platforms;
69 drvAttrs = config_: kernelConf: kernelPatches: configfile:
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
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*
79 # This command lists those (kernel-named) platforms:
80 # .../linux $ grep -l uImage ./arch/*/Makefile | cut -d'/' -f3 | sort
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 = [
95 lib.elem stdenv.hostPlatform.linuxArch linuxPlatformsUsingUImage
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);
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 = [
124 # module makefiles often run uname commands to find out the kernel version
125 (buildPackages.deterministic-uname.override { inherit modDirVersion; })
127 ++ optional (lib.versionAtLeast version "5.13") zstd
128 ++ optionals withRust [ rustc rust-bindgen ]
131 in (optionalAttrs isModular { outputs = [ "out" "dev" ]; }) // {
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;
144 depsBuildBuild = [ buildPackages.stdenv.cc ];
145 nativeBuildInputs = [
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}=/";
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)
183 url = "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/patch/?id=d9e5c3e9e75162f845880535957b7fd0b4637d23";
184 hash = "sha256-bBOyJcP6jUvozFJU0SPTOf3cmnTQ6ZZ4PlHjiniHXLU=";
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')
208 patchShebangs scripts
210 # also patch arch-specific install scripts
211 for i in $(find arch -name install.sh); do
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
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"
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"
246 buildFlagsArray+=("KBUILD_BUILD_TIMESTAMP=$(date -u -d @$SOURCE_DATE_EPOCH)")
252 "KBUILD_BUILD_VERSION=1-NixOS"
254 "vmlinux" # for "perf" and things like that
255 ] ++ optional isModular "modules"
256 ++ optionals buildDTBs ["dtbs" "DTC_FLAGS=-@"]
260 "INSTALL_PATH=$(out)"
261 ] ++ (optional isModular "INSTALL_MOD_PATH=$(out)")
262 ++ optionals buildDTBs ["dtbs_install" "INSTALL_DTBS_PATH=$(out)/dtbs"];
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?
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.
274 # Thankfully, there's a way out that doesn't involve just hardcoding everything.
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.
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)
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.
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)
295 # More specifically, the install script exec's into ~/bin/installkernel, if one
296 # exists, with the following arguments:
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
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.
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.
310 # FIXME: figure out a less roundabout way of doing this.
311 installkernel = buildPackages.writeShellScriptBin "installkernel" ''
316 installFlagsArray+=("-j$NIX_BUILD_CORES")
317 export HOME=${installkernel}
320 # Some image types need special install targets (e.g. uImage is installed with make uinstall on arm)
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"
328 postInstall = optionalString isModular ''
331 if [ -z "''${dontStrip-}" ]; then
332 installFlagsArray+=("INSTALL_MOD_STRIP=1")
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
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.
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
378 # Remove all driver-specific code (50M of which is 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
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
400 requiredSystemFeatures = [ "big-parallel" ];
403 # https://github.com/NixOS/nixpkgs/pull/345534#issuecomment-2391238381
404 broken = withRust && lib.versionOlder version "6.12";
408 (if kernelPatches == [] then "" else
410 + lib.concatStringsSep ", " (map (x: x.name) kernelPatches)
412 license = lib.licenses.gpl2Only;
413 homepage = "https://www.kernel.org/";
414 maintainers = lib.teams.linux-kernel.members ++ [
415 maintainers.thoughtpolice
417 platforms = platforms.linux;
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
425 # Absolute paths for compilers avoid any PATH-clobbering issues.
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:
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 [])
439 stdenv.mkDerivation (
440 builtins.foldl' lib.recursiveUpdate {} [
441 (drvAttrs config stdenv.hostPlatform.linux-kernel kernelPatches configfile)
443 inherit pname version;
445 enableParallelBuilding = true;
447 hardeningDisable = [ "bindnow" "format" "fortify" "stackprotector" "pic" "pie" ];
451 ] ++ commonMakeFlags;
453 passthru = { inherit commonMakeFlags; };
455 karch = stdenv.hostPlatform.linuxArch;
457 (optionalAttrs (pos != null) { inherit pos; })