From fc6cf6169868e60c189e4b243330c3717ff159f3 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Thu, 26 May 2022 13:58:07 +0200 Subject: ... --- modules/stage-1/default.nix | 669 -------------------------------------------- 1 file changed, 669 deletions(-) delete mode 100644 modules/stage-1/default.nix (limited to 'modules/stage-1/default.nix') diff --git a/modules/stage-1/default.nix b/modules/stage-1/default.nix deleted file mode 100644 index 5d49475f..00000000 --- a/modules/stage-1/default.nix +++ /dev/null @@ -1,669 +0,0 @@ -# This module builds the initial ramdisk, which contains an init -# script that performs the first stage of booting the system: it loads -# the modules necessary to mount the root file system, then calls the -# init in the root file system to start the second boot stage. - -{ config, lib, utils, pkgs, ... }: - -with lib; - -let - - udev = config.systemd.package; - - kernel-name = config.boot.kernelPackages.kernel.name or "kernel"; - - modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; }; - firmware = config.hardware.firmware; - - - # Determine the set of modules that we need to mount the root FS. - modulesClosure = pkgs.makeModulesClosure { - rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules; - kernel = modulesTree; - firmware = firmware; - allowMissing = false; - }; - - - # The initrd only has to mount `/` or any FS marked as necessary for - # booting (such as the FS containing `/nix/store`, or an FS needed for - # mounting `/`, like `/` on a loopback). - fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems; - - # A utility for enumerating the shared-library dependencies of a program - findLibs = pkgs.buildPackages.writeShellScriptBin "find-libs" '' - set -euo pipefail - - declare -A seen - left=() - - patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf" - - function add_needed { - rpath="$($patchelf --print-rpath $1)" - dir="$(dirname $1)" - for lib in $($patchelf --print-needed $1); do - left+=("$lib" "$rpath" "$dir") - done - } - - add_needed "$1" - - while [ ''${#left[@]} -ne 0 ]; do - next=''${left[0]} - rpath=''${left[1]} - ORIGIN=''${left[2]} - left=("''${left[@]:3}") - if [ -z ''${seen[$next]+x} ]; then - seen[$next]=1 - - # Ignore the dynamic linker which for some reason appears as a DT_NEEDED of glibc but isn't in glibc's RPATH. - case "$next" in - ld*.so.?) continue;; - esac - - IFS=: read -ra paths <<< $rpath - res= - for path in "''${paths[@]}"; do - path=$(eval "echo $path") - if [ -f "$path/$next" ]; then - res="$path/$next" - echo "$res" - add_needed "$res" - break - fi - done - if [ -z "$res" ]; then - echo "Couldn't satisfy dependency $next" >&2 - exit 1 - fi - fi - done - ''; - - # Some additional utilities needed in stage 1, like mount, lvm, fsck - # etc. We don't want to bring in all of those packages, so we just - # copy what we need. Instead of using statically linked binaries, - # we just copy what we need from Glibc and use patchelf to make it - # work. - extraUtils = pkgs.runCommandCC "extra-utils" - { nativeBuildInputs = [pkgs.buildPackages.nukeReferences]; - allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd - } - '' - set +o pipefail - - mkdir -p $out/bin $out/lib - ln -s $out/bin $out/sbin - - copy_bin_and_libs () { - [ -f "$out/bin/$(basename $1)" ] && rm "$out/bin/$(basename $1)" - cp -pdv $1 $out/bin - } - - # Copy BusyBox. - for BIN in ${pkgs.busybox}/{s,}bin/*; do - copy_bin_and_libs $BIN - done - - # Copy some util-linux stuff. - copy_bin_and_libs ${pkgs.util-linux}/sbin/blkid - - # Copy dmsetup and lvm. - copy_bin_and_libs ${getBin pkgs.lvm2}/bin/dmsetup - copy_bin_and_libs ${getBin pkgs.lvm2}/bin/lvm - - # Add RAID mdadm tool. - copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm - copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon - - # Copy udev. - copy_bin_and_libs ${udev}/bin/udevadm - copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl - for BIN in ${udev}/lib/udev/*_id; do - copy_bin_and_libs $BIN - done - # systemd-udevd is only a symlink to udevadm these days - ln -sf udevadm $out/bin/systemd-udevd - - # Copy modprobe. - copy_bin_and_libs ${pkgs.kmod}/bin/kmod - ln -sf kmod $out/bin/modprobe - - # Copy resize2fs if any ext* filesystems are to be resized - ${optionalString (any (fs: fs.autoResize && (lib.hasPrefix "ext" fs.fsType)) fileSystems) '' - # We need mke2fs in the initrd. - copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs - ''} - - # Copy secrets if needed. - # - # TODO: move out to a separate script; see #85000. - ${optionalString (!config.boot.loader.supportsInitrdSecrets) - (concatStringsSep "\n" (mapAttrsToList (dest: source: - let source' = if source == null then dest else source; in - '' - mkdir -p $(dirname "$out/secrets/${dest}") - # Some programs (e.g. ssh) doesn't like secrets to be - # symlinks, so we use `cp -L` here to match the - # behaviour when secrets are natively supported. - cp -Lr ${source'} "$out/secrets/${dest}" - '' - ) config.boot.initrd.secrets)) - } - - ${config.boot.initrd.extraUtilsCommands} - - # Copy ld manually since it isn't detected correctly - cp -pv ${pkgs.stdenv.cc.libc.out}/lib/ld*.so.? $out/lib - - # Copy all of the needed libraries - find $out/bin $out/lib -type f | while read BIN; do - echo "Copying libs for executable $BIN" - for LIB in $(${findLibs}/bin/find-libs $BIN); do - TGT="$out/lib/$(basename $LIB)" - if [ ! -f "$TGT" ]; then - SRC="$(readlink -e $LIB)" - cp -pdv "$SRC" "$TGT" - fi - done - done - - # Strip binaries further than normal. - chmod -R u+w $out - stripDirs "$STRIP" "lib bin" "-s" - - # Run patchelf to make the programs refer to the copied libraries. - find $out/bin $out/lib -type f | while read i; do - if ! test -L $i; then - nuke-refs -e $out $i - fi - done - - find $out/bin -type f | while read i; do - if ! test -L $i; then - echo "patching $i..." - patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true - fi - done - - if [ -z "${toString (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform)}" ]; then - # Make sure that the patchelf'ed binaries still work. - echo "testing patched programs..." - $out/bin/ash -c 'echo hello world' | grep "hello world" - export LD_LIBRARY_PATH=$out/lib - $out/bin/mount --help 2>&1 | grep -q "BusyBox" - $out/bin/blkid -V 2>&1 | grep -q 'libblkid' - $out/bin/udevadm --version - $out/bin/dmsetup --version 2>&1 | tee -a log | grep -q "version:" - LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep -q "LVM" - $out/bin/mdadm --version - - ${config.boot.initrd.extraUtilsCommandsTest} - fi - ''; # */ - - - # Networkd link files are used early by udev to set up interfaces early. - # This must be done in stage 1 to avoid race conditions between udev and - # network daemons. - linkUnits = pkgs.runCommand "link-units" { - allowedReferences = [ extraUtils ]; - preferLocalBuild = true; - } ('' - mkdir -p $out - cp -v ${udev}/lib/systemd/network/*.link $out/ - '' + ( - let - links = filterAttrs (n: v: hasSuffix ".link" n) config.systemd.network.units; - files = mapAttrsToList (n: v: "${v.unit}/${n}") links; - in - concatMapStringsSep "\n" (file: "cp -v ${file} $out/") files - )); - - udevRules = pkgs.runCommand "udev-rules" { - allowedReferences = [ extraUtils ]; - preferLocalBuild = true; - } '' - mkdir -p $out - - echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules - - cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/ - cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/ - cp -v ${udev}/lib/udev/rules.d/75-net-description.rules $out/ - cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/ - cp -v ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/ - cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/ - ${config.boot.initrd.extraUdevRulesCommands} - - for i in $out/*.rules; do - substituteInPlace $i \ - --replace ata_id ${extraUtils}/bin/ata_id \ - --replace scsi_id ${extraUtils}/bin/scsi_id \ - --replace cdrom_id ${extraUtils}/bin/cdrom_id \ - --replace ${pkgs.coreutils}/bin/basename ${extraUtils}/bin/basename \ - --replace ${pkgs.util-linux}/bin/blkid ${extraUtils}/bin/blkid \ - --replace ${getBin pkgs.lvm2}/bin ${extraUtils}/bin \ - --replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \ - --replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \ - --replace ${udev} ${extraUtils} - done - - # Work around a bug in QEMU, which doesn't implement the "READ - # DISC INFORMATION" SCSI command: - # https://bugzilla.redhat.com/show_bug.cgi?id=609049 - # As a result, `cdrom_id' doesn't print - # ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the - # /dev/disk/by-label symlinks from being created. We need these - # in the NixOS installation CD, so use ID_CDROM_MEDIA in the - # corresponding udev rules for now. This was the behaviour in - # udev <= 154. See also - # http://www.spinics.net/lists/hotplug/msg03935.html - substituteInPlace $out/60-persistent-storage.rules \ - --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA - ''; # */ - - - # The init script of boot stage 1 (loading kernel modules for - # mounting the root FS). - bootStage1 = pkgs.substituteAll { - src = ./stage-1-init.sh; - - shell = "${extraUtils}/bin/ash"; - - isExecutable = true; - - postInstall = '' - echo checking syntax - # check both with bash - ${pkgs.buildPackages.bash}/bin/sh -n $target - # and with ash shell, just in case - ${pkgs.buildPackages.busybox}/bin/ash -n $target - ''; - - inherit linkUnits udevRules extraUtils modulesClosure; - - inherit (config.boot) resumeDevice; - - inherit (config.system.build) earlyMountScript; - - inherit (config.boot.initrd) checkJournalingFS verbose - preLVMCommands preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules; - - resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}") - (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption.enable - # Don't include zram devices - && !(hasPrefix "/dev/zram" sd.device) - ) config.swapDevices); - - fsInfo = - let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType (builtins.concatStringsSep "," fs.options) ]; - in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems)); - - setHostId = optionalString (config.networking.hostId != null) '' - hi="${config.networking.hostId}" - ${if pkgs.stdenv.isBigEndian then '' - echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > /etc/hostid - '' else '' - echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > /etc/hostid - ''} - ''; - }; - - - # The closure of the init script of boot stage 1 is what we put in - # the initial RAM disk. - initialRamdisk = pkgs.makeInitrd { - name = "initrd-${kernel-name}"; - inherit (config.boot.initrd) compressor compressorArgs prepend; - - contents = - [ { object = bootStage1; - symlink = "/init"; - } - { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf; - symlink = "/etc/mdadm.conf"; - } - { object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" { - src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf"; - preferLocalBuild = true; - } '' - target=$out - ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out - ''; - symlink = "/etc/modprobe.d/ubuntu.conf"; - } - { object = pkgs.kmod-debian-aliases; - symlink = "/etc/modprobe.d/debian.conf"; - } - ]; - }; - - # Script to add secret files to the initrd at bootloader update time - initialRamdiskSecretAppender = - let - compressorExe = initialRamdisk.compressorExecutableFunction pkgs; - in pkgs.writeScriptBin "append-initrd-secrets" - '' - #!${pkgs.bash}/bin/bash -e - function usage { - echo "USAGE: $0 INITRD_FILE" >&2 - echo "Appends this configuration's secrets to INITRD_FILE" >&2 - } - - if [ $# -ne 1 ]; then - usage - exit 1 - fi - - if [ "$1"x = "--helpx" ]; then - usage - exit 0 - fi - - ${lib.optionalString (config.boot.initrd.secrets == {}) - "exit 0"} - - export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin - - function cleanup { - if [ -n "$tmp" -a -d "$tmp" ]; then - rm -fR "$tmp" - fi - } - trap cleanup EXIT - - tmp=$(mktemp -d initrd-secrets.XXXXXXXXXX) - - ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source: - let source' = if source == null then dest else toString source; in - '' - mkdir -p $(dirname "$tmp/${dest}") - cp -aL ${source'} "$tmp/${dest}" - '' - ) config.boot.initrd.secrets) - } - - (cd "$tmp" && find . -print0 | sort -z | cpio --quiet -o -H newc -R +0:+0 --reproducible --null) | \ - ${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1" - ''; - -in - -{ - disabledModules = [ "system/boot/stage-1.nix" ]; - - options = { - - boot.resumeDevice = mkOption { - type = types.str; - default = ""; - example = "/dev/sda3"; - description = '' - Device for manual resume attempt during boot. This should be used primarily - if you want to resume from file. If left empty, the swap partitions are used. - Specify here the device where the file resides. - You should also use boot.kernelParams to specify - resume_offset. - ''; - }; - - boot.initrd.enable = mkOption { - type = types.bool; - default = !config.boot.isContainer; - defaultText = "!config.boot.isContainer"; - description = '' - Whether to enable the NixOS initial RAM disk (initrd). This may be - needed to perform some initialisation tasks (like mounting - network/encrypted file systems) before continuing the boot process. - ''; - }; - - boot.initrd.prepend = mkOption { - default = [ ]; - type = types.listOf types.str; - description = '' - Other initrd files to prepend to the final initrd we are building. - ''; - }; - - boot.initrd.checkJournalingFS = mkOption { - default = true; - type = types.bool; - description = '' - Whether to run fsck on journaling filesystems such as ext3. - ''; - }; - - boot.initrd.mdadmConf = mkOption { - default = ""; - type = types.lines; - description = '' - Contents of /etc/mdadm.conf in stage 1. - ''; - }; - - boot.initrd.preLVMCommands = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands to be executed immediately before LVM discovery. - ''; - }; - - boot.initrd.preDeviceCommands = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands to be executed before udev is started to create - device nodes. - ''; - }; - - boot.initrd.postDeviceCommands = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands to be executed immediately after stage 1 of the - boot has loaded kernel modules and created device nodes in - /dev. - ''; - }; - - boot.initrd.postMountCommands = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands to be executed immediately after the stage 1 - filesystems have been mounted. - ''; - }; - - boot.initrd.preFailCommands = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands to be executed before the failure prompt is shown. - ''; - }; - - boot.initrd.extraUtilsCommands = mkOption { - internal = true; - default = ""; - type = types.lines; - description = '' - Shell commands to be executed in the builder of the - extra-utils derivation. This can be used to provide - additional utilities in the initial ramdisk. - ''; - }; - - boot.initrd.extraUtilsCommandsTest = mkOption { - internal = true; - default = ""; - type = types.lines; - description = '' - Shell commands to be executed in the builder of the - extra-utils derivation after patchelf has done its - job. This can be used to test additional utilities - copied in extraUtilsCommands. - ''; - }; - - boot.initrd.extraUdevRulesCommands = mkOption { - internal = true; - default = ""; - type = types.lines; - description = '' - Shell commands to be executed in the builder of the - udev-rules derivation. This can be used to add - additional udev rules in the initial ramdisk. - ''; - }; - - boot.initrd.compressor = mkOption { - default = ( - if lib.versionAtLeast config.boot.kernelPackages.kernel.version "5.9" - then "zstd" - else "gzip" - ); - defaultText = "zstd if the kernel supports it (5.9+), gzip if not."; - type = types.unspecified; # We don't have a function type... - description = '' - The compressor to use on the initrd image. May be any of: - - - The name of one of the predefined compressors, see pkgs/build-support/kernel/initrd-compressor-meta.nix for the definitions. - A function which, given the nixpkgs package set, returns the path to a compressor tool, e.g. pkgs: "''${pkgs.pigz}/bin/pigz" - (not recommended, because it does not work when cross-compiling) the full path to a compressor tool, e.g. "''${pkgs.pigz}/bin/pigz" - - - The given program should read data from stdin and write it to stdout compressed. - ''; - example = "xz"; - }; - - boot.initrd.compressorArgs = mkOption { - default = null; - type = types.nullOr (types.listOf types.str); - description = "Arguments to pass to the compressor for the initrd image, or null to use the compressor's defaults."; - }; - - boot.initrd.secrets = mkOption - { default = {}; - type = types.attrsOf (types.nullOr types.path); - description = - '' - Secrets to append to the initrd. The attribute name is the - path the secret should have inside the initrd, the value - is the path it should be copied from (or null for the same - path inside and out). - ''; - example = literalExpression - '' - { "/etc/dropbear/dropbear_rsa_host_key" = - ./secret-dropbear-key; - } - ''; - }; - - boot.initrd.supportedFilesystems = mkOption { - default = [ ]; - example = [ "btrfs" ]; - type = types.listOf types.str; - description = "Names of supported filesystem types in the initial ramdisk."; - }; - - boot.initrd.verbose = mkOption { - default = true; - type = types.bool; - description = - '' - Verbosity of the initrd. Please note that disabling verbosity removes - only the mandatory messages generated by the NixOS scripts. For a - completely silent boot, you might also want to set the two following - configuration options: - - - boot.consoleLogLevel = 0; - boot.kernelParams = [ "quiet" "udev.log_priority=3" ]; - - ''; - }; - - boot.loader.supportsInitrdSecrets = mkOption - { internal = true; - default = false; - type = types.bool; - description = - '' - Whether the bootloader setup runs append-initrd-secrets. - If not, any needed secrets must be copied into the initrd - and thus added to the store. - ''; - }; - - fileSystems = mkOption { - type = with lib.types; attrsOf (submodule { - options.neededForBoot = mkOption { - default = false; - type = types.bool; - description = '' - If set, this file system will be mounted in the initial ramdisk. - Note that the file system will always be mounted in the initial - ramdisk if its mount point is one of the following: - ${concatStringsSep ", " ( - forEach utils.pathsNeededForBoot (i: "${i}") - )}. - ''; - }; - }); - }; - - }; - - config = mkIf config.boot.initrd.enable { - assertions = [ - { assertion = any (fs: fs.mountPoint == "/") fileSystems; - message = "The ‘fileSystems’ option does not specify your root file system."; - } - { assertion = let inherit (config.boot) resumeDevice; in - resumeDevice == "" || builtins.substring 0 1 resumeDevice == "/"; - message = "boot.resumeDevice has to be an absolute path." - + " Old \"x:y\" style is no longer supported."; - } - # TODO: remove when #85000 is fixed - { assertion = !config.boot.loader.supportsInitrdSecrets -> - all (source: - builtins.isPath source || - (builtins.isString source && hasPrefix builtins.storeDir source)) - (attrValues config.boot.initrd.secrets); - message = '' - boot.loader.initrd.secrets values must be unquoted paths when - using a bootloader that doesn't natively support initrd - secrets, e.g.: - - boot.initrd.secrets = { - "/etc/secret" = /path/to/secret; - }; - - Note that this will result in all secrets being stored - world-readable in the Nix store! - ''; - } - ]; - - system.build = - { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; }; - - system.requiredKernelConfig = with config.lib.kernelConfig; [ - (isYes "TMPFS") - (isYes "BLK_DEV_INITRD") - ]; - - boot.initrd.supportedFilesystems = map (fs: fs.fsType) fileSystems; - - }; -} -- cgit v1.2.3