at master 18 kB view raw
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}