Merge #361424: refactor lib.packagesFromDirectoryRecursive (v2)
[NixPkgs.git] / nixos / modules / system / boot / stage-1.nix
blob280d38ce32f48664cced143a5fc7151571534e07
1 # This module builds the initial ramdisk, which contains an init
2 # script that performs the first stage of booting the system: it loads
3 # the modules necessary to mount the root file system, then calls the
4 # init in the root file system to start the second boot stage.
6 { config, options, lib, utils, pkgs, ... }:
8 with lib;
10 let
12   udev = config.systemd.package;
14   kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
16   # Determine the set of modules that we need to mount the root FS.
17   modulesClosure = pkgs.makeModulesClosure {
18     rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
19     kernel = config.system.modulesTree;
20     firmware = config.hardware.firmware;
21     allowMissing = false;
22   };
25   # The initrd only has to mount `/` or any FS marked as necessary for
26   # booting (such as the FS containing `/nix/store`, or an FS needed for
27   # mounting `/`, like `/` on a loopback).
28   fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
30   # Determine whether zfs-mount(8) is needed.
31   zfsRequiresMountHelper = any (fs: lib.elem "zfsutil" fs.options) fileSystems;
33   # A utility for enumerating the shared-library dependencies of a program
34   findLibs = pkgs.buildPackages.writeShellScriptBin "find-libs" ''
35     set -euo pipefail
37     declare -A seen
38     left=()
40     patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
42     function add_needed {
43       rpath="$($patchelf --print-rpath $1)"
44       dir="$(dirname $1)"
45       for lib in $($patchelf --print-needed $1); do
46         left+=("$lib" "$rpath" "$dir")
47       done
48     }
50     add_needed "$1"
52     while [ ''${#left[@]} -ne 0 ]; do
53       next=''${left[0]}
54       rpath=''${left[1]}
55       ORIGIN=''${left[2]}
56       left=("''${left[@]:3}")
57       if [ -z ''${seen[$next]+x} ]; then
58         seen[$next]=1
60         # Ignore the dynamic linker which for some reason appears as a DT_NEEDED of glibc but isn't in glibc's RPATH.
61         case "$next" in
62           ld*.so.?) continue;;
63         esac
65         IFS=: read -ra paths <<< $rpath
66         res=
67         for path in "''${paths[@]}"; do
68           path=$(eval "echo $path")
69           if [ -f "$path/$next" ]; then
70               res="$path/$next"
71               echo "$res"
72               add_needed "$res"
73               break
74           fi
75         done
76         if [ -z "$res" ]; then
77           echo "Couldn't satisfy dependency $next" >&2
78           exit 1
79         fi
80       fi
81     done
82   '';
84   # Some additional utilities needed in stage 1, like mount, lvm, fsck
85   # etc.  We don't want to bring in all of those packages, so we just
86   # copy what we need.  Instead of using statically linked binaries,
87   # we just copy what we need from Glibc and use patchelf to make it
88   # work.
89   extraUtils = pkgs.runCommand "extra-utils"
90     { nativeBuildInputs = with pkgs.buildPackages; [ nukeReferences bintools ];
91       allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
92     }
93     ''
94       set +o pipefail
96       mkdir -p $out/bin $out/lib
97       ln -s $out/bin $out/sbin
99       copy_bin_and_libs () {
100         [ -f "$out/bin/$(basename $1)" ] && rm "$out/bin/$(basename $1)"
101         cp -pdv $1 $out/bin
102       }
104       # Copy BusyBox.
105       for BIN in ${pkgs.busybox}/{s,}bin/*; do
106         copy_bin_and_libs $BIN
107       done
109       ${optionalString zfsRequiresMountHelper ''
110         # Filesystems using the "zfsutil" option are mounted regardless of the
111         # mount.zfs(8) helper, but it is required to ensure that ZFS properties
112         # are used as mount options.
113         #
114         # BusyBox does not use the ZFS helper in the first place.
115         # util-linux searches /sbin/ as last path for helpers (stage-1-init.sh
116         # must symlink it to the store PATH).
117         # Without helper program, both `mount`s silently fails back to internal
118         # code, using default options and effectively ignore security relevant
119         # ZFS properties such as `setuid=off` and `exec=off` (unless manually
120         # duplicated in `fileSystems.*.options`, defeating "zfsutil"'s purpose).
121         copy_bin_and_libs ${lib.getOutput "mount" pkgs.util-linux}/bin/mount
122         copy_bin_and_libs ${config.boot.zfs.package}/bin/mount.zfs
123       ''}
125       # Copy some util-linux stuff.
126       copy_bin_and_libs ${pkgs.util-linux}/sbin/blkid
128       # Copy dmsetup and lvm.
129       copy_bin_and_libs ${getBin pkgs.lvm2}/bin/dmsetup
130       copy_bin_and_libs ${getBin pkgs.lvm2}/bin/lvm
132       # Copy udev.
133       copy_bin_and_libs ${udev}/bin/udevadm
134       cp ${lib.getLib udev.kmod}/lib/libkmod.so* $out/lib
135       copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
136       for BIN in ${udev}/lib/udev/*_id; do
137         copy_bin_and_libs $BIN
138       done
139       # systemd-udevd is only a symlink to udevadm these days
140       ln -sf udevadm $out/bin/systemd-udevd
142       # Copy modprobe.
143       copy_bin_and_libs ${pkgs.kmod}/bin/kmod
144       ln -sf kmod $out/bin/modprobe
146       # Copy multipath.
147       ${optionalString config.services.multipath.enable ''
148         copy_bin_and_libs ${config.services.multipath.package}/bin/multipath
149         copy_bin_and_libs ${config.services.multipath.package}/bin/multipathd
150         # Copy lib/multipath manually.
151         cp -rpv ${config.services.multipath.package}/lib/multipath $out/lib
152       ''}
154       # Copy secrets if needed.
155       #
156       # TODO: move out to a separate script; see #85000.
157       ${optionalString (!config.boot.loader.supportsInitrdSecrets)
158           (concatStringsSep "\n" (mapAttrsToList (dest: source:
159              let source' = if source == null then dest else source; in
160                ''
161                   mkdir -p $(dirname "$out/secrets/${dest}")
162                   # Some programs (e.g. ssh) doesn't like secrets to be
163                   # symlinks, so we use `cp -L` here to match the
164                   # behaviour when secrets are natively supported.
165                   cp -Lr ${source'} "$out/secrets/${dest}"
166                 ''
167           ) config.boot.initrd.secrets))
168        }
170       ${config.boot.initrd.extraUtilsCommands}
172       # Copy ld manually since it isn't detected correctly
173       cp -pv ${pkgs.stdenv.cc.libc.out}/lib/ld*.so.? $out/lib
175       # Copy all of the needed libraries in a consistent order so
176       # duplicates are resolved the same way.
177       find $out/bin $out/lib -type f | sort | while read BIN; do
178         echo "Copying libs for executable $BIN"
179         for LIB in $(${findLibs}/bin/find-libs $BIN); do
180           TGT="$out/lib/$(basename $LIB)"
181           if [ ! -f "$TGT" ]; then
182             SRC="$(readlink -e $LIB)"
183             cp -pdv "$SRC" "$TGT"
184           fi
185         done
186       done
188       # Strip binaries further than normal.
189       chmod -R u+w $out
190       stripDirs "$STRIP" "$RANLIB" "lib bin" "-s"
192       # Run patchelf to make the programs refer to the copied libraries.
193       find $out/bin $out/lib -type f | while read i; do
194         nuke-refs -e $out $i
195       done
197       find $out/bin -type f | while read i; do
198         echo "patching $i..."
199         patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
200       done
202       find $out/lib -type f \! -name 'ld*.so.?' | while read i; do
203         echo "patching $i..."
204         patchelf --set-rpath $out/lib $i
205       done
207       if [ -z "${toString (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform)}" ]; then
208       # Make sure that the patchelf'ed binaries still work.
209       echo "testing patched programs..."
210       $out/bin/ash -c 'echo hello world' | grep "hello world"
211       ${if zfsRequiresMountHelper then ''
212         $out/bin/mount -V 1>&1 | grep -q "mount from util-linux"
213         $out/bin/mount.zfs -h 2>&1 | grep -q "Usage: mount.zfs"
214       '' else ''
215         $out/bin/mount --help 2>&1 | grep -q "BusyBox"
216       ''}
217       $out/bin/blkid -V 2>&1 | grep -q 'libblkid'
218       $out/bin/udevadm --version
219       $out/bin/dmsetup --version 2>&1 | tee -a log | grep -q "version:"
220       LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep -q "LVM"
221       ${optionalString config.services.multipath.enable ''
222         ($out/bin/multipath || true) 2>&1 | grep -q 'need to be root'
223         ($out/bin/multipathd || true) 2>&1 | grep -q 'need to be root'
224       ''}
226       ${config.boot.initrd.extraUtilsCommandsTest}
227       fi
228     ''; # */
231   # Networkd link files are used early by udev to set up interfaces early.
232   # This must be done in stage 1 to avoid race conditions between udev and
233   # network daemons.
234   linkUnits = pkgs.runCommand "link-units" {
235       allowedReferences = [ extraUtils ];
236       preferLocalBuild = true;
237     } (''
238       mkdir -p $out
239       cp -v ${udev}/lib/systemd/network/*.link $out/
240       '' + (
241       let
242         links = filterAttrs (n: v: hasSuffix ".link" n) config.systemd.network.units;
243         files = mapAttrsToList (n: v: "${v.unit}/${n}") links;
244       in
245         concatMapStringsSep "\n" (file: "cp -v ${file} $out/") files
246       ));
248   udevRules = pkgs.runCommand "udev-rules" {
249       allowedReferences = [ extraUtils ];
250       preferLocalBuild = true;
251     } ''
252       mkdir -p $out
254       cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
255       cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
256       cp -v ${udev}/lib/udev/rules.d/75-net-description.rules $out/
257       cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
258       cp -v ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/
259       cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
260       ${config.boot.initrd.extraUdevRulesCommands}
262       for i in $out/*.rules; do
263           substituteInPlace $i \
264             --replace ata_id ${extraUtils}/bin/ata_id \
265             --replace scsi_id ${extraUtils}/bin/scsi_id \
266             --replace cdrom_id ${extraUtils}/bin/cdrom_id \
267             --replace ${pkgs.coreutils}/bin/basename ${extraUtils}/bin/basename \
268             --replace ${pkgs.util-linux}/bin/blkid ${extraUtils}/bin/blkid \
269             --replace ${getBin pkgs.lvm2}/bin ${extraUtils}/bin \
270             --replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \
271             --replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \
272             --replace ${udev} ${extraUtils}
273       done
275       # Work around a bug in QEMU, which doesn't implement the "READ
276       # DISC INFORMATION" SCSI command:
277       #   https://bugzilla.redhat.com/show_bug.cgi?id=609049
278       # As a result, `cdrom_id' doesn't print
279       # ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the
280       # /dev/disk/by-label symlinks from being created.  We need these
281       # in the NixOS installation CD, so use ID_CDROM_MEDIA in the
282       # corresponding udev rules for now.  This was the behaviour in
283       # udev <= 154.  See also
284       #   https://www.spinics.net/lists/hotplug/msg03935.html
285       substituteInPlace $out/60-persistent-storage.rules \
286         --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
287     ''; # */
290   # The init script of boot stage 1 (loading kernel modules for
291   # mounting the root FS).
292   bootStage1 = pkgs.substituteAll {
293     src = ./stage-1-init.sh;
295     shell = "${extraUtils}/bin/ash";
297     isExecutable = true;
299     postInstall = ''
300       echo checking syntax
301       # check both with bash
302       ${pkgs.buildPackages.bash}/bin/sh -n $target
303       # and with ash shell, just in case
304       ${pkgs.buildPackages.busybox}/bin/ash -n $target
305     '';
307     inherit linkUnits udevRules extraUtils;
309     inherit (config.boot) resumeDevice;
311     inherit (config.system.nixos) distroName;
313     inherit (config.system.build) earlyMountScript;
315     inherit (config.boot.initrd) checkJournalingFS verbose
316       preLVMCommands preDeviceCommands postDeviceCommands postResumeCommands postMountCommands preFailCommands kernelModules;
318     resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
319                     (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption.enable
320                              # Don't include zram devices
321                              && !(hasPrefix "/dev/zram" sd.device)
322                             ) config.swapDevices);
324     fsInfo =
325       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) ];
326       in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
328     setHostId = optionalString (config.networking.hostId != null) ''
329       hi="${config.networking.hostId}"
330       ${if pkgs.stdenv.hostPlatform.isBigEndian then ''
331         echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > /etc/hostid
332       '' else ''
333         echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > /etc/hostid
334       ''}
335     '';
336   };
339   # The closure of the init script of boot stage 1 is what we put in
340   # the initial RAM disk.
341   initialRamdisk = pkgs.makeInitrd {
342     name = "initrd-${kernel-name}";
343     inherit (config.boot.initrd) compressor compressorArgs prepend;
345     contents =
346       [ { object = bootStage1;
347           symlink = "/init";
348         }
349         { object = "${modulesClosure}/lib";
350           symlink = "/lib";
351         }
352         { object = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
353           symlink = "/etc/modprobe.d/ubuntu.conf";
354         }
355         { object = config.environment.etc."modprobe.d/nixos.conf".source;
356           symlink = "/etc/modprobe.d/nixos.conf";
357         }
358         { object = pkgs.kmod-debian-aliases;
359           symlink = "/etc/modprobe.d/debian.conf";
360         }
361       ] ++ lib.optionals config.services.multipath.enable [
362         { object = pkgs.runCommand "multipath.conf" {
363               src = config.environment.etc."multipath.conf".text;
364               preferLocalBuild = true;
365             } ''
366               target=$out
367               printf "$src" > $out
368               substituteInPlace $out \
369                 --replace ${config.services.multipath.package}/lib ${extraUtils}/lib
370             '';
371           symlink = "/etc/multipath.conf";
372         }
373       ] ++ (lib.mapAttrsToList
374         (symlink: options:
375           {
376             inherit symlink;
377             object = options.source;
378           }
379         )
380         config.boot.initrd.extraFiles);
381   };
383   # Script to add secret files to the initrd at bootloader update time
384   initialRamdiskSecretAppender =
385     let
386       compressorExe = initialRamdisk.compressorExecutableFunction pkgs;
387     in pkgs.writeScriptBin "append-initrd-secrets"
388       ''
389         #!${pkgs.bash}/bin/bash -e
390         function usage {
391           echo "USAGE: $0 INITRD_FILE" >&2
392           echo "Appends this configuration's secrets to INITRD_FILE" >&2
393         }
395         if [ $# -ne 1 ]; then
396           usage
397           exit 1
398         fi
400         if [ "$1"x = "--helpx" ]; then
401           usage
402           exit 0
403         fi
405         ${lib.optionalString (config.boot.initrd.secrets == {})
406             "exit 0"}
408         export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin
410         function cleanup {
411           if [ -n "$tmp" -a -d "$tmp" ]; then
412             rm -fR "$tmp"
413           fi
414         }
415         trap cleanup EXIT
417         tmp=$(mktemp -d ''${TMPDIR:-/tmp}/initrd-secrets.XXXXXXXXXX)
419         ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source:
420             let source' = if source == null then dest else toString source; in
421               ''
422                 mkdir -p $(dirname "$tmp/.initrd-secrets/${dest}")
423                 cp -a ${source'} "$tmp/.initrd-secrets/${dest}"
424               ''
425           ) config.boot.initrd.secrets)
426          }
428         # mindepth 1 so that we don't change the mode of /
429         (cd "$tmp" && find . -mindepth 1 | xargs touch -amt 197001010000 && find . -mindepth 1 -print0 | sort -z | cpio --quiet -o -H newc -R +0:+0 --reproducible --null) | \
430           ${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1"
431       '';
436   options = {
438     boot.resumeDevice = mkOption {
439       type = types.str;
440       default = "";
441       example = "/dev/sda3";
442       description = ''
443         Device for manual resume attempt during boot. This should be used primarily
444         if you want to resume from file. If left empty, the swap partitions are used.
445         Specify here the device where the file resides.
446         You should also use {var}`boot.kernelParams` to specify
447         `«resume_offset»`.
448       '';
449     };
451     boot.initrd.enable = mkOption {
452       type = types.bool;
453       default = !config.boot.isContainer;
454       defaultText = literalExpression "!config.boot.isContainer";
455       description = ''
456         Whether to enable the NixOS initial RAM disk (initrd). This may be
457         needed to perform some initialisation tasks (like mounting
458         network/encrypted file systems) before continuing the boot process.
459       '';
460     };
462     boot.initrd.extraFiles = mkOption {
463       default = { };
464       type = types.attrsOf
465         (types.submodule {
466           options = {
467             source = mkOption {
468               type = types.package;
469               description = "The object to make available inside the initrd.";
470             };
471           };
472         });
473       description = ''
474         Extra files to link and copy in to the initrd.
475       '';
476     };
478     boot.initrd.prepend = mkOption {
479       default = [ ];
480       type = types.listOf types.str;
481       description = ''
482         Other initrd files to prepend to the final initrd we are building.
483       '';
484     };
486     boot.initrd.checkJournalingFS = mkOption {
487       default = true;
488       type = types.bool;
489       description = ''
490         Whether to run {command}`fsck` on journaling filesystems such as ext3.
491       '';
492     };
494     boot.initrd.preLVMCommands = mkOption {
495       default = "";
496       type = types.lines;
497       description = ''
498         Shell commands to be executed immediately before LVM discovery.
499       '';
500     };
502     boot.initrd.preDeviceCommands = mkOption {
503       default = "";
504       type = types.lines;
505       description = ''
506         Shell commands to be executed before udev is started to create
507         device nodes.
508       '';
509     };
511     boot.initrd.postDeviceCommands = mkOption {
512       default = "";
513       type = types.lines;
514       description = ''
515         Shell commands to be executed immediately after stage 1 of the
516         boot has loaded kernel modules and created device nodes in
517         {file}`/dev`.
518       '';
519     };
521     boot.initrd.postResumeCommands = mkOption {
522       default = "";
523       type = types.lines;
524       description = ''
525         Shell commands to be executed immediately after attempting to resume.
526       '';
527     };
529     boot.initrd.postMountCommands = mkOption {
530       default = "";
531       type = types.lines;
532       description = ''
533         Shell commands to be executed immediately after the stage 1
534         filesystems have been mounted.
535       '';
536     };
538     boot.initrd.preFailCommands = mkOption {
539       default = "";
540       type = types.lines;
541       description = ''
542         Shell commands to be executed before the failure prompt is shown.
543       '';
544     };
546     boot.initrd.extraUtilsCommands = mkOption {
547       internal = true;
548       default = "";
549       type = types.lines;
550       description = ''
551         Shell commands to be executed in the builder of the
552         extra-utils derivation.  This can be used to provide
553         additional utilities in the initial ramdisk.
554       '';
555     };
557     boot.initrd.extraUtilsCommandsTest = mkOption {
558       internal = true;
559       default = "";
560       type = types.lines;
561       description = ''
562         Shell commands to be executed in the builder of the
563         extra-utils derivation after patchelf has done its
564         job.  This can be used to test additional utilities
565         copied in extraUtilsCommands.
566       '';
567     };
569     boot.initrd.extraUdevRulesCommands = mkOption {
570       internal = true;
571       default = "";
572       type = types.lines;
573       description = ''
574         Shell commands to be executed in the builder of the
575         udev-rules derivation.  This can be used to add
576         additional udev rules in the initial ramdisk.
577       '';
578     };
580     boot.initrd.compressor = mkOption {
581       default = (
582         if lib.versionAtLeast config.boot.kernelPackages.kernel.version "5.9"
583         then "zstd"
584         else "gzip"
585       );
586       defaultText = literalMD "`zstd` if the kernel supports it (5.9+), `gzip` if not";
587       type = types.either types.str (types.functionTo types.str);
588       description = ''
589         The compressor to use on the initrd image. May be any of:
591         - The name of one of the predefined compressors, see {file}`pkgs/build-support/kernel/initrd-compressor-meta.nix` for the definitions.
592         - A function which, given the nixpkgs package set, returns the path to a compressor tool, e.g. `pkgs: "''${pkgs.pigz}/bin/pigz"`
593         - (not recommended, because it does not work when cross-compiling) the full path to a compressor tool, e.g. `"''${pkgs.pigz}/bin/pigz"`
595         The given program should read data from stdin and write it to stdout compressed.
596       '';
597       example = "xz";
598     };
600     boot.initrd.compressorArgs = mkOption {
601       default = null;
602       type = types.nullOr (types.listOf types.str);
603       description = "Arguments to pass to the compressor for the initrd image, or null to use the compressor's defaults.";
604     };
606     boot.initrd.secrets = mkOption
607       { default = {};
608         type = types.attrsOf (types.nullOr types.path);
609         description = ''
610             Secrets to append to the initrd. The attribute name is the
611             path the secret should have inside the initrd, the value
612             is the path it should be copied from (or null for the same
613             path inside and out).
615             Note that `nixos-rebuild switch` will generate the initrd
616             also for past generations, so if secrets are moved or deleted
617             you will also have to garbage collect the generations that
618             use those secrets.
619           '';
620         example = literalExpression
621           ''
622             { "/etc/dropbear/dropbear_rsa_host_key" =
623                 ./secret-dropbear-key;
624             }
625           '';
626       };
628     boot.initrd.supportedFilesystems = mkOption {
629       default = { };
630       inherit (options.boot.supportedFilesystems) example type description;
631     };
633     boot.initrd.verbose = mkOption {
634       default = true;
635       type = types.bool;
636       description = ''
637           Verbosity of the initrd. Please note that disabling verbosity removes
638           only the mandatory messages generated by the NixOS scripts. For a
639           completely silent boot, you might also want to set the two following
640           configuration options:
642           - `boot.consoleLogLevel = 0;`
643           - `boot.kernelParams = [ "quiet" "udev.log_level=3" ];`
644         '';
645     };
647     boot.loader.supportsInitrdSecrets = mkOption
648       { internal = true;
649         default = false;
650         type = types.bool;
651         description = ''
652             Whether the bootloader setup runs append-initrd-secrets.
653             If not, any needed secrets must be copied into the initrd
654             and thus added to the store.
655           '';
656       };
658     fileSystems = mkOption {
659       type = with lib.types; attrsOf (submodule {
660         options.neededForBoot = mkOption {
661           default = false;
662           type = types.bool;
663           description = ''
664             If set, this file system will be mounted in the initial ramdisk.
665             Note that the file system will always be mounted in the initial
666             ramdisk if its mount point is one of the following:
667             ${concatStringsSep ", " (
668               forEach utils.pathsNeededForBoot (i: "{file}`${i}`")
669             )}.
670           '';
671         };
672       });
673     };
675   };
677   config = mkIf config.boot.initrd.enable {
678     assertions = [
679       { assertion = !config.boot.initrd.systemd.enable -> any (fs: fs.mountPoint == "/") fileSystems;
680         message = "The ‘fileSystems’ option does not specify your root file system.";
681       }
682       { assertion = let inherit (config.boot) resumeDevice; in
683           resumeDevice == "" || builtins.substring 0 1 resumeDevice == "/";
684         message = "boot.resumeDevice has to be an absolute path."
685           + " Old \"x:y\" style is no longer supported.";
686       }
687       # TODO: remove when #85000 is fixed
688       { assertion = !config.boot.loader.supportsInitrdSecrets ->
689           all (source:
690             builtins.isPath source ||
691             (builtins.isString source && hasPrefix builtins.storeDir source))
692           (attrValues config.boot.initrd.secrets);
693         message = ''
694           boot.loader.initrd.secrets values must be unquoted paths when
695           using a bootloader that doesn't natively support initrd
696           secrets, e.g.:
698             boot.initrd.secrets = {
699               "/etc/secret" = /path/to/secret;
700             };
702           Note that this will result in all secrets being stored
703           world-readable in the Nix store!
704         '';
705       }
706     ];
708     system.build = mkMerge [
709       { inherit bootStage1 initialRamdiskSecretAppender extraUtils; }
711       # generated in nixos/modules/system/boot/systemd/initrd.nix
712       (mkIf (!config.boot.initrd.systemd.enable) { inherit initialRamdisk; })
713     ];
715     system.requiredKernelConfig = with config.lib.kernelConfig; [
716       (isYes "TMPFS")
717       (isYes "BLK_DEV_INITRD")
718     ];
720     boot.initrd.supportedFilesystems = map (fs: fs.fsType) fileSystems;
721   };
723   imports = [
724     (mkRenamedOptionModule [ "boot" "initrd" "mdadmConf" ] [ "boot" "swraid" "mdadmConf" ])
725   ];