nixos/systemd/initrd: Check MODULES accurately

`config.system.build.kernel.config` is not actually accurate. When a
kconfig is specified in the `config` argument to `kernel/build.nix`
(a.k.a. `manualConfig` or `linuxManualConfig`), then `isSet` will
return `true` and other queries like `isYes` will be accurate. But if
a kconfig is not specified in that `config` argument, then `isSet`
will return `false` and other queries will be inaccurate, e.g. `isYes`
"MODULES"` can return `false` even though your `configfile` has it
enabled.

Note the difference between the `config` argument and the `configfile`
argument. The `configfile` is how the kernel will be actually built,
while `config` is merely passed through as a source of eval-time
information. Importantly, neither is derived from the other *in any
way*, unless `builtins.isPath configfile || allowImportFromDerivation`
in which case the default value for `config` is derived from reading
`configfile`.

The more generic `kernel/generic.nix` (a.k.a. `buildLinux`) creates
its `configfile` in a derivation, so it cannot be read at eval time by
default. It calls into `kernel/build.nix`, and only passes a `config`
with `CONFIG_MODULES`, `CONFIG_FW_LOADER`, and `CONFIG_RUST` set. So
almost nothing in `kernel.config` is accurate in the typical
case. `MODULES` happens to be one of the three that *is* accurate
typically, but regardless we obviously can't rely on that since a user
of `kernel/build.nix` is likely to mess it up. Even worse,
`structuredExtraConfig` is not incorporated into `kernel.config` at
all, which leads to the incredibly confusing scenario where a kconfig
is specified in `structuredExtraConfig` but still is not represented
accurately by these queries.

All of this is why, in most cases, the implementation of
`requiredKernelConfig` deliberately does absolutely nothing and
creates an empty list of assertions, and it's all extremely confusing.

With all that in mind:

TODO:

- The structured config used to generate the `configfile` should be
  reflected in the `config` argument to `kernel/build.nix`, and
  consequently `kernel.config`.
- The three kconfigs represented by `config` in `kernel/generic.nix`
  now, `CONFIG_MODULES`, `CONFIG_FW_LOADER`, and `CONFIG_RUST`, should
  be set in the structured config.
- Queries for kconfigs that we don't actually know the value of at
  eval time should fail to evaluate, rather than evaluating
  inaccurately.
- Most of the ways we use these eval-time queries should instead be
  done at build time, so they can use the complete `configfile` rather
  than the incomplete eval-time `config` value.
- The ones that we still want to happen at eval-time should be more
  prepared for the possibility that we can't know the value of
  arbitrary kconfigs at eval time.

---

Anyway, all that is to say: I'd like for this all to be better, but am
not willing to work on the kernel expressions myself at the moment, so
I thought I'd write down the reasons why this change was necessary,
and the extent of the problem.

We had already successfully considered this issue in one place in
`systemd/initrd.nix`, but it seems there are two more places where we
should have taken the same care.
This commit is contained in:
Will Fancher
2026-04-22 23:28:16 -04:00
parent 61d18372d6
commit 983d84d401
+9 -3
View File
@@ -47,6 +47,12 @@ let
cfg = config.boot.initrd.systemd;
withKmod =
let
kconfig = config.system.build.kernel.config;
in
kconfig.isSet "MODULES" -> kconfig.isYes "MODULES";
upstreamUnits = [
"basic.target"
"breakpoint-pre-udev.service"
@@ -525,7 +531,7 @@ in
pkgs.coreutils
cfg.package
]
++ lib.optional (config.system.build.kernel.config.isYes "MODULES") cfg.package.kmod
++ lib.optional withKmod cfg.package.kmod
++ lib.optionals cfg.shell.enable [
# bashInteractive is easier to use and also required by debug-shell.service
pkgs.bashInteractive
@@ -575,7 +581,7 @@ in
// optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") {
"/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source;
}
// optionalAttrs (with config.system.build.kernel.config; isSet "MODULES" -> isYes "MODULES") {
// optionalAttrs withKmod {
"/lib".source = "${config.system.build.modulesClosure}/lib";
"/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules;
@@ -660,7 +666,7 @@ in
) cfg.automounts
);
services."modprobe@" = lib.mkIf (config.system.build.kernel.config.isYes "MODULES") {
services."modprobe@" = lib.mkIf withKmod {
serviceConfig.ExecSearchPath = lib.makeBinPath [ cfg.package.kmod ];
};