1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11
12 inherit (config.boot) kernelPatches;
13 inherit (config.boot.kernel) features randstructSeed;
14 inherit (config.boot.kernelPackages) kernel;
15
16 modulesTypeDesc = ''
17 This can either be a list of modules, or an attrset. In an
18 attrset, names that are set to `true` represent modules that will
19 be included. Note that setting these names to `false` does not
20 prevent the module from being loaded. For that, use
21 {option}`boot.blacklistedKernelModules`.
22 '';
23
24 kernelModulesConf = pkgs.writeText "nixos.conf" ''
25 ${concatStringsSep "\n" config.boot.kernelModules}
26 '';
27
28 # A list of attrnames is coerced into an attrset of bools by
29 # setting the values to true.
30 attrNamesToTrue = types.coercedTo (types.listOf types.str) (
31 enabledList: lib.genAttrs enabledList (_attrName: true)
32 ) (types.attrsOf types.bool);
33
34in
35
36{
37
38 ###### interface
39
40 options = {
41 boot.kernel.enable =
42 mkEnableOption "the Linux kernel. This is useful for systemd-like containers which do not require a kernel"
43 // {
44 default = true;
45 };
46
47 boot.kernel.features = mkOption {
48 default = { };
49 example = literalExpression "{ debug = true; }";
50 internal = true;
51 description = ''
52 This option allows to enable or disable certain kernel features.
53 It's not API, because it's about kernel feature sets, that
54 make sense for specific use cases. Mostly along with programs,
55 which would have separate nixos options.
56 `grep features pkgs/os-specific/linux/kernel/common-config.nix`
57 '';
58 };
59
60 boot.kernelPackages = mkOption {
61 default = pkgs.linuxPackages;
62 type = types.raw;
63 apply =
64 kernelPackages:
65 kernelPackages.extend (
66 self: super: {
67 kernel = super.kernel.override (originalArgs: {
68 inherit randstructSeed;
69 kernelPatches = (originalArgs.kernelPatches or [ ]) ++ kernelPatches;
70 features = lib.recursiveUpdate super.kernel.features features;
71 });
72 }
73 );
74 # We don't want to evaluate all of linuxPackages for the manual
75 # - some of it might not even evaluate correctly.
76 defaultText = literalExpression "pkgs.linuxPackages";
77 example = literalExpression "pkgs.linuxKernel.packages.linux_5_10";
78 description = ''
79 This option allows you to override the Linux kernel used by
80 NixOS. Since things like external kernel module packages are
81 tied to the kernel you're using, it also overrides those.
82 This option is a function that takes Nixpkgs as an argument
83 (as a convenience), and returns an attribute set containing at
84 the very least an attribute {var}`kernel`.
85 Additional attributes may be needed depending on your
86 configuration. For instance, if you use the NVIDIA X driver,
87 then it also needs to contain an attribute
88 {var}`nvidia_x11`.
89
90 Please note that we strictly support kernel versions that are
91 maintained by the Linux developers only. More information on the
92 availability of kernel versions is documented
93 [in the Linux section of the manual](https://nixos.org/manual/nixos/unstable/index.html#sec-kernel-config).
94 '';
95 };
96
97 boot.kernelPatches = mkOption {
98 type = types.listOf types.attrs;
99 default = [ ];
100 example = literalExpression ''
101 [
102 {
103 name = "foo";
104 patch = ./foo.patch;
105 structuredExtraConfig.FOO = lib.kernel.yes;
106 features.foo = true;
107 }
108 {
109 name = "foo-ml-mbox";
110 patch = (fetchurl {
111 url = "https://lore.kernel.org/lkml/19700205182810.58382-1-email@domain/t.mbox.gz";
112 hash = "sha256-...";
113 });
114 }
115 ]
116 '';
117 description = ''
118 A list of additional patches to apply to the kernel.
119
120 Every item should be an attribute set with the following attributes:
121
122 ```nix
123 {
124 name = "foo"; # descriptive name, required
125
126 patch = ./foo.patch; # path or derivation that contains the patch source
127 # (required, but can be null if only config changes
128 # are needed)
129
130 structuredExtraConfig = { # attrset of extra configuration parameters without the CONFIG_ prefix
131 FOO = lib.kernel.yes; # (optional)
132 }; # values should generally be lib.kernel.yes,
133 # lib.kernel.no or lib.kernel.module
134
135 features = { # attrset of extra "features" the kernel is considered to have
136 foo = true; # (may be checked by other NixOS modules, optional)
137 };
138
139 extraConfig = "FOO y"; # extra configuration options in string form without the CONFIG_ prefix
140 # (optional, multiple lines allowed to specify multiple options)
141 # (deprecated, use structuredExtraConfig instead)
142 }
143 ```
144
145 There's a small set of existing kernel patches in Nixpkgs, available as `pkgs.kernelPatches`,
146 that follow this format and can be used directly.
147 '';
148 };
149
150 boot.kernel.randstructSeed = mkOption {
151 type = types.str;
152 default = "";
153 example = "my secret seed";
154 description = ''
155 Provides a custom seed for the {var}`RANDSTRUCT` security
156 option of the Linux kernel. Note that {var}`RANDSTRUCT` is
157 only enabled in NixOS hardened kernels. Using a custom seed requires
158 building the kernel and dependent packages locally, since this
159 customization happens at build time.
160 '';
161 };
162
163 boot.kernelParams = mkOption {
164 type = types.listOf (
165 types.strMatching ''([^"[:space:]]|"[^"]*")+''
166 // {
167 name = "kernelParam";
168 description = "string, with spaces inside double quotes";
169 }
170 );
171 default = [ ];
172 description = "Parameters added to the kernel command line.";
173 };
174
175 boot.consoleLogLevel = mkOption {
176 type = types.int;
177 default = 4;
178 description = ''
179 The kernel console `loglevel`. All Kernel Messages with a log level smaller
180 than this setting will be printed to the console.
181 '';
182 };
183
184 boot.vesa = mkOption {
185 type = types.bool;
186 default = false;
187 description = ''
188 (Deprecated) This option, if set, activates the VESA 800x600 video
189 mode on boot and disables kernel modesetting. It is equivalent to
190 specifying `[ "vga=0x317" "nomodeset" ]` in the
191 {option}`boot.kernelParams` option. This option is
192 deprecated as of 2020: Xorg now works better with modesetting, and
193 you might want a different VESA vga setting, anyway.
194 '';
195 };
196
197 boot.extraModulePackages = mkOption {
198 type = types.listOf types.package;
199 default = [ ];
200 example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]";
201 description = "A list of additional packages supplying kernel modules.";
202 };
203
204 boot.kernelModules = mkOption {
205 type = attrNamesToTrue;
206 default = { };
207 description = ''
208 The set of kernel modules to be loaded in the second stage of
209 the boot process. Note that modules that are needed to
210 mount the root file system should be added to
211 {option}`boot.initrd.availableKernelModules` or
212 {option}`boot.initrd.kernelModules`.
213
214 ${modulesTypeDesc}
215 '';
216 apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods);
217 };
218
219 boot.initrd.availableKernelModules = mkOption {
220 type = attrNamesToTrue;
221 default = { };
222 example = [
223 "sata_nv"
224 "ext3"
225 ];
226 description = ''
227 The set of kernel modules in the initial ramdisk used during the
228 boot process. This set must include all modules necessary for
229 mounting the root device. That is, it should include modules
230 for the physical device (e.g., SCSI drivers) and for the file
231 system (e.g., ext3). The set specified here is automatically
232 closed under the module dependency relation, i.e., all
233 dependencies of the modules list here are included
234 automatically. The modules listed here are available in the
235 initrd, but are only loaded on demand (e.g., the ext3 module is
236 loaded automatically when an ext3 filesystem is mounted, and
237 modules for PCI devices are loaded when they match the PCI ID
238 of a device in your system). To force a module to be loaded,
239 include it in {option}`boot.initrd.kernelModules`.
240
241 ${modulesTypeDesc}
242 '';
243 apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods);
244 };
245
246 boot.initrd.kernelModules = mkOption {
247 type = attrNamesToTrue;
248 default = { };
249 description = ''
250 Set of modules that are always loaded by the initrd.
251
252 ${modulesTypeDesc}
253 '';
254 apply = mods: lib.attrNames (lib.filterAttrs (_: v: v) mods);
255 };
256
257 boot.initrd.includeDefaultModules = mkOption {
258 type = types.bool;
259 default = true;
260 description = ''
261 This option, if set, adds a collection of default kernel modules
262 to {option}`boot.initrd.availableKernelModules` and
263 {option}`boot.initrd.kernelModules`.
264 '';
265 };
266
267 boot.initrd.allowMissingModules = mkOption {
268 type = types.bool;
269 default = false;
270 description = ''
271 Whether the initrd can be built even though modules listed in
272 {option}`boot.initrd.kernelModules` or
273 {option}`boot.initrd.availableKernelModules` are missing from
274 the kernel. This is useful when combining configurations that
275 include a lot of modules, such as
276 {option}`hardware.enableAllHardware`, with kernels that don't
277 provide as many modules as typical NixOS kernels.
278
279 Note that enabling this is discouraged. Instead, try disabling
280 individual modules by setting e.g.
281 `boot.initrd.availableKernelModules.foo = lib.mkForce false;`
282 '';
283 };
284
285 system.modulesTree = mkOption {
286 type = types.listOf types.path;
287 internal = true;
288 default = [ ];
289 description = ''
290 Tree of kernel modules. This includes the kernel, plus modules
291 built outside of the kernel. Combine these into a single tree of
292 symlinks because modprobe only supports one directory.
293 '';
294 # Convert the list of path to only one path.
295 apply =
296 let
297 kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
298 in
299 modules: (pkgs.aggregateModules modules).override { name = kernel-name + "-modules"; };
300 };
301
302 system.requiredKernelConfig = mkOption {
303 default = [ ];
304 example = literalExpression ''
305 with config.lib.kernelConfig; [
306 (isYes "MODULES")
307 (isEnabled "FB_CON_DECOR")
308 (isEnabled "BLK_DEV_INITRD")
309 ]
310 '';
311 internal = true;
312 type = types.listOf types.attrs;
313 description = ''
314 This option allows modules to specify the kernel config options that
315 must be set (or unset) for the module to work. Please use the
316 lib.kernelConfig functions to build list elements.
317 '';
318 };
319
320 };
321
322 ###### implementation
323
324 config = mkMerge [
325 (mkIf config.boot.initrd.enable {
326 boot.initrd.availableKernelModules = optionals config.boot.initrd.includeDefaultModules (
327 [
328 # Note: most of these (especially the SATA/PATA modules)
329 # shouldn't be included by default since nixos-generate-config
330 # detects them, but I'm keeping them for now for backwards
331 # compatibility.
332
333 # Some SATA/PATA stuff.
334 "ahci"
335 "sata_nv"
336 "sata_via"
337 "sata_sis"
338 "sata_uli"
339 "ata_piix"
340 "pata_marvell"
341
342 # NVMe
343 "nvme"
344
345 # Standard SCSI stuff.
346 "sd_mod"
347 "sr_mod"
348
349 # SD cards and internal eMMC drives.
350 "mmc_block"
351
352 # Support USB keyboards, in case the boot fails and we only have
353 # a USB keyboard, or for LUKS passphrase prompt.
354 "uhci_hcd"
355 "ehci_hcd"
356 "ehci_pci"
357 "ohci_hcd"
358 "ohci_pci"
359 "xhci_hcd"
360 "xhci_pci"
361 "usbhid"
362 "hid_generic"
363 "hid_lenovo"
364 "hid_apple"
365 "hid_roccat"
366 "hid_logitech_hidpp"
367 "hid_logitech_dj"
368 "hid_microsoft"
369 "hid_cherry"
370 "hid_corsair"
371
372 ]
373 ++ optionals pkgs.stdenv.hostPlatform.isx86 [
374 # Misc. x86 keyboard stuff.
375 "pcips2"
376 "atkbd"
377 "i8042"
378 ]
379 );
380
381 boot.initrd.kernelModules = optionals config.boot.initrd.includeDefaultModules [
382 # For LVM.
383 "dm_mod"
384 ];
385 })
386
387 (mkIf config.boot.kernel.enable {
388 system.build = { inherit kernel; };
389
390 system.modulesTree = [ (lib.getOutput "modules" kernel) ] ++ config.boot.extraModulePackages;
391
392 # Not required for, e.g., containers as they don't have their own kernel or initrd.
393 # They boot directly into stage 2.
394 system.systemBuilderArgs.kernelParams = config.boot.kernelParams;
395 system.systemBuilderCommands =
396 let
397 kernelPath = "${config.boot.kernelPackages.kernel}/" + "${config.system.boot.loader.kernelFile}";
398 initrdPath = "${config.system.build.initialRamdisk}/" + "${config.system.boot.loader.initrdFile}";
399 in
400 ''
401 if [ ! -f ${kernelPath} ]; then
402 echo "The bootloader cannot find the proper kernel image."
403 echo "(Expecting ${kernelPath})"
404 false
405 fi
406
407 ln -s ${kernelPath} $out/kernel
408 ln -s ${config.system.modulesTree} $out/kernel-modules
409 ${optionalString (config.hardware.deviceTree.package != null) ''
410 ln -s ${config.hardware.deviceTree.package} $out/dtbs
411 ''}
412
413 echo -n "$kernelParams" > $out/kernel-params
414
415 ln -s ${initrdPath} $out/initrd
416
417 ${optionalString (config.boot.initrd.secrets != { }) ''
418 ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
419 ''}
420
421 ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
422 '';
423
424 # Implement consoleLogLevel both in early boot and using sysctl
425 # (so you don't need to reboot to have changes take effect).
426 boot.kernelParams = [
427 "loglevel=${toString config.boot.consoleLogLevel}"
428 ]
429 ++ optionals config.boot.vesa [
430 "vga=0x317"
431 "nomodeset"
432 ];
433
434 boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
435
436 boot.kernelModules = [
437 "loop"
438 "atkbd"
439 ];
440
441 # Create /etc/modules-load.d/nixos.conf, which is read by
442 # systemd-modules-load.service to load required kernel modules.
443 environment.etc = {
444 "modules-load.d/nixos.conf".source = kernelModulesConf;
445 };
446
447 systemd.services.systemd-modules-load = {
448 wantedBy = [ "multi-user.target" ];
449 restartTriggers = [ kernelModulesConf ];
450 serviceConfig = {
451 # Ignore failed module loads. Typically some of the
452 # modules in ‘boot.kernelModules’ are "nice to have but
453 # not required" (e.g. acpi-cpufreq), so we don't want to
454 # barf on those.
455 SuccessExitStatus = "0 1";
456 };
457 };
458
459 lib.kernelConfig = {
460 isYes = option: {
461 assertion = config: config.isYes option;
462 message = "CONFIG_${option} is not yes!";
463 configLine = "CONFIG_${option}=y";
464 };
465
466 isNo = option: {
467 assertion = config: config.isNo option;
468 message = "CONFIG_${option} is not no!";
469 configLine = "CONFIG_${option}=n";
470 };
471
472 isModule = option: {
473 assertion = config: config.isModule option;
474 message = "CONFIG_${option} is not built as a module!";
475 configLine = "CONFIG_${option}=m";
476 };
477
478 ### Usually you will just want to use these two
479 # True if yes or module
480 isEnabled = option: {
481 assertion = config: config.isEnabled option;
482 message = "CONFIG_${option} is not enabled!";
483 configLine = "CONFIG_${option}=y";
484 };
485
486 # True if no or omitted
487 isDisabled = option: {
488 assertion = config: config.isDisabled option;
489 message = "CONFIG_${option} is not disabled!";
490 configLine = "CONFIG_${option}=n";
491 };
492 };
493
494 # The config options that all modules can depend upon
495 system.requiredKernelConfig =
496 with config.lib.kernelConfig;
497 [
498 # !!! Should this really be needed?
499 (isYes "BINFMT_ELF")
500 ]
501 ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT"));
502
503 # nixpkgs kernels are assumed to have all required features
504 assertions =
505 if config.boot.kernelPackages.kernel ? features then
506 [ ]
507 else
508 let
509 cfg = config.boot.kernelPackages.kernel.config;
510 in
511 map (attrs: {
512 assertion = attrs.assertion cfg;
513 inherit (attrs) message;
514 }) config.system.requiredKernelConfig;
515
516 })
517
518 ];
519
520}