{
  lib,
  stdenv,
  buildPackages,
  runCommand,
  nettools,
  bc,
  bison,
  flex,
  perl,
  rsync,
  gmp,
  libmpc,
  mpfr,
  openssl,
  cpio,
  elfutils,
  hexdump,
  zstd,
  python3Minimal,
  zlib,
  pahole,
  kmod,
  ubootTools,
  erofs-utils,
  cryptsetup,
  fetchpatch,
  rustc,
  rust-bindgen,
  rustPlatform,
}:

let
  lib_ = lib;
  stdenv_ = stdenv;

  readConfig =
    configfile:
    import
      (runCommand "config.nix" { } ''
        echo "{" > "$out"
        while IFS='=' read key val; do
          [ "x''${key#CONFIG_}" != "x$key" ] || continue
          no_firstquote="''${val#\"}";
          echo '  "'"$key"'" = "'"''${no_firstquote%\"}"'";' >> "$out"
        done < "${configfile}"
        echo "}" >> $out
      '').outPath;
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 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
      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
            rust-bindgen
          ];

      in
      (optionalAttrs isModular {
        outputs = [
          "out"
          "dev"
        ];
      })
      // {
        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
            nettools
            openssl
            rsync
            gmp
            libmpc
            mpfr
            elfutils
            zstd
            python3Minimal
            kmod
            hexdump
            erofs-utils
            cryptsetup
          ]
          ++ optional needsUbootTools ubootTools
          ++ optionals (lib.versionAtLeast version "5.2") [
            cpio
            pahole
            zlib
          ]
          ++ optionals withRust [
            rustc
            rust-bindgen
          ];

        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 =
        #   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 "''${makeFlagsArray[@]}" oldconfig
          runHook postConfigure

          make $makeFlags "''${makeFlagsArray[@]}" 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

          buildFlagsArray+=("KBUILD_BUILD_TIMESTAMP=$(date -u -d @$SOURCE_DATE_EPOCH)")

          cd $buildRoot
        '';

        buildFlags =
          [
            "KBUILD_BUILD_VERSION=1-PatOS"
            kernelConf.target
            "vmlinux" # for "perf" and things like that
          ]
          ++ optional isModular "modules"
          ++ optionals buildDTBs [
            "dtbs"
            "DTC_FLAGS=-@"
          ]
          ++ extraMakeFlags;

        installFlags =
          [
            "INSTALL_PATH=$(out)"
          ]
          ++ (optional isModular "INSTALL_MOD_PATH=$(out)")
          ++ optionals buildDTBs [
            "dtbs_install"
            "INSTALL_DTBS_PATH=$(out)/dtbs"
          ];

        dontStrip = true;

        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
          ''
            installFlagsArray+=("-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"
            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/
          # if [ -z "''${dontStrip-}" ]; then
          #   installFlagsArray+=("INSTALL_MOD_STRIP=1")
          # fi
          make modules_install $makeFlags "''${makeFlagsArray[@]}" \
            $installFlags "''${installFlagsArray[@]}"
          unlink $out/lib/modules/${modDirVersion}/build
          rm -f $out/lib/modules/${modDirVersion}/source

          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 "''${makeFlagsArray[@]}" 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

          pkgName="patos-kernel-modules"
          mkdir -p $out/tree/usr/lib/extension-release.d
          cat << EOF > $out/tree/usr/lib/extension-release.d/extension-release.$pkgName
          ID=patos
          IMAGE_ID=$pkgName
          IMAGE_VERSION=${version}
          VERSION_ID=patos
          EOF
          cp -Prp $out/lib/modules $out/tree/usr/lib/modules
          find $out/tree -type d -exec chmod 0755 {} \;
          mkfs.erofs --all-root -zlz4hc,12 -C1048576 -Efragments,dedupe,ztailpacking $out/$pkgName.raw $out/tree/
          veritysetup format --root-hash-file $out/$pkgName.roothash $out/$pkgName.raw $out/$pkgName.verity
          chmod -R 755 $out/tree && rm -rf $out/tree
        '';

        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 = lib.teams.linux-kernel.members ++ [
            maintainers.thoughtpolice
          ];
          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;
      };

    # Absolute paths for compilers avoid any PATH-clobbering issues.
    commonMakeFlags =
      [
        "ARCH=${stdenv.hostPlatform.linuxArch}"
        "CROSS_COMPILE=${stdenv.cc.targetPrefix}"
      ]
      ++ lib.optionals (stdenv.isx86_64 && stdenv.cc.bintools.isLLVM) [
        # The wrapper for ld.lld breaks linking the kernel. We use the
        # unwrapped linker as workaround. See:
        #
        # https://github.com/NixOS/nixpkgs/issues/321667
        "LD=${stdenv.cc.bintools.bintools}/bin/${stdenv.cc.targetPrefix}ld"
      ]
      ++ (stdenv.hostPlatform.linux-kernel.makeFlags or [ ])
      ++ 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)"
        ] ++ commonMakeFlags;

        passthru = { inherit commonMakeFlags; };

        karch = stdenv.hostPlatform.linuxArch;
      }
      (optionalAttrs (pos != null) { inherit pos; })
    ]
  )
)