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