diff --git a/lib/types.nix b/lib/types.nix index 715da842ac0183..73d59309e91b4a 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -1456,6 +1456,12 @@ let nestedTypes.finalType = finalType; }; + # A list of attrnames is coerced into an attrset of bools by + # setting the values to true. + attrNamesToTrue = coercedTo (types.listOf types.str) ( + enabledList: lib.genAttrs enabledList (_attrName: true) + ) (types.attrsOf types.bool); + # Augment the given type with an additional type check function. addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; }; diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md index c0add330b72e67..8539da71a214ea 100644 --- a/nixos/doc/manual/development/option-types.section.md +++ b/nixos/doc/manual/development/option-types.section.md @@ -135,6 +135,29 @@ merging is handled. problems. ::: +`types.attrNamesToTrue` + +: Either a list of attribute names, or an attribute set of + booleans. A list will be coerced into an attribute set with those + names, whose values are set to `true`. This is useful when it is + convenient to be able to write definitions as a simple list, but + still need to be able to override and disable individual values. + + ::: {#ex-types-attrNamesToTrue .example} + ### `types.attrNamesToTrue` + ``` + { + foo = [ "bar" ]; + } + ``` + + ``` + { + foo.bar = true; + } + ``` + ::: + `types.pkgs` : A type for the top level Nixpkgs package set. diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index b660d2c6d7a96c..1d8f65ad28d324 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -13,6 +13,14 @@ let inherit (config.boot.kernel) features randstructSeed; inherit (config.boot.kernelPackages) kernel; + modulesTypeDesc = '' + This can either be a list of modules, or an attrset. In an + attrset, names that are set to `true` represent modules that will + be included. Note that setting these names to `false` does not + prevent the module from being loaded. For that, use + {option}`boot.blacklistedKernelModules`. + ''; + kernelModulesConf = pkgs.writeText "nixos.conf" '' ${concatStringsSep "\n" config.boot.kernelModules} ''; @@ -188,20 +196,23 @@ in }; boot.kernelModules = mkOption { - type = types.listOf types.str; - default = [ ]; + type = types.attrNamesToTrue; + default = { }; description = '' The set of kernel modules to be loaded in the second stage of the boot process. Note that modules that are needed to mount the root file system should be added to {option}`boot.initrd.availableKernelModules` or {option}`boot.initrd.kernelModules`. + + ${modulesTypeDesc} ''; + apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods); }; boot.initrd.availableKernelModules = mkOption { - type = types.listOf types.str; - default = [ ]; + type = types.attrNamesToTrue; + default = { }; example = [ "sata_nv" "ext3" @@ -220,13 +231,21 @@ in modules for PCI devices are loaded when they match the PCI ID of a device in your system). To force a module to be loaded, include it in {option}`boot.initrd.kernelModules`. + + ${modulesTypeDesc} ''; + apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods); }; boot.initrd.kernelModules = mkOption { - type = types.listOf types.str; - default = [ ]; - description = "List of modules that are always loaded by the initrd."; + type = types.attrNamesToTrue; + default = { }; + description = '' + Set of modules that are always loaded by the initrd. + + ${modulesTypeDesc} + ''; + apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods); }; boot.initrd.includeDefaultModules = mkOption { @@ -239,6 +258,24 @@ in ''; }; + boot.initrd.allowMissingModules = mkOption { + type = types.bool; + default = false; + description = '' + Whether the initrd can be built even though modules listed in + {option}`boot.initrd.kernelModules` or + {option}`boot.initrd.availableKernelModules` are missing from + the kernel. This is useful when combining configurations that + include a lot of modules, such as + {option}`hardware.enableAllHardware`, with kernels that don't + provide as many modules as typical NixOS kernels. + + Note that enabling this is discouraged. Instead, try disabling + individual modules by setting e.g. + `boot.initrd.availableKernelModules.foo = lib.mkForce false;` + ''; + }; + system.modulesTree = mkOption { type = types.listOf types.path; internal = true; diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix index df0d5d6679884a..b38e78c3222aab 100644 --- a/nixos/modules/system/boot/modprobe.nix +++ b/nixos/modules/system/boot/modprobe.nix @@ -25,16 +25,19 @@ with lib; }; boot.blacklistedKernelModules = mkOption { - type = types.listOf types.str; - default = [ ]; + type = types.attrNamesToTrue; + default = { }; example = [ "cirrusfb" "i2c_piix4" ]; description = '' - List of names of kernel modules that should not be loaded - automatically by the hardware probing code. + Set of names of kernel modules that should not be loaded + automatically by the hardware probing code. This can either be + a list of modules or an attrset. In an attrset, names that are + set to `true` represent modules that will be blacklisted. ''; + apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods); }; boot.extraModprobeConfig = mkOption { diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index c916160aa7917d..09e6fcaf9ea776 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -25,7 +25,7 @@ let rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules; kernel = config.system.modulesTree; firmware = config.hardware.firmware; - allowMissing = false; + allowMissing = config.boot.initrd.allowMissingModules; inherit (config.boot.initrd) extraFirmwarePaths; }; @@ -783,7 +783,14 @@ in ]; system.build = mkMerge [ - { inherit bootStage1 initialRamdiskSecretAppender extraUtils; } + { + inherit + bootStage1 + initialRamdiskSecretAppender + extraUtils + modulesClosure + ; + } # generated in nixos/modules/system/boot/systemd/initrd.nix (mkIf (!config.boot.initrd.systemd.enable) { inherit initialRamdisk; }) diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 23b9e232c64df8..0a516cac9fff82 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -99,14 +99,6 @@ let }; kernel-name = config.boot.kernelPackages.kernel.name or "kernel"; - # 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 = config.system.modulesTree; - firmware = config.hardware.firmware; - allowMissing = false; - inherit (config.boot.initrd) extraFirmwarePaths; - }; initrdBinEnv = pkgs.buildEnv { name = "initrd-bin-env"; @@ -477,7 +469,7 @@ in } ''; - "/lib".source = "${modulesClosure}/lib"; + "/lib".source = "${config.system.build.modulesClosure}/lib"; "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index ef2a5a6850034d..b28a902a7c54c8 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -326,9 +326,7 @@ in zfs = lib.mkForce false; } ''; - type = types.coercedTo (types.listOf types.str) ( - enabled: lib.listToAttrs (map (fs: lib.nameValuePair fs true) enabled) - ) (types.attrsOf types.bool); + type = types.attrNamesToTrue; description = '' Names of supported filesystem types, or an attribute set of file system types and their state. The set form may be used together with `lib.mkForce` to