1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 inherit (config.boot) kernelPatches;
8 inherit (config.boot.kernel) features randstructSeed;
9 inherit (config.boot.kernelPackages) kernel;
10
11 kernelModulesConf = pkgs.writeText "nixos.conf"
12 ''
13 ${concatStringsSep "\n" config.boot.kernelModules}
14 '';
15
16in
17
18{
19
20 ###### interface
21
22 options = {
23
24 boot.kernel.features = mkOption {
25 default = {};
26 example = literalExpression "{ debug = true; }";
27 internal = true;
28 description = ''
29 This option allows to enable or disable certain kernel features.
30 It's not API, because it's about kernel feature sets, that
31 make sense for specific use cases. Mostly along with programs,
32 which would have separate nixos options.
33 `grep features pkgs/os-specific/linux/kernel/common-config.nix`
34 '';
35 };
36
37 boot.kernelPackages = mkOption {
38 default = pkgs.linuxPackages;
39 type = types.unspecified // { merge = mergeEqualOption; };
40 apply = kernelPackages: kernelPackages.extend (self: super: {
41 kernel = super.kernel.override (originalArgs: {
42 inherit randstructSeed;
43 kernelPatches = (originalArgs.kernelPatches or []) ++ kernelPatches;
44 features = lib.recursiveUpdate super.kernel.features features;
45 });
46 });
47 # We don't want to evaluate all of linuxPackages for the manual
48 # - some of it might not even evaluate correctly.
49 defaultText = literalExpression "pkgs.linuxPackages";
50 example = literalExpression "pkgs.linuxKernel.packages.linux_5_10";
51 description = ''
52 This option allows you to override the Linux kernel used by
53 NixOS. Since things like external kernel module packages are
54 tied to the kernel you're using, it also overrides those.
55 This option is a function that takes Nixpkgs as an argument
56 (as a convenience), and returns an attribute set containing at
57 the very least an attribute <varname>kernel</varname>.
58 Additional attributes may be needed depending on your
59 configuration. For instance, if you use the NVIDIA X driver,
60 then it also needs to contain an attribute
61 <varname>nvidia_x11</varname>.
62 '';
63 };
64
65 boot.kernelPatches = mkOption {
66 type = types.listOf types.attrs;
67 default = [];
68 example = literalExpression "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]";
69 description = "A list of additional patches to apply to the kernel.";
70 };
71
72 boot.kernel.randstructSeed = mkOption {
73 type = types.str;
74 default = "";
75 example = "my secret seed";
76 description = ''
77 Provides a custom seed for the <varname>RANDSTRUCT</varname> security
78 option of the Linux kernel. Note that <varname>RANDSTRUCT</varname> is
79 only enabled in NixOS hardened kernels. Using a custom seed requires
80 building the kernel and dependent packages locally, since this
81 customization happens at build time.
82 '';
83 };
84
85 boot.kernelParams = mkOption {
86 type = types.listOf (types.strMatching ''([^"[:space:]]|"[^"]*")+'' // {
87 name = "kernelParam";
88 description = "string, with spaces inside double quotes";
89 });
90 default = [ ];
91 description = "Parameters added to the kernel command line.";
92 };
93
94 boot.consoleLogLevel = mkOption {
95 type = types.int;
96 default = 4;
97 description = ''
98 The kernel console <literal>loglevel</literal>. All Kernel Messages with a log level smaller
99 than this setting will be printed to the console.
100 '';
101 };
102
103 boot.vesa = mkOption {
104 type = types.bool;
105 default = false;
106 description = ''
107 (Deprecated) This option, if set, activates the VESA 800x600 video
108 mode on boot and disables kernel modesetting. It is equivalent to
109 specifying <literal>[ "vga=0x317" "nomodeset" ]</literal> in the
110 <option>boot.kernelParams</option> option. This option is
111 deprecated as of 2020: Xorg now works better with modesetting, and
112 you might want a different VESA vga setting, anyway.
113 '';
114 };
115
116 boot.extraModulePackages = mkOption {
117 type = types.listOf types.package;
118 default = [];
119 example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]";
120 description = "A list of additional packages supplying kernel modules.";
121 };
122
123 boot.kernelModules = mkOption {
124 type = types.listOf types.str;
125 default = [];
126 description = ''
127 The set of kernel modules to be loaded in the second stage of
128 the boot process. Note that modules that are needed to
129 mount the root file system should be added to
130 <option>boot.initrd.availableKernelModules</option> or
131 <option>boot.initrd.kernelModules</option>.
132 '';
133 };
134
135 boot.initrd.availableKernelModules = mkOption {
136 type = types.listOf types.str;
137 default = [];
138 example = [ "sata_nv" "ext3" ];
139 description = ''
140 The set of kernel modules in the initial ramdisk used during the
141 boot process. This set must include all modules necessary for
142 mounting the root device. That is, it should include modules
143 for the physical device (e.g., SCSI drivers) and for the file
144 system (e.g., ext3). The set specified here is automatically
145 closed under the module dependency relation, i.e., all
146 dependencies of the modules list here are included
147 automatically. The modules listed here are available in the
148 initrd, but are only loaded on demand (e.g., the ext3 module is
149 loaded automatically when an ext3 filesystem is mounted, and
150 modules for PCI devices are loaded when they match the PCI ID
151 of a device in your system). To force a module to be loaded,
152 include it in <option>boot.initrd.kernelModules</option>.
153 '';
154 };
155
156 boot.initrd.kernelModules = mkOption {
157 type = types.listOf types.str;
158 default = [];
159 description = "List of modules that are always loaded by the initrd.";
160 };
161
162 boot.initrd.includeDefaultModules = mkOption {
163 type = types.bool;
164 default = true;
165 description = ''
166 This option, if set, adds a collection of default kernel modules
167 to <option>boot.initrd.availableKernelModules</option> and
168 <option>boot.initrd.kernelModules</option>.
169 '';
170 };
171
172 system.modulesTree = mkOption {
173 type = types.listOf types.path;
174 internal = true;
175 default = [];
176 description = ''
177 Tree of kernel modules. This includes the kernel, plus modules
178 built outside of the kernel. Combine these into a single tree of
179 symlinks because modprobe only supports one directory.
180 '';
181 # Convert the list of path to only one path.
182 apply = pkgs.aggregateModules;
183 };
184
185 system.requiredKernelConfig = mkOption {
186 default = [];
187 example = literalExpression ''
188 with config.lib.kernelConfig; [
189 (isYes "MODULES")
190 (isEnabled "FB_CON_DECOR")
191 (isEnabled "BLK_DEV_INITRD")
192 ]
193 '';
194 internal = true;
195 type = types.listOf types.attrs;
196 description = ''
197 This option allows modules to specify the kernel config options that
198 must be set (or unset) for the module to work. Please use the
199 lib.kernelConfig functions to build list elements.
200 '';
201 };
202
203 };
204
205
206 ###### implementation
207
208 config = mkMerge
209 [ (mkIf config.boot.initrd.enable {
210 boot.initrd.availableKernelModules =
211 optionals config.boot.initrd.includeDefaultModules ([
212 # Note: most of these (especially the SATA/PATA modules)
213 # shouldn't be included by default since nixos-generate-config
214 # detects them, but I'm keeping them for now for backwards
215 # compatibility.
216
217 # Some SATA/PATA stuff.
218 "ahci"
219 "sata_nv"
220 "sata_via"
221 "sata_sis"
222 "sata_uli"
223 "ata_piix"
224 "pata_marvell"
225
226 # Standard SCSI stuff.
227 "sd_mod"
228 "sr_mod"
229
230 # SD cards and internal eMMC drives.
231 "mmc_block"
232
233 # Support USB keyboards, in case the boot fails and we only have
234 # a USB keyboard, or for LUKS passphrase prompt.
235 "uhci_hcd"
236 "ehci_hcd"
237 "ehci_pci"
238 "ohci_hcd"
239 "ohci_pci"
240 "xhci_hcd"
241 "xhci_pci"
242 "usbhid"
243 "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
244 "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft"
245
246 ] ++ optionals pkgs.stdenv.hostPlatform.isx86 [
247 # Misc. x86 keyboard stuff.
248 "pcips2" "atkbd" "i8042"
249
250 # x86 RTC needed by the stage 2 init script.
251 "rtc_cmos"
252 ]);
253
254 boot.initrd.kernelModules =
255 optionals config.boot.initrd.includeDefaultModules [
256 # For LVM.
257 "dm_mod"
258 ];
259 })
260
261 (mkIf (!config.boot.isContainer) {
262 system.build = { inherit kernel; };
263
264 system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages;
265
266 # Implement consoleLogLevel both in early boot and using sysctl
267 # (so you don't need to reboot to have changes take effect).
268 boot.kernelParams =
269 [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++
270 optionals config.boot.vesa [ "vga=0x317" "nomodeset" ];
271
272 boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel;
273
274 boot.kernelModules = [ "loop" "atkbd" ];
275
276 # The Linux kernel >= 2.6.27 provides firmware.
277 hardware.firmware = [ kernel ];
278
279 # Create /etc/modules-load.d/nixos.conf, which is read by
280 # systemd-modules-load.service to load required kernel modules.
281 environment.etc =
282 { "modules-load.d/nixos.conf".source = kernelModulesConf;
283 };
284
285 systemd.services.systemd-modules-load =
286 { wantedBy = [ "multi-user.target" ];
287 restartTriggers = [ kernelModulesConf ];
288 serviceConfig =
289 { # Ignore failed module loads. Typically some of the
290 # modules in ‘boot.kernelModules’ are "nice to have but
291 # not required" (e.g. acpi-cpufreq), so we don't want to
292 # barf on those.
293 SuccessExitStatus = "0 1";
294 };
295 };
296
297 lib.kernelConfig = {
298 isYes = option: {
299 assertion = config: config.isYes option;
300 message = "CONFIG_${option} is not yes!";
301 configLine = "CONFIG_${option}=y";
302 };
303
304 isNo = option: {
305 assertion = config: config.isNo option;
306 message = "CONFIG_${option} is not no!";
307 configLine = "CONFIG_${option}=n";
308 };
309
310 isModule = option: {
311 assertion = config: config.isModule option;
312 message = "CONFIG_${option} is not built as a module!";
313 configLine = "CONFIG_${option}=m";
314 };
315
316 ### Usually you will just want to use these two
317 # True if yes or module
318 isEnabled = option: {
319 assertion = config: config.isEnabled option;
320 message = "CONFIG_${option} is not enabled!";
321 configLine = "CONFIG_${option}=y";
322 };
323
324 # True if no or omitted
325 isDisabled = option: {
326 assertion = config: config.isDisabled option;
327 message = "CONFIG_${option} is not disabled!";
328 configLine = "CONFIG_${option}=n";
329 };
330 };
331
332 # The config options that all modules can depend upon
333 system.requiredKernelConfig = with config.lib.kernelConfig;
334 [
335 # !!! Should this really be needed?
336 (isYes "MODULES")
337 (isYes "BINFMT_ELF")
338 ] ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT"));
339
340 # nixpkgs kernels are assumed to have all required features
341 assertions = if config.boot.kernelPackages.kernel ? features then [] else
342 let cfg = config.boot.kernelPackages.kernel.config; in map (attrs:
343 { assertion = attrs.assertion cfg; inherit (attrs) message; }
344 ) config.system.requiredKernelConfig;
345
346 })
347
348 ];
349
350}