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