From a3e2a970f896821f75c9f57962953d77c4c5a39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Sj=C3=B6strom?= <lars@radicore.se> Date: Mon, 17 Mar 2025 10:18:30 +0100 Subject: [PATCH] chore: clean up --- flake.nix | 80 +- pkgs/cert/default.nix | 17 + pkgs/image/default.nix | 149 +++- pkgs/image/mkimage.sh | 140 --- pkgs/kernel/default.nix | 20 +- pkgs/kernel/generic.config | 7 +- pkgs/kernel/manual-config.nix | 981 ++++++++++++---------- pkgs/rootfs/default.nix | 38 - pkgs/rootfs/mkinitrd.nix | 67 +- pkgs/rootfs/mkinitrd.sh | 53 -- pkgs/rootfs/{mkrootfs.sh => mkrootfs.nix} | 65 +- 11 files changed, 845 insertions(+), 772 deletions(-) create mode 100644 pkgs/cert/default.nix delete mode 100644 pkgs/image/mkimage.sh delete mode 100644 pkgs/rootfs/default.nix delete mode 100644 pkgs/rootfs/mkinitrd.sh rename pkgs/rootfs/{mkrootfs.sh => mkrootfs.nix} (75%) diff --git a/flake.nix b/flake.nix index 5b1774f..2358ab1 100644 --- a/flake.nix +++ b/flake.nix @@ -22,22 +22,14 @@ { packages = { default = patosPkgs.image; - image = pkgs.callPackage ./pkgs/image { - inherit patosPkgs; - inherit version; - }; - rootfs = pkgs.callPackage ./pkgs/rootfs { - inherit patosPkgs; - inherit version; - }; - initrd = pkgs.callPackage ./pkgs/rootfs/mkinitrd.nix { - inherit patosPkgs; - inherit version; - }; + image = pkgs.callPackage ./pkgs/image { inherit patosPkgs version; }; + rootfs = pkgs.callPackage ./pkgs/rootfs/mkrootfs.nix { inherit patosPkgs version; }; + initrd = pkgs.callPackage ./pkgs/rootfs/mkinitrd.nix { inherit patosPkgs version; }; kernel = pkgs.callPackage ./pkgs/kernel { }; glibc = pkgs.callPackage ./pkgs/glibc { }; busybox = pkgs.callPackage ./pkgs/busybox { }; openssl = pkgs.callPackage ./pkgs/openssl { }; + cert = pkgs.callPackage ./pkgs/cert { }; kexec = pkgs.callPackage ./pkgs/kexec-tools { }; lvm2 = pkgs.callPackage ./pkgs/lvm2 { }; tpm2-tools = pkgs.callPackage ./pkgs/tpm2-tools { inherit patosPkgs; }; @@ -51,38 +43,38 @@ name = "debug-tools"; version = "0.0.1"; packages = [ - { drv = pkgs.curl; path = "bin/curl"; } - { drv = pkgs.bash; path = "bin/bash"; } - { drv = patosPkgs.glibc; path = "bin/ldd"; } - { drv = pkgs.keyutils; path = "bin/keyctl"; } - { drv = pkgs.gnutar; path = "bin/tar"; } - { drv = pkgs.binutils-unwrapped; path = "bin/strings"; } - { drv = pkgs.strace; path = "bin/strace"; } - { drv = patosPkgs.tpm2-tools; path = "bin/tpm2"; } - { drv = patosPkgs.openssl; path = "bin/openssl"; } - { drv = pkgs.cryptsetup; path = "bin/cryptsetup"; } - { drv = pkgs.cryptsetup; path = "bin/veritysetup"; } - { drv = pkgs.erofs-utils; path = "bin/mkfs.erofs"; } - # shared lib required for cryptsetup - { drv = pkgs.popt; path = "lib/libpopt.so.0.0.2"; } - { drv = pkgs.popt; path = "lib/libpopt.so.0"; } - { drv = pkgs.popt; path = "lib/libpopt.so"; } - # shared lib required for mkfs.erofs - { drv = pkgs.lz4.lib; path = "lib/liblz4.so.1.10.0"; } - { drv = pkgs.lz4.lib; path = "lib/liblz4.so.1"; } - { drv = pkgs.lz4.lib; path = "lib/liblz4.so"; } - # shared lib required for binutils - { drv = pkgs.binutils-unwrapped.lib; path = "lib/libsframe.so.1.0.0"; } - { drv = pkgs.binutils-unwrapped.lib; path = "lib/libsframe.so.1"; } - { drv = pkgs.binutils-unwrapped.lib; path = "lib/libbfd-2.43.1.so"; } - { drv = pkgs.binutils-unwrapped.lib; path = "lib/libbfd.so"; } - # shared lib required for strace - { drv = pkgs.elfutils.out; path = "lib/libdw-0.192.so"; } - { drv = pkgs.elfutils.out; path = "lib/libdw.so.1"; } - { drv = pkgs.elfutils.out; path = "lib/libdw.so"; } - { drv = pkgs.elfutils.out; path = "lib/libelf-0.192.so"; } - { drv = pkgs.elfutils.out; path = "lib/libelf.so.1"; } - { drv = pkgs.elfutils.out; path = "lib/libelf.so"; } + { drv = pkgs.curl; path = "bin/curl"; } + { drv = pkgs.bash; path = "bin/bash"; } + { drv = patosPkgs.glibc; path = "bin/ldd"; } + { drv = pkgs.keyutils; path = "bin/keyctl"; } + { drv = pkgs.gnutar; path = "bin/tar"; } + { drv = pkgs.binutils-unwrapped; path = "bin/strings"; } + { drv = pkgs.strace; path = "bin/strace"; } + { drv = patosPkgs.tpm2-tools; path = "bin/tpm2"; } + { drv = patosPkgs.openssl; path = "bin/openssl"; } + { drv = pkgs.cryptsetup; path = "bin/cryptsetup"; } + { drv = pkgs.cryptsetup; path = "bin/veritysetup"; } + { drv = pkgs.erofs-utils; path = "bin/mkfs.erofs"; } + # shared lib required for cryptsetup + { drv = pkgs.popt; path = "lib/libpopt.so.0.0.2"; } + { drv = pkgs.popt; path = "lib/libpopt.so.0"; } + { drv = pkgs.popt; path = "lib/libpopt.so"; } + # shared lib required for mkfs.erofs + { drv = pkgs.lz4.lib; path = "lib/liblz4.so.1.10.0"; } + { drv = pkgs.lz4.lib; path = "lib/liblz4.so.1"; } + { drv = pkgs.lz4.lib; path = "lib/liblz4.so"; } + # shared lib required for binutils + { drv = pkgs.binutils-unwrapped.lib; path = "lib/libsframe.so.1.0.0"; } + { drv = pkgs.binutils-unwrapped.lib; path = "lib/libsframe.so.1"; } + { drv = pkgs.binutils-unwrapped.lib; path = "lib/libbfd-2.43.1.so"; } + { drv = pkgs.binutils-unwrapped.lib; path = "lib/libbfd.so"; } + # shared lib required for strace + { drv = pkgs.elfutils.out; path = "lib/libdw-0.192.so"; } + { drv = pkgs.elfutils.out; path = "lib/libdw.so.1"; } + { drv = pkgs.elfutils.out; path = "lib/libdw.so"; } + { drv = pkgs.elfutils.out; path = "lib/libelf-0.192.so"; } + { drv = pkgs.elfutils.out; path = "lib/libelf.so.1"; } + { drv = pkgs.elfutils.out; path = "lib/libelf.so"; } ]; }; }; diff --git a/pkgs/cert/default.nix b/pkgs/cert/default.nix new file mode 100644 index 0000000..f3237e9 --- /dev/null +++ b/pkgs/cert/default.nix @@ -0,0 +1,17 @@ +{ + runCommand, + pkgs, + +}: + +runCommand "patagia-certs" + { + buildInputs = with pkgs; [ + openssl + ]; + + } + '' + mkdir -pv $out + openssl req -new -x509 -days 365 -nodes -out $out/cert.pem -keyout $out/key.pem -subj "/CN=patagia-signing" + '' diff --git a/pkgs/image/default.nix b/pkgs/image/default.nix index 5612185..7d5f565 100644 --- a/pkgs/image/default.nix +++ b/pkgs/image/default.nix @@ -1,16 +1,15 @@ { pkgs, - stdenvNoCC, patosPkgs, version, + runCommand, ... }: let pname = "patos-image"; in -stdenvNoCC.mkDerivation (finalAttrs: { +runCommand pname { inherit version; - inherit pname; buildInputs = with pkgs; [ erofs-utils @@ -27,12 +26,142 @@ stdenvNoCC.mkDerivation (finalAttrs: { SYSTEMD_REPART_MKFS_OPTIONS_EROFS = "--all-root"; # -zlz4hc,12 -C1048576 -Efragments,dedupe,ztailpacking"; }; - systemd = patosPkgs.systemd.out; - kernel = patosPkgs.kernel; - initrd = patosPkgs.initrd.out; - rootfs = patosPkgs.rootfs.out; - kernelCmdLine = "console=ttyS0"; +} +'' +mkdir -p $out/init.repart.d $out/final.repart.d $out/boot +pushd $out - builder = ./mkimage.sh; -}) +# Don't seem to work just to create a symlink to rootfs derivation? +# ln -sf $rootfs rootfs +mkdir rootfs +cp -prP ${patosPkgs.rootfs}/* rootfs/ +find rootfs/ -type d -exec chmod 755 {} \; + +# set default target to multi-user +ln -sf multi-user.target rootfs/usr/lib/systemd/system/default.target + +# enable dbus +ln -sf ../dbus.service rootfs/usr/lib/systemd/system/multi-user.target.wants/dbus.service +ln -sf ../dbus.socket rootfs/usr/lib/systemd/system/sockets.target.wants/dbus.socket + +# enable network services +ln -sf ../systemd-networkd.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-networkd.service +ln -sf ../systemd-resolved.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-resolved.service +ln -sf ../systemd-timesyncd.service rootfs/usr/lib/systemd/system/multi-user.target.wants/systemd-timesyncd.service +# enable default network config +mv rootfs/usr/lib/systemd/network/89-ethernet.network.example rootfs/usr/lib/systemd/network/89-ethernet.network + +# enable confext/sysext services +ln -sf ../systemd-confext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-confext.service +ln -sf ../systemd-sysext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-sysext.service + +# Initial partitioning +cat <<EOF > init.repart.d/10-root.conf +[Partition] +Type=root +Format=erofs +Minimize=best +CopyFiles=/rootfs:/ +Verity=data +VerityMatchKey=root +SplitName=root +EOF + +cat <<EOF > init.repart.d/20-root-verity.conf +[Partition] +Type=root-verity +Verity=hash +VerityMatchKey=root +Minimize=best +SplitName=verity +EOF + +#TODO: Add verity signature partition + +${patosPkgs.systemd}/usr/bin/systemd-repart \ + --no-pager \ + --empty=create \ + --size=auto \ + --definitions=./init.repart.d \ + --split=true \ + --json=pretty \ + --root=$out \ + patos-$version.raw > init-repart-output.json && rm -f patos-$version.raw + +roothash=$(jq -r '.[0].roothash' init-repart-output.json) +rootPart=$(jq -r '.[0].split_path' init-repart-output.json) +rootUuid=$(jq -r '.[0].uuid' init-repart-output.json) + +verityPart=$(jq -r '.[1].split_path' init-repart-output.json) +verityUuid=$(jq -r '.[1].uuid' init-repart-output.json) + +${patosPkgs.systemd}/usr/bin/ukify build \ + --linux ${patosPkgs.kernel}/bzImage \ + --initrd ${patosPkgs.initrd}/initrd.xz \ + --os-release @rootfs/etc/os-release \ + --cmdline "$kernelCmdLine roothash=$roothash" \ + -o patos_${version}.efi + +# install ESP +SYSTEMD_RELAX_ESP_CHECKS=1 ${patosPkgs.systemd}/usr/bin/bootctl install --root ./rootfs --esp-path /boot \ + --secure-boot-auto-enroll=true --certificate=${patosPkgs.cert}/cert.pem --private-key=${patosPkgs.cert}/key.pem +echo "timeout 2" > rootfs/boot/loader/loader.conf + +# sign EFIs +${patosPkgs.systemd}/usr/lib/systemd/systemd-sbsign sign --certificate=${patosPkgs.cert}/cert.pem --private-key=${patosPkgs.cert}/key.pem \ + rootfs/boot/EFI/BOOT/BOOTX64.EFI --output=rootfs/boot/EFI/BOOT/BOOTX64.EFI + +${patosPkgs.systemd}/usr/lib/systemd/systemd-sbsign sign --certificate=${patosPkgs.cert}/cert.pem --private-key=${patosPkgs.cert}/key.pem \ + patos_${version}.efi --output=patos_${version}.efi + +# install UKI +cp patos_${version}.efi rootfs/boot/EFI/Linux + +echo "secure-boot-enroll force" >> rootfs/boot/loader/loader.conf + +# Final partitioning +cat <<EOF > final.repart.d/10-esp.conf +[Partition] +Type=esp +Format=vfat +SizeMinBytes=160M +SizeMaxBytes=160M +CopyFiles=/rootfs/boot:/ +EOF + +cat <<EOF > final.repart.d/20-root.conf +[Partition] +Type=root +Label=root-${version} +CopyBlocks=/$rootPart +UUID=$rootUuid +SizeMinBytes=256M +SizeMaxBytes=256M +ReadOnly=1 +EOF + +cat <<EOF > final.repart.d/22-root-verity.conf +[Partition] +Type=root-verity +Label=verity-${version} +CopyBlocks=/$verityPart +UUID=$verityUuid +SizeMinBytes=10M +SizeMaxBytes=10M +ReadOnly=1 +EOF + +# finalize image ready for boot +${patosPkgs.systemd}/usr/bin/systemd-repart \ + --no-pager \ + --empty=create \ + --size=auto \ + --definitions=./final.repart.d \ + --root=$out \ + patos-$version.raw > final-repart-output.json + +rm -rf rootfs + +popd +'' diff --git a/pkgs/image/mkimage.sh b/pkgs/image/mkimage.sh deleted file mode 100644 index 1d14349..0000000 --- a/pkgs/image/mkimage.sh +++ /dev/null @@ -1,140 +0,0 @@ -set -ex -o pipefail - -mkdir -p $out/init.repart.d $out/final.repart.d $out/boot -pushd $out - -# Don't seem to work just to create a symlink to rootfs derivation? -# ln -sf $rootfs rootfs -mkdir rootfs -cp -prP $rootfs/* rootfs/ -find rootfs/ -type d -exec chmod 755 {} \; - -# set default target to multi-user -ln -sf multi-user.target rootfs/usr/lib/systemd/system/default.target - -# enable dbus -ln -sf ../dbus.service rootfs/usr/lib/systemd/system/multi-user.target.wants/dbus.service -ln -sf ../dbus.socket rootfs/usr/lib/systemd/system/sockets.target.wants/dbus.socket - -# enable network services -ln -sf ../systemd-networkd.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-networkd.service -ln -sf ../systemd-resolved.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-resolved.service -ln -sf ../systemd-timesyncd.service rootfs/usr/lib/systemd/system/multi-user.target.wants/systemd-timesyncd.service -# enable default network config -mv rootfs/usr/lib/systemd/network/89-ethernet.network.example rootfs/usr/lib/systemd/network/89-ethernet.network - -# enable confext/sysext services -ln -sf ../systemd-confext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-confext.service -ln -sf ../systemd-sysext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-sysext.service - -# Initial partitioning -cat <<EOF > init.repart.d/10-root.conf -[Partition] -Type=root -Format=erofs -Minimize=best -CopyFiles=/rootfs:/ -Verity=data -VerityMatchKey=root -SplitName=root -EOF - -cat <<EOF > init.repart.d/20-root-verity.conf -[Partition] -Type=root-verity -Verity=hash -VerityMatchKey=root -Minimize=best -SplitName=verity -EOF - -#TODO: Add verity signature partition - -$systemd/usr/bin/systemd-repart \ - --no-pager \ - --empty=create \ - --size=auto \ - --definitions=./init.repart.d \ - --split=true \ - --json=pretty \ - --root=$out \ - patos-$version.raw > init-repart-output.json && rm -f patos-$version.raw - -roothash=$(jq -r '.[0].roothash' init-repart-output.json) -rootPart=$(jq -r '.[0].split_path' init-repart-output.json) -rootUuid=$(jq -r '.[0].uuid' init-repart-output.json) - -verityPart=$(jq -r '.[1].split_path' init-repart-output.json) -verityUuid=$(jq -r '.[1].uuid' init-repart-output.json) - -$systemd/usr/bin/ukify build \ - --linux $kernel/bzImage \ - --initrd $initrd/initrd.xz \ - --os-release @rootfs/etc/os-release \ - --cmdline "$kernelCmdLine roothash=$roothash" \ - -o patos_${version}.efi - -# Secure boot -openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem -subj "/CN=patagia-signing" - -# install ESP -SYSTEMD_RELAX_ESP_CHECKS=1 $systemd/usr/bin/bootctl install --root ./rootfs --esp-path /boot \ - --secure-boot-auto-enroll=true --certificate=./cert.pem --private-key=./key.pem -echo "timeout 2" > rootfs/boot/loader/loader.conf - -# sign EFIs -$systemd/usr/lib/systemd/systemd-sbsign sign --certificate=./cert.pem --private-key=./key.pem \ - rootfs/boot/EFI/BOOT/BOOTX64.EFI --output=rootfs/boot/EFI/BOOT/BOOTX64.EFI - -$systemd/usr/lib/systemd/systemd-sbsign sign --certificate=./cert.pem --private-key=./key.pem \ - patos_${version}.efi --output=patos_${version}.efi - -# install UKI -cp patos_${version}.efi rootfs/boot/EFI/Linux - -echo "secure-boot-enroll force" >> rootfs/boot/loader/loader.conf - -# Final partitioning -cat <<EOF > final.repart.d/10-esp.conf -[Partition] -Type=esp -Format=vfat -SizeMinBytes=160M -SizeMaxBytes=160M -CopyFiles=/rootfs/boot:/ -EOF - -cat <<EOF > final.repart.d/20-root.conf -[Partition] -Type=root -Label=root-${version} -CopyBlocks=/${rootPart} -UUID=${rootUuid} -SizeMinBytes=256M -SizeMaxBytes=256M -ReadOnly=1 -EOF - -cat <<EOF > final.repart.d/22-root-verity.conf -[Partition] -Type=root-verity -Label=verity-${version} -CopyBlocks=/${verityPart} -UUID=${verityUuid} -SizeMinBytes=10M -SizeMaxBytes=10M -ReadOnly=1 -EOF - -# finalize image ready for boot -$systemd/usr/bin/systemd-repart \ - --no-pager \ - --empty=create \ - --size=auto \ - --definitions=./final.repart.d \ - --root=$out \ - patos-$version.raw > final-repart-output.json - -rm -rf rootfs - -popd diff --git a/pkgs/kernel/default.nix b/pkgs/kernel/default.nix index edbfb65..a5f24db 100644 --- a/pkgs/kernel/default.nix +++ b/pkgs/kernel/default.nix @@ -3,13 +3,13 @@ let version = "6.13.7"; hash = "sha256-Ojm2IDi3rC9D0mofhLQoPhl4BOHoF61jfpo9h0xHgB0="; in - (pkgs.callPackage ./manual-config.nix {}) { - version = "${version}-patos1"; - modDirVersion = version; - src = pkgs.fetchurl { - url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${version}.tar.xz"; - hash = hash; - }; - configfile = ./generic.config; - allowImportFromDerivation = true; - } +(pkgs.callPackage ./manual-config.nix { }) { + version = "${version}-patos1"; + modDirVersion = version; + src = pkgs.fetchurl { + url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${version}.tar.xz"; + hash = hash; + }; + configfile = ./generic.config; + allowImportFromDerivation = true; +} diff --git a/pkgs/kernel/generic.config b/pkgs/kernel/generic.config index 647bf91..048421b 100644 --- a/pkgs/kernel/generic.config +++ b/pkgs/kernel/generic.config @@ -522,10 +522,6 @@ CONFIG_DEBUG_BUGVERBOSE=y CONFIG_DEBUG_ENTRY=y CONFIG_DEBUG_FS_ALLOW_ALL=y CONFIG_DEBUG_FS=y -#CONFIG_DEBUG_INFO_BTF_MODULES=y -#CONFIG_DEBUG_INFO_BTF=y -#CONFIG_DEBUG_INFO_COMPRESSED_NONE=y -#CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_DEBUG_INFO=n CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_LIST=y @@ -1401,9 +1397,8 @@ CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_SIG=y -CONFIG_MODULE_SIG_FORCE=n +CONFIG_MODULE_SIG_FORCE=y CONFIG_MODULE_SIG_ALL=y -CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" CONFIG_MODULES_TREE_LOOKUP=y CONFIG_MODULES_USE_ELF_RELA=y CONFIG_MODULES=y diff --git a/pkgs/kernel/manual-config.nix b/pkgs/kernel/manual-config.nix index ffcf758..9f1ba99 100644 --- a/pkgs/kernel/manual-config.nix +++ b/pkgs/kernel/manual-config.nix @@ -1,465 +1,576 @@ -{ lib, stdenv, buildPackages, runCommand, nettools, bc, bison, flex, perl, rsync, gmp, libmpc, mpfr, openssl -, cpio, elfutils, hexdump, zstd, python3Minimal, zlib, pahole, kmod, ubootTools -, fetchpatch -, rustc, rust-bindgen, rustPlatform +{ + lib, + stdenv, + buildPackages, + runCommand, + nettools, + bc, + bison, + flex, + perl, + rsync, + gmp, + libmpc, + mpfr, + openssl, + cpio, + elfutils, + hexdump, + zstd, + python3Minimal, + zlib, + pahole, + kmod, + ubootTools, + 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; + 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 -let - # Shadow the un-defaulted parameter; don't want null. - modDirVersion = modDirVersion_; - inherit (lib) - hasAttr getAttr optional optionals optionalString optionalAttrs maintainers platforms; +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 ? { }, - 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 + # 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 ; - config = let attrName = attr: "CONFIG_" + attr; in { - isSet = attr: hasAttr (attrName attr) config; + 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; - getValue = attr: if config.isSet attr then getAttr (attrName attr) config else null; + config = + let + attrName = attr: "CONFIG_" + attr; + in + { + isSet = attr: hasAttr (attrName attr) config; - isYes = attr: (config.getValue attr) == "y"; + getValue = attr: if config.isSet attr then getAttr (attrName attr) config else null; - isNo = attr: (config.getValue attr) == "n"; + isYes = attr: (config.getValue attr) == "y"; - isModule = attr: (config.getValue attr) == "m"; + isNo = attr: (config.getValue attr) == "n"; - isEnabled = attr: (config.isModule attr) || (config.isYes attr); + isModule = attr: (config.getValue attr) == "m"; - isDisabled = attr: (!(config.isSet attr)) || (config.isNo attr); - } // config_; + isEnabled = attr: (config.isModule attr) || (config.isYes attr); - isModular = config.isYes "MODULES"; - withRust = config.isYes "RUST"; + isDisabled = attr: (!(config.isSet attr)) || (config.isNo attr); + } + // config_; - buildDTBs = kernelConf.DTB or false; + isModular = config.isYes "MODULES"; + withRust = config.isYes "RUST"; - # 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 ] - ; + buildDTBs = kernelConf.DTB or false; - 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; + # 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 + ] + ++ 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 + ''; + + 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; }; - inherit src; - - depsBuildBuild = [ buildPackages.stdenv.cc ]; - nativeBuildInputs = [ - bison - flex - perl - bc - nettools - 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 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=-@"] + # 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 - installFlags = [ - "INSTALL_PATH=$(out)" - ] ++ (optional isModular "INSTALL_MOD_PATH=$(out)") - ++ optionals buildDTBs ["dtbs_install" "INSTALL_DTBS_PATH=$(out)/dtbs"]; + stdenv.mkDerivation ( + builtins.foldl' lib.recursiveUpdate { } [ + (drvAttrs config stdenv.hostPlatform.linux-kernel kernelPatches configfile) + { + inherit pname version; - dontStrip = true; + enableParallelBuilding = 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 - ''; - - 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 + hardeningDisable = [ + "bindnow" + "format" + "fortify" + "stackprotector" + "pic" + "pie" ]; - 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 + makeFlags = [ + "O=$(buildRoot)" + ] ++ commonMakeFlags; -stdenv.mkDerivation ( - builtins.foldl' lib.recursiveUpdate {} [ - (drvAttrs config stdenv.hostPlatform.linux-kernel kernelPatches configfile) - { - inherit pname version; + passthru = { inherit commonMakeFlags; }; - 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; }) - ] -)) + karch = stdenv.hostPlatform.linuxArch; + } + (optionalAttrs (pos != null) { inherit pos; }) + ] + ) +) diff --git a/pkgs/rootfs/default.nix b/pkgs/rootfs/default.nix deleted file mode 100644 index d3c39c3..0000000 --- a/pkgs/rootfs/default.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ - pkgs, - stdenvNoCC, - patosPkgs, - version, - ... -}: -let - pname = "patos-rootfs"; - defaultPassword = "patos"; -in -stdenvNoCC.mkDerivation (finalAttrs: { - inherit version; - inherit pname; - inherit defaultPassword; - - buildInputs = with pkgs; [ - glibc - binutils - ]; - - glibcPatos = patosPkgs.glibc.out; - systemd = patosPkgs.systemd.out; - dbusBroker = patosPkgs.dbus-broker.out; - kernel = patosPkgs.kernel; - busybox = patosPkgs.busybox.out; - kmodLibs = pkgs.kmod.lib; - kmodBin = pkgs.kmod.out; - cacert = pkgs.cacert.out; - libbpf = pkgs.libbpf.out; - btrfs = pkgs.btrfs-progs.out; - tpm2Libs = patosPkgs.tpm2-tss.out; - kexec = patosPkgs.kexec.out; - lvm2 = patosPkgs.lvm2.out; - openssl = patosPkgs.openssl.out; - - builder = ./mkrootfs.sh; -}) diff --git a/pkgs/rootfs/mkinitrd.nix b/pkgs/rootfs/mkinitrd.nix index 3708483..8eb721e 100644 --- a/pkgs/rootfs/mkinitrd.nix +++ b/pkgs/rootfs/mkinitrd.nix @@ -1,23 +1,66 @@ { pkgs, - stdenvNoCC, patosPkgs, - version, + runCommand, ... }: -let - pname = "patos-ramdisk"; -in -stdenvNoCC.mkDerivation (finalAttrs: { - inherit version; - inherit pname; - +runCommand "patos-initrd" { buildInputs = with pkgs; [ cpio xz ]; +} +'' +echo "Building initram disk" +mkdir -p $out/root +pushd $out/root - rootfs = patosPkgs.rootfs.out; +### copy rootfs +cp -prP ${patosPkgs.rootfs}/* . +find . -type d -exec chmod 755 {} \; +mkdir sysroot - builder = ./mkinitrd.sh; -}) +### create directories +ln -sf ../usr/lib/systemd/systemd init + +### Create needed files +echo patos > ./etc/hostname + +ln -sf /etc/os-release ./etc/initrd-release + +# set default target to initrd inside initrd +ln -sf initrd.target ./usr/lib/systemd/system/default.target + +# bind mount /run to /sysroot/run +cat <<EOF > ./usr/lib/systemd/system/sysroot-run.mount +[Unit] +Before=initrd-fs.target +DefaultDependencies=false + +[Mount] +Options=bind +What=/run +Where=/sysroot/run +EOF +mkdir ./usr/lib/systemd/system/initrd-fs.target.requires/ +ln -sf ../sysroot-run.mount ./usr/lib/systemd/system/initrd-fs.target.requires/sysroot-run.mount + +# repart: generate crypttab and fstab under /run +mkdir ./usr/lib/systemd/system/systemd-repart.service.d +cat <<EOF > ./usr/lib/systemd/system/systemd-repart.service.d/override.conf +[Unit] +After=sysroot-run.mount +Requires=sysroot-run.mount + +[Service] +Environment=SYSTEMD_REPART_MKFS_OPTIONS_BTRFS=--nodiscard +ExecStart= +ExecStart=systemd-repart --dry-run=no --generate-crypttab=/run/crypttab --generate-fstab=/run/fstab +EOF + +# gen initrd +find . -print0 | cpio --null --owner=root:root -o --format=newc | xz -9 --check=crc32 > ../initrd.xz + +popd +rm -rf $out/root +'' diff --git a/pkgs/rootfs/mkinitrd.sh b/pkgs/rootfs/mkinitrd.sh deleted file mode 100644 index 43708d0..0000000 --- a/pkgs/rootfs/mkinitrd.sh +++ /dev/null @@ -1,53 +0,0 @@ -set -ex -p pipefail -echo "Building initram disk" -mkdir -p $out/root -pushd $out/root - -### copy rootfs -cp -prP $rootfs/* . -find . -type d -exec chmod 755 {} \; -mkdir sysroot - -### create directories -ln -sf ../usr/lib/systemd/systemd init - -### Create needed files -echo patos > ./etc/hostname - -ln -sf /etc/os-release ./etc/initrd-release - -# set default target to initrd inside initrd -ln -sf initrd.target ./usr/lib/systemd/system/default.target - -# bind mount /run to /sysroot/run -cat <<EOF > ./usr/lib/systemd/system/sysroot-run.mount -[Unit] -Before=initrd-fs.target -DefaultDependencies=false - -[Mount] -Options=bind -What=/run -Where=/sysroot/run -EOF -mkdir ./usr/lib/systemd/system/initrd-fs.target.requires/ -ln -sf ../sysroot-run.mount ./usr/lib/systemd/system/initrd-fs.target.requires/sysroot-run.mount - -# repart: generate crypttab and fstab under /run -mkdir ./usr/lib/systemd/system/systemd-repart.service.d -cat <<EOF > ./usr/lib/systemd/system/systemd-repart.service.d/override.conf -[Unit] -After=sysroot-run.mount -Requires=sysroot-run.mount - -[Service] -Environment=SYSTEMD_REPART_MKFS_OPTIONS_BTRFS=--nodiscard -ExecStart= -ExecStart=systemd-repart --dry-run=no --generate-crypttab=/run/crypttab --generate-fstab=/run/fstab -EOF - -# gen initrd -find . -print0 | cpio --null --owner=root:root -o --format=newc | xz -9 --check=crc32 > ../initrd.xz - -popd -rm -rf $out/root diff --git a/pkgs/rootfs/mkrootfs.sh b/pkgs/rootfs/mkrootfs.nix similarity index 75% rename from pkgs/rootfs/mkrootfs.sh rename to pkgs/rootfs/mkrootfs.nix index 3ccc93c..fb25c4d 100644 --- a/pkgs/rootfs/mkrootfs.sh +++ b/pkgs/rootfs/mkrootfs.nix @@ -1,5 +1,22 @@ -set -ex -o pipefail +{ + pkgs, + patosPkgs, + version, + runCommand, + ... +}: +let + defaultPassword = "patos"; +in +runCommand "patos-rootfs" +{ + buildInputs = [ + pkgs.glibc + pkgs.binutils + ]; +} +'' ### create directory structure mkdir -p $out/etc/repart.d $out/dev $out/proc $out/sys \ $out/tmp $out/root $out/run $out/boot $out/mnt $out/home $out/srv $out/var/tmp @@ -11,7 +28,7 @@ ln -sf ../proc/self/mounts $out/etc/mtab ### install systemd echo "Installing systemd" -cp -Pr $systemd/* $out/ +cp -Pr ${patosPkgs.systemd}/* $out/ find $out -type d -exec chmod 755 {} \; rm -rf $out/usr/include rm -rf $out/usr/sbin @@ -117,57 +134,57 @@ ManagerEnvironment=PATH=/bin:/sbin:/usr/bin SYSTEMD_CRYPTTAB=/run/crypttab SYSTE EOF ### install PatOS glibc -cp -P $glibcPatos/lib/*.so* $out/usr/lib/ +cp -P ${patosPkgs.glibc}/lib/*.so* $out/usr/lib/ ### install openssl -cp -P $openssl/lib/*.so* $out/usr/lib/ -cp -Pr $openssl/etc/ssl $out/etc/ +cp -P ${patosPkgs.openssl}/lib/*.so* $out/usr/lib/ +cp -Pr ${patosPkgs.openssl}/etc/ssl $out/etc/ ### install busybox -cp $busybox/bin/busybox $out/usr/bin/ +cp ${patosPkgs.busybox}/bin/busybox $out/usr/bin/ $out/usr/bin/busybox --list | xargs -I {} ln -sf busybox $out/usr/bin/{} ### install dbus broker -cp -r $dbusBroker/* $out/ +cp -r ${patosPkgs.dbus-broker}/* $out/ ### install kexec -cp -Pr ${kexec}/sbin/kexec $out/usr/bin/ +cp -Pr ${patosPkgs.kexec}/sbin/kexec $out/usr/bin/ ### install dmsetup udev rules -cp -P ${lvm2}/usr/bin/dmsetup $out/usr/bin/ -cp -P ${lvm2}/lib/libdevmapper.so* $out/usr/lib/ -cp -P ${lvm2}/lib/udev/rules.d/* $out/usr/lib/udev/rules.d/ +cp -P ${patosPkgs.lvm2}/usr/bin/dmsetup $out/usr/bin/ +cp -P ${patosPkgs.lvm2}/lib/libdevmapper.so* $out/usr/lib/ +cp -P ${patosPkgs.lvm2}/lib/udev/rules.d/* $out/usr/lib/udev/rules.d/ ### install btrfs progs -cp -Pr ${btrfs}/bin/* $out/usr/bin/ -cp -Pr ${btrfs}/lib/* $out/usr/lib/ +cp -Pr ${pkgs.btrfs-progs}/bin/* $out/usr/bin/ +cp -Pr ${pkgs.btrfs-progs}/lib/* $out/usr/lib/ ### install tpm2 libs -cp -P ${tpm2Libs}/lib/*.so* $out/usr/lib/ +cp -P ${patosPkgs.tpm2-tss}/lib/*.so* $out/usr/lib/ ### install lib kmod -cp -P $kmodLibs/lib/*.so* $out/usr/lib/ -cp -P $kmodBin/bin/* $out/usr/bin +cp -P ${pkgs.kmod.lib}/lib/*.so* $out/usr/lib/ +cp -P ${pkgs.kmod}/bin/* $out/usr/bin ### install libbpf -cp -P $libbpf/lib/libbpf*.so* $out/usr/lib +cp -P ${pkgs.libbpf}/lib/libbpf*.so* $out/usr/lib ### install ca cert bundle chmod 755 $out/etc/ssl $out/etc/ssl/certs -cp -P $cacert/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/cert.pem +cp -P ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/cert.pem ln -sf ../cert.pem $out/etc/ssl/certs/ca-certificates.crt ln -sf ../cert.pem $out/etc/ssl/certs/ca-bundle.crt # setup default files -$systemd/usr/bin/systemd-hwdb --root=$out --usr update -$systemd/usr/bin/systemd-tmpfiles --root=$out $out/usr/lib/tmpfiles.d/etc.conf --create +${patosPkgs.systemd}/usr/bin/systemd-hwdb --root=$out --usr update +${patosPkgs.systemd}/usr/bin/systemd-tmpfiles --root=$out $out/usr/lib/tmpfiles.d/etc.conf --create cp $out/usr/share/factory/etc/nsswitch.conf $out/etc/ cp $out/usr/share/factory/etc/locale.conf $out/etc/ cp $out/usr/share/factory/etc/vconsole.conf $out/etc/ # install sys users mkdir creds -echo -n $defaultPassword > creds/passwd.plaintext-password.root -CREDENTIALS_DIRECTORY=$PWD/creds SYSTEMD_CRYPT_PREFIX='$6$' $systemd/usr/bin/systemd-sysusers --root=$out rootfs/usr/lib/sysusers.d/*.conf +echo -n ${defaultPassword} > creds/passwd.plaintext-password.root +CREDENTIALS_DIRECTORY=$PWD/creds SYSTEMD_CRYPT_PREFIX='$6$' ${patosPkgs.systemd}/usr/bin/systemd-sysusers --root=$out rootfs/usr/lib/sysusers.d/*.conf chmod 600 $out/etc/shadow rm -rf creds @@ -195,6 +212,6 @@ find $out -type f -executable -exec strip {} \; find $out -type d -exec chmod 755 {} \; ### install kernel modules -cp -r $kernel/lib/modules $out/usr/lib/ +cp -r ${patosPkgs.kernel}/lib/modules $out/usr/lib/ find $out/usr/lib/modules -type d -exec chmod 755 {} \; - +''