mirror of
https://github.com/CHN-beta/nixpkgs.git
synced 2026-01-12 02:40:31 +08:00
606 lines
20 KiB
Nix
606 lines
20 KiB
Nix
{
|
||
lib,
|
||
stdenv,
|
||
buildPackages,
|
||
bc,
|
||
bison,
|
||
flex,
|
||
perl,
|
||
rsync,
|
||
gmp,
|
||
libmpc,
|
||
mpfr,
|
||
openssl,
|
||
cpio,
|
||
elfutils,
|
||
hexdump,
|
||
zstd,
|
||
python3Minimal,
|
||
zlib,
|
||
pahole,
|
||
kmod,
|
||
ubootTools,
|
||
fetchpatch,
|
||
rustc-unwrapped,
|
||
rust-bindgen-unwrapped,
|
||
rustPlatform,
|
||
}:
|
||
|
||
let
|
||
lib_ = lib;
|
||
stdenv_ = stdenv;
|
||
|
||
readConfig =
|
||
configfile:
|
||
lib.listToAttrs (
|
||
map
|
||
(
|
||
line:
|
||
let
|
||
match = lib.match "(.*)=\"?(.*)\"?" line;
|
||
in
|
||
{
|
||
name = lib.elemAt match 0;
|
||
value = lib.elemAt match 1;
|
||
}
|
||
)
|
||
(
|
||
lib.filter (line: !(lib.hasPrefix "#" line || line == "")) (
|
||
lib.splitString "\n" (builtins.readFile configfile)
|
||
)
|
||
)
|
||
);
|
||
in
|
||
lib.makeOverridable (
|
||
{
|
||
# The kernel version
|
||
version,
|
||
# The kernel pname (should be set for variants)
|
||
pname ? "linux",
|
||
# Position of the Linux build expression
|
||
pos ? null,
|
||
# Additional kernel make flags
|
||
extraMakeFlags ? [ ],
|
||
# The name of the kernel module directory
|
||
# Needs to be X.Y.Z[-extra], so pad with zeros if needed.
|
||
modDirVersion ? null, # derive from version
|
||
# The kernel source (tarball, git checkout, etc.)
|
||
src,
|
||
# a list of { name=..., patch=..., extraConfig=...} patches
|
||
kernelPatches ? [ ],
|
||
# The kernel .config file
|
||
configfile,
|
||
# Manually specified nixexpr representing the config
|
||
# If unspecified, this will be autodetected from the .config
|
||
config ? lib.optionalAttrs (builtins.isPath configfile || allowImportFromDerivation) (
|
||
readConfig configfile
|
||
),
|
||
# Custom seed used for CONFIG_GCC_PLUGIN_RANDSTRUCT if enabled. This is
|
||
# automatically extended with extra per-version and per-config values.
|
||
randstructSeed ? "",
|
||
# Extra meta attributes
|
||
extraMeta ? { },
|
||
|
||
# for module compatibility
|
||
isZen ? false,
|
||
isLibre ? false,
|
||
isHardened ? false,
|
||
|
||
# Whether to utilize the controversial import-from-derivation feature to parse the config
|
||
allowImportFromDerivation ? false,
|
||
# ignored
|
||
features ? null,
|
||
lib ? lib_,
|
||
stdenv ? stdenv_,
|
||
}:
|
||
|
||
let
|
||
# Provide defaults. Note that we support `null` so that callers don't need to use optionalAttrs,
|
||
# which can lead to unnecessary strictness and infinite recursions.
|
||
modDirVersion_ = if modDirVersion == null then lib.versions.pad 3 version else modDirVersion;
|
||
in
|
||
let
|
||
# Shadow the un-defaulted parameter; don't want null.
|
||
modDirVersion = modDirVersion_;
|
||
inherit (lib)
|
||
hasAttr
|
||
getAttr
|
||
optional
|
||
optionals
|
||
optionalString
|
||
optionalAttrs
|
||
maintainers
|
||
teams
|
||
platforms
|
||
;
|
||
|
||
drvAttrs =
|
||
config_: kernelConf: kernelPatches: configfile:
|
||
let
|
||
# Folding in `ubootTools` in the default nativeBuildInputs is problematic, as
|
||
# it makes updating U-Boot cumbersome, since it will go above the current
|
||
# threshold of rebuilds
|
||
#
|
||
# To prevent these needless rounds of staging for U-Boot builds, we can
|
||
# limit the inclusion of ubootTools to target platforms where uImage *may*
|
||
# be produced.
|
||
#
|
||
# This command lists those (kernel-named) platforms:
|
||
# .../linux $ grep -l uImage ./arch/*/Makefile | cut -d'/' -f3 | sort
|
||
#
|
||
# This is still a guesstimation, but since none of our cached platforms
|
||
# coincide in that list, this gives us "perfect" decoupling here.
|
||
linuxPlatformsUsingUImage = [
|
||
"arc"
|
||
"arm"
|
||
"csky"
|
||
"mips"
|
||
"powerpc"
|
||
"sh"
|
||
"sparc"
|
||
"xtensa"
|
||
];
|
||
needsUbootTools = lib.elem stdenv.hostPlatform.linuxArch linuxPlatformsUsingUImage;
|
||
|
||
config =
|
||
let
|
||
attrName = attr: "CONFIG_" + attr;
|
||
in
|
||
{
|
||
isSet = attr: hasAttr (attrName attr) config;
|
||
|
||
getValue = attr: if config.isSet attr then getAttr (attrName attr) config else null;
|
||
|
||
isYes = attr: (config.getValue attr) == "y";
|
||
|
||
isNo = attr: (config.getValue attr) == "n";
|
||
|
||
isModule = attr: (config.getValue attr) == "m";
|
||
|
||
isEnabled = attr: (config.isModule attr) || (config.isYes attr);
|
||
|
||
isDisabled = attr: (!(config.isSet attr)) || (config.isNo attr);
|
||
}
|
||
// config_;
|
||
|
||
isModular = config.isYes "MODULES";
|
||
withRust = config.isYes "RUST";
|
||
|
||
buildDTBs = kernelConf.DTB or false;
|
||
|
||
# Dependencies that are required to build kernel modules
|
||
moduleBuildDependencies = [
|
||
pahole
|
||
perl
|
||
elfutils
|
||
# module makefiles often run uname commands to find out the kernel version
|
||
(buildPackages.deterministic-uname.override { inherit modDirVersion; })
|
||
]
|
||
++ optional (lib.versionAtLeast version "5.13") zstd
|
||
++ optionals withRust [
|
||
rustc-unwrapped
|
||
rust-bindgen-unwrapped
|
||
];
|
||
|
||
in
|
||
(optionalAttrs isModular {
|
||
outputs = [
|
||
"out"
|
||
"dev"
|
||
"modules"
|
||
];
|
||
})
|
||
// {
|
||
__structuredAttrs = true;
|
||
|
||
passthru = rec {
|
||
inherit
|
||
version
|
||
modDirVersion
|
||
config
|
||
kernelPatches
|
||
configfile
|
||
moduleBuildDependencies
|
||
stdenv
|
||
;
|
||
inherit
|
||
isZen
|
||
isHardened
|
||
isLibre
|
||
withRust
|
||
;
|
||
isXen = lib.warn "The isXen attribute is deprecated. All Nixpkgs kernels that support it now have Xen enabled." true;
|
||
baseVersion = lib.head (lib.splitString "-rc" version);
|
||
kernelOlder = lib.versionOlder baseVersion;
|
||
kernelAtLeast = lib.versionAtLeast baseVersion;
|
||
};
|
||
|
||
inherit src;
|
||
|
||
depsBuildBuild = [ buildPackages.stdenv.cc ];
|
||
nativeBuildInputs = [
|
||
bison
|
||
flex
|
||
perl
|
||
bc
|
||
openssl
|
||
rsync
|
||
gmp
|
||
libmpc
|
||
mpfr
|
||
elfutils
|
||
zstd
|
||
python3Minimal
|
||
kmod
|
||
hexdump
|
||
]
|
||
++ optional needsUbootTools ubootTools
|
||
++ optionals (lib.versionAtLeast version "5.2") [
|
||
cpio
|
||
pahole
|
||
zlib
|
||
]
|
||
++ optionals withRust [
|
||
rustc-unwrapped
|
||
rust-bindgen-unwrapped
|
||
];
|
||
|
||
env = {
|
||
RUST_LIB_SRC = lib.optionalString withRust rustPlatform.rustLibSrc;
|
||
|
||
# avoid leaking Rust source file names into the final binary, which adds
|
||
# a false dependency on rust-lib-src on targets with uncompressed kernels
|
||
KRUSTFLAGS = lib.optionalString withRust "--remap-path-prefix ${rustPlatform.rustLibSrc}=/";
|
||
};
|
||
|
||
patches =
|
||
# kernelPatches can contain config changes and no actual patch
|
||
lib.filter (p: p != null) (map (p: p.patch) kernelPatches)
|
||
# Required for deterministic builds along with some postPatch magic.
|
||
++ optional (lib.versionOlder version "5.19") ./randstruct-provide-seed.patch
|
||
++ optional (lib.versionAtLeast version "5.19") ./randstruct-provide-seed-5.19.patch
|
||
# Linux 5.12 marked certain PowerPC-only symbols as GPL, which breaks
|
||
# OpenZFS; this was fixed in Linux 5.19 so we backport the fix
|
||
# https://github.com/openzfs/zfs/pull/13367
|
||
++
|
||
optional
|
||
(
|
||
lib.versionAtLeast version "5.12" && lib.versionOlder version "5.19" && stdenv.hostPlatform.isPower
|
||
)
|
||
(fetchpatch {
|
||
url = "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/patch/?id=d9e5c3e9e75162f845880535957b7fd0b4637d23";
|
||
hash = "sha256-bBOyJcP6jUvozFJU0SPTOf3cmnTQ6ZZ4PlHjiniHXLU=";
|
||
});
|
||
|
||
postPatch = ''
|
||
# Ensure that depmod gets resolved through PATH
|
||
sed -i Makefile -e 's|= /sbin/depmod|= depmod|'
|
||
|
||
# Some linux-hardened patches now remove certain files in the scripts directory, so the file may not exist.
|
||
[[ -f scripts/ld-version.sh ]] && patchShebangs scripts/ld-version.sh
|
||
|
||
# Set randstruct seed to a deterministic but diversified value. Note:
|
||
# we could have instead patched gen-random-seed.sh to take input from
|
||
# the buildFlags, but that would require also patching the kernel's
|
||
# toplevel Makefile to add a variable export. This would be likely to
|
||
# cause future patch conflicts.
|
||
for file in scripts/gen-randstruct-seed.sh scripts/gcc-plugins/gen-random-seed.sh; do
|
||
if [ -f "$file" ]; then
|
||
substituteInPlace "$file" \
|
||
--replace NIXOS_RANDSTRUCT_SEED \
|
||
$(echo ${randstructSeed}${src} ${placeholder "configfile"} | sha256sum | cut -d ' ' -f 1 | tr -d '\n')
|
||
break
|
||
fi
|
||
done
|
||
|
||
patchShebangs scripts
|
||
|
||
# also patch arch-specific install scripts
|
||
for i in $(find arch -name install.sh); do
|
||
patchShebangs "$i"
|
||
done
|
||
|
||
# unset $src because the build system tries to use it and spams a bunch of warnings
|
||
# see: https://github.com/torvalds/linux/commit/b1992c3772e69a6fd0e3fc81cd4d2820c8b6eca0
|
||
unset src
|
||
'';
|
||
|
||
configurePhase = ''
|
||
runHook preConfigure
|
||
|
||
mkdir build
|
||
export buildRoot="$(pwd)/build"
|
||
|
||
echo "manual-config configurePhase buildRoot=$buildRoot pwd=$PWD"
|
||
|
||
if [ -f "$buildRoot/.config" ]; then
|
||
echo "Could not link $buildRoot/.config : file exists"
|
||
exit 1
|
||
fi
|
||
ln -sv ${configfile} $buildRoot/.config
|
||
|
||
# reads the existing .config file and prompts the user for options in
|
||
# the current kernel source that are not found in the file.
|
||
make "''${makeFlags[@]}" oldconfig
|
||
runHook postConfigure
|
||
|
||
make "''${makeFlags[@]}" prepare
|
||
actualModDirVersion="$(cat $buildRoot/include/config/kernel.release)"
|
||
if [ "$actualModDirVersion" != "${modDirVersion}" ]; then
|
||
echo "Error: modDirVersion ${modDirVersion} specified in the Nix expression is wrong, it should be: $actualModDirVersion"
|
||
exit 1
|
||
fi
|
||
|
||
buildFlags+=("KBUILD_BUILD_TIMESTAMP=$(date -u -d @$SOURCE_DATE_EPOCH)")
|
||
|
||
cd $buildRoot
|
||
'';
|
||
|
||
buildFlags = [
|
||
"KBUILD_BUILD_VERSION=1-NixOS"
|
||
kernelConf.target
|
||
"vmlinux" # for "perf" and things like that
|
||
"scripts_gdb"
|
||
]
|
||
++ optional isModular "modules"
|
||
++ optionals buildDTBs [
|
||
"dtbs"
|
||
"DTC_FLAGS=-@"
|
||
]
|
||
++ extraMakeFlags;
|
||
|
||
installFlags = [
|
||
"INSTALL_PATH=${placeholder "out"}"
|
||
]
|
||
++ (optional isModular "INSTALL_MOD_PATH=${placeholder "modules"}")
|
||
++ optionals buildDTBs [
|
||
"dtbs_install"
|
||
"INSTALL_DTBS_PATH=${placeholder "out"}/dtbs"
|
||
];
|
||
|
||
preInstall =
|
||
let
|
||
# All we really need to do here is copy the final image and System.map to $out,
|
||
# and use the kernel's modules_install, firmware_install, dtbs_install, etc. targets
|
||
# for the rest. Easy, right?
|
||
#
|
||
# Unfortunately for us, the obvious way of getting the built image path,
|
||
# make -s image_name, does not work correctly, because some architectures
|
||
# (*cough* aarch64 *cough*) change KBUILD_IMAGE on the fly in their install targets,
|
||
# so we end up attempting to install the thing we didn't actually build.
|
||
#
|
||
# Thankfully, there's a way out that doesn't involve just hardcoding everything.
|
||
#
|
||
# The kernel has an install target, which runs a pretty simple shell script
|
||
# (located at scripts/install.sh or arch/$arch/boot/install.sh, depending on
|
||
# which kernel version you're looking at) that tries to do something sensible.
|
||
#
|
||
# (it would be great to hijack this script immediately, as it has all the
|
||
# information we need passed to it and we don't need it to try and be smart,
|
||
# but unfortunately, the exact location of the scripts differs between kernel
|
||
# versions, and they're seemingly not considered to be public API at all)
|
||
#
|
||
# One of the ways it tries to discover what "something sensible" actually is
|
||
# is by delegating to what's supposed to be a user-provided install script
|
||
# located at ~/bin/installkernel.
|
||
#
|
||
# (the other options are:
|
||
# - a distribution-specific script at /sbin/installkernel,
|
||
# which we can't really create in the sandbox easily
|
||
# - an architecture-specific script at arch/$arch/boot/install.sh,
|
||
# which attempts to guess _something_ and usually guesses very wrong)
|
||
#
|
||
# More specifically, the install script exec's into ~/bin/installkernel, if one
|
||
# exists, with the following arguments:
|
||
#
|
||
# $1: $KERNELRELEASE - full kernel version string
|
||
# $2: $KBUILD_IMAGE - the final image path
|
||
# $3: System.map - path to System.map file, seemingly hardcoded everywhere
|
||
# $4: $INSTALL_PATH - path to the destination directory as specified in installFlags
|
||
#
|
||
# $2 is exactly what we want, so hijack the script and use the knowledge given to it
|
||
# by the makefile overlords for our own nefarious ends.
|
||
#
|
||
# Note that the makefiles specifically look in ~/bin/installkernel, and
|
||
# writeShellScriptBin writes the script to <store path>/bin/installkernel,
|
||
# so HOME needs to be set to just the store path.
|
||
#
|
||
# FIXME: figure out a less roundabout way of doing this.
|
||
installkernel = buildPackages.writeShellScriptBin "installkernel" ''
|
||
cp -av $2 $4
|
||
cp -av $3 $4
|
||
'';
|
||
in
|
||
''
|
||
installFlags+=("-j$NIX_BUILD_CORES")
|
||
export HOME=${installkernel}
|
||
'';
|
||
|
||
# Some image types need special install targets (e.g. uImage is installed with make uinstall on arm)
|
||
installTargets = [
|
||
(kernelConf.installTarget or (
|
||
if kernelConf.target == "uImage" && stdenv.hostPlatform.linuxArch == "arm" then
|
||
"uinstall"
|
||
else if
|
||
(
|
||
kernelConf.target == "zImage"
|
||
|| kernelConf.target == "Image.gz"
|
||
|| kernelConf.target == "vmlinuz.efi"
|
||
)
|
||
&& builtins.elem stdenv.hostPlatform.linuxArch [
|
||
"arm"
|
||
"arm64"
|
||
"parisc"
|
||
"riscv"
|
||
]
|
||
then
|
||
"zinstall"
|
||
else
|
||
"install"
|
||
)
|
||
)
|
||
];
|
||
|
||
# We remove a bunch of stuff that is symlinked from other places to save space,
|
||
# which trips the broken symlink check. So, just skip it. We'll know if it explodes.
|
||
dontCheckForBrokenSymlinks = true;
|
||
|
||
postInstall = optionalString isModular ''
|
||
mkdir -p $dev
|
||
cp vmlinux $dev/
|
||
|
||
mkdir -p $dev/lib/modules/${modDirVersion}/build/scripts
|
||
cp -rL ../scripts/gdb/ $dev/lib/modules/${modDirVersion}/build/scripts
|
||
|
||
if [ -z "''${dontStrip-}" ]; then
|
||
installFlags+=("INSTALL_MOD_STRIP=1")
|
||
fi
|
||
make modules_install "''${makeFlags[@]}" "''${installFlags[@]}"
|
||
unlink $modules/lib/modules/${modDirVersion}/build
|
||
|
||
mkdir -p $dev/lib/modules/${modDirVersion}/{build,source}
|
||
|
||
# To save space, exclude a bunch of unneeded stuff when copying.
|
||
(cd .. && rsync --archive --prune-empty-dirs \
|
||
--exclude='/build/' \
|
||
* $dev/lib/modules/${modDirVersion}/source/)
|
||
|
||
cd $dev/lib/modules/${modDirVersion}/source
|
||
|
||
cp $buildRoot/{.config,Module.symvers} $dev/lib/modules/${modDirVersion}/build
|
||
make modules_prepare "''${makeFlags[@]}" O=$dev/lib/modules/${modDirVersion}/build
|
||
|
||
# For reproducibility, removes accidental leftovers from a `cc1` call
|
||
# from a `try-run` call from the Makefile
|
||
rm -f $dev/lib/modules/${modDirVersion}/build/.[0-9]*.d
|
||
|
||
# Keep some extra files on some arches (powerpc, aarch64)
|
||
for f in arch/powerpc/lib/crtsavres.o arch/arm64/kernel/ftrace-mod.o; do
|
||
if [ -f "$buildRoot/$f" ]; then
|
||
cp $buildRoot/$f $dev/lib/modules/${modDirVersion}/build/$f
|
||
fi
|
||
done
|
||
|
||
# !!! No documentation on how much of the source tree must be kept
|
||
# If/when kernel builds fail due to missing files, you can add
|
||
# them here. Note that we may see packages requiring headers
|
||
# from drivers/ in the future; it adds 50M to keep all of its
|
||
# headers on 3.10 though.
|
||
|
||
chmod u+w -R ..
|
||
arch=$(cd $dev/lib/modules/${modDirVersion}/build/arch; ls)
|
||
|
||
# Remove unused arches
|
||
for d in $(cd arch/; ls); do
|
||
if [ "$d" = "$arch" ]; then continue; fi
|
||
if [ "$arch" = arm64 ] && [ "$d" = arm ]; then continue; fi
|
||
rm -rf arch/$d
|
||
done
|
||
|
||
# Remove all driver-specific code (50M of which is headers)
|
||
rm -fR drivers
|
||
|
||
# Keep all headers
|
||
find . -type f -name '*.h' -print0 | xargs -0 -r chmod u-w
|
||
|
||
# Keep linker scripts (they are required for out-of-tree modules on aarch64)
|
||
find . -type f -name '*.lds' -print0 | xargs -0 -r chmod u-w
|
||
|
||
# Keep root and arch-specific Makefiles
|
||
chmod u-w Makefile arch/"$arch"/Makefile*
|
||
|
||
# Keep whole scripts dir
|
||
chmod u-w -R scripts
|
||
|
||
# Delete everything not kept
|
||
find . -type f -perm -u=w -print0 | xargs -0 -r rm
|
||
|
||
# Delete empty directories
|
||
find -empty -type d -delete
|
||
'';
|
||
|
||
requiredSystemFeatures = [ "big-parallel" ];
|
||
|
||
meta = {
|
||
# https://github.com/NixOS/nixpkgs/pull/345534#issuecomment-2391238381
|
||
broken = withRust && lib.versionOlder version "6.12";
|
||
|
||
description =
|
||
"The Linux kernel"
|
||
+ (
|
||
if kernelPatches == [ ] then
|
||
""
|
||
else
|
||
" (with patches: " + lib.concatStringsSep ", " (map (x: x.name) kernelPatches) + ")"
|
||
);
|
||
license = lib.licenses.gpl2Only;
|
||
homepage = "https://www.kernel.org/";
|
||
maintainers = [ maintainers.thoughtpolice ];
|
||
teams = [ teams.linux-kernel ];
|
||
platforms = platforms.linux;
|
||
badPlatforms =
|
||
lib.optionals (lib.versionOlder version "4.15") [
|
||
"riscv32-linux"
|
||
"riscv64-linux"
|
||
]
|
||
++ lib.optional (lib.versionOlder version "5.19") "loongarch64-linux";
|
||
timeout = 14400; # 4 hours
|
||
}
|
||
// extraMeta;
|
||
};
|
||
|
||
commonMakeFlags = import ./common-flags.nix {
|
||
inherit
|
||
lib
|
||
stdenv
|
||
buildPackages
|
||
extraMakeFlags
|
||
;
|
||
};
|
||
in
|
||
|
||
stdenv.mkDerivation (
|
||
builtins.foldl' lib.recursiveUpdate { } [
|
||
(drvAttrs config stdenv.hostPlatform.linux-kernel kernelPatches configfile)
|
||
{
|
||
inherit pname version;
|
||
|
||
enableParallelBuilding = true;
|
||
|
||
hardeningDisable = [
|
||
"bindnow"
|
||
"format"
|
||
"fortify"
|
||
"stackprotector"
|
||
"pic"
|
||
"pie"
|
||
];
|
||
|
||
makeFlags = [
|
||
"O=$(buildRoot)"
|
||
|
||
# We have a `modules` variable in the environment for our
|
||
# split output, but the kernel Makefiles also define their
|
||
# own `modules` variable. Their definition wins, but Make
|
||
# remembers that the variable was originally from the
|
||
# environment and exports it to all the build recipes. This
|
||
# breaks the build with an “Argument list too long” error due
|
||
# to passing the huge list of every module object file in the
|
||
# environment of every process invoked by every build recipe.
|
||
#
|
||
# We use `--eval` here to undefine the inherited environment
|
||
# variable before any Makefiles are read, ensuring that the
|
||
# kernel’s definition creates a new, unexported variable.
|
||
"--eval=undefine modules"
|
||
]
|
||
++ commonMakeFlags;
|
||
|
||
passthru = { inherit commonMakeFlags; };
|
||
|
||
karch = stdenv.hostPlatform.linuxArch;
|
||
}
|
||
(optionalAttrs (pos != null) { inherit pos; })
|
||
]
|
||
)
|
||
)
|