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