Linux kernel: Difference between revisions

From NixOS Wiki
imported>Pcboy
m pkgs.linux_zen.override is incorrect. Should be kernel.override.
TefLa (talk | contribs)
m Fix: linux_zen.kernel -> linuxKernel.kernels.linux_zen
 
(27 intermediate revisions by 16 users not shown)
Line 3: Line 3:
== Configuration ==
== Configuration ==


You can choose your kernel simply by setting the <code>boot.kernelPackages</code> option
You can change the installed kernel using {{nixos:option|boot.kernelPackages}}. To use the latest release:


For example by adding this to  <code>/etc/nixos/configuration.nix</code>:
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
<syntaxHighlight lang="nix">
{
boot.kernelPackages = pkgs.linuxPackages_latest;
  boot.kernelPackages = pkgs.linuxPackages_latest;
</syntaxHighlight>
}
And rebuild your system and reboot to use your new kernel:
</nowiki>}}
 
Then rebuild your system and reboot to use your new kernel:
<syntaxHighlight lang="console">
<syntaxHighlight lang="console">
$ sudo nixos-rebuild boot
$ sudo nixos-rebuild boot
Line 17: Line 19:
=== List available kernels ===
=== List available kernels ===
You can list available kernels using  <code>nix repl</code> (previously <code>nix-repl</code>) by typing the package name and using the tab completion:
You can list available kernels using  <code>nix repl</code> (previously <code>nix-repl</code>) by typing the package name and using the tab completion:
 
 
<syntaxHighlight lang="console">
<syntaxHighlight lang="console">
$ nix repl
$ nix repl
Line 26: Line 28:


nix-repl> pkgs.linuxPackages
nix-repl> pkgs.linuxPackages
pkgs.linuxPackages                          pkgs.linuxPackages_latest
pkgs.linuxPackages                          pkgs.linuxPackages_custom
pkgs.linuxPackages-libre                    pkgs.linuxPackages_latest-libre
pkgs.linuxPackages-libre                    pkgs.linuxPackages_custom_tinyconfig_kernel
pkgs.linuxPackages-rt                        pkgs.linuxPackages_latest_hardened
pkgs.linuxPackages-rt                        pkgs.linuxPackages_hardened
pkgs.linuxPackages-rt_5_4                    pkgs.linuxPackages_latest_xen_dom0
pkgs.linuxPackages-rt_latest                pkgs.linuxPackages_latest
pkgs.linuxPackages-rt_5_6                    pkgs.linuxPackages_latest_xen_dom0_hardened
pkgs.linuxPackagesFor                        pkgs.linuxPackages_latest-libre
pkgs.linuxPackages-rt_latest                pkgs.linuxPackages_mptcp
pkgs.linuxPackages_4_14                      pkgs.linuxPackages_latest_hardened
pkgs.linuxPackagesFor                        pkgs.linuxPackages_rpi0
pkgs.linuxPackages_4_19                      pkgs.linuxPackages_latest_xen_dom0
pkgs.linuxPackages_4_14                     pkgs.linuxPackages_rpi1
pkgs.linuxPackages_4_19_hardened            pkgs.linuxPackages_latest_xen_dom0_hardened
pkgs.linuxPackages_4_19                      pkgs.linuxPackages_rpi2
pkgs.linuxPackages_4_9                      pkgs.linuxPackages_lqx
pkgs.linuxPackages_4_4                      pkgs.linuxPackages_rpi3
pkgs.linuxPackages_5_10                      pkgs.linuxPackages_rpi0
pkgs.linuxPackages_4_9                      pkgs.linuxPackages_rpi4
pkgs.linuxPackages_5_10_hardened            pkgs.linuxPackages_rpi02w
pkgs.linuxPackages_5_4                      pkgs.linuxPackages_testing
pkgs.linuxPackages_5_15                     pkgs.linuxPackages_rpi1
pkgs.linuxPackages_5_8                       pkgs.linuxPackages_testing_bcachefs
pkgs.linuxPackages_5_15_hardened            pkgs.linuxPackages_rpi2
pkgs.linuxPackages_5_9                       pkgs.linuxPackages_testing_hardened
pkgs.linuxPackages_5_18                      pkgs.linuxPackages_rpi3
pkgs.linuxPackages_custom                    pkgs.linuxPackages_xen_dom0
pkgs.linuxPackages_5_19                      pkgs.linuxPackages_rpi4
pkgs.linuxPackages_custom_tinyconfig_kernel  pkgs.linuxPackages_xen_dom0_hardened
pkgs.linuxPackages_5_4                      pkgs.linuxPackages_rt_5_10
pkgs.linuxPackages_hardened                  pkgs.linuxPackages_zen
pkgs.linuxPackages_5_4_hardened              pkgs.linuxPackages_rt_5_15
pkgs.linuxPackages_hardkernel_4_14
pkgs.linuxPackages_6_0                      pkgs.linuxPackages_rt_5_4
pkgs.linuxPackages_hardkernel_latest
pkgs.linuxPackages_6_1                      pkgs.linuxPackages_rt_6_1
pkgs.linuxPackages_6_1_hardened              pkgs.linuxPackages_testing
pkgs.linuxPackages_6_2                       pkgs.linuxPackages_testing_bcachefs
pkgs.linuxPackages_6_3                       pkgs.linuxPackages_xanmod
pkgs.linuxPackages_6_4                      pkgs.linuxPackages_xanmod_latest
pkgs.linuxPackages_6_5                      pkgs.linuxPackages_xanmod_stable
pkgs.linuxPackages_6_6                      pkgs.linuxPackages_xen_dom0
pkgs.linuxPackages_6_6_hardened              pkgs.linuxPackages_xen_dom0_hardened
pkgs.linuxPackages_6_7                      pkgs.linuxPackages_zen
pkgs.linuxPackages_6_8
</syntaxhighlight>
</syntaxhighlight>


Line 51: Line 62:
Note that if you deviate from the default kernel version, you should also take extra care that extra kernel modules must match the same version. The safest way to do this is to use <code>config.boot.kernelPackages</code> to select the correct module set:
Note that if you deviate from the default kernel version, you should also take extra care that extra kernel modules must match the same version. The safest way to do this is to use <code>config.boot.kernelPackages</code> to select the correct module set:


<syntaxHighlight lang="nix">
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{ config, ... }:
{ config, ... }:
{
{
   boot.extraModulePackages = with config.boot.kernelPackages; [ wireguard ];
   boot.extraModulePackages = with config.boot.kernelPackages; [ bbswitch ];
}
}
</syntaxHighlight>
</nowiki>}}


Note that wireguard has been merged into the mainline kernel, so on newer (21.05+) versions of NixOS with the default kernel wireguard is no longer an option available.
=== Customizing kernel module parameters ===


=== Custom kernel commandline ===
You can use {{nixos:option|boot.extraModprobeConfig}}, which is analogous to creating modprobe.conf files in /etc/modprobe.d/ in regular Linux distributions. This can be used for both built-in and loadable kernel modules.


The config attribute <code>boot.kernelParams</code> can be set to supply the Linux kernel with additional command line arguments at boot time.
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  boot.extraModprobeConfig = ''
    # example settings
    options yourmodulename optionA=valueA optionB=valueB # syntax
    options thinkpad_acpi  fan_control=1                # example #1 kernel module parameter
    options usbcore        blinkenlights=1              # example #2 kernel module parameter
  '';
}
</nowiki>}}


<syntaxHighlight lang="nix">
{{nixos:option|boot.kernelParams}} can be used to set additional kernel command line arguments at boot time. It can only be used for built-in modules. For example:
{ pkgs, config, ... }:


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
{
   boot.kernelParams = [ /* list of command line arguments */ ];
   boot.kernelParams = [
    "quiet"
    "splash"
    "usbcore.blinkenlights=1"
  ];
}
}
</syntaxHighlight>
</nowiki>}}
 
=== Enable SysRq ===
 
The Linux kernel is known<ref>https://github.com/hakavlad/nohang?tab=readme-ov-file#what-is-the-problem</ref> to not handle out-of-memory situation properly and can freeze for a long time, often leaving no option but doing a hard reboot. The [https://en.wikipedia.org/wiki/Magic_SysRq_key SysRq shortcuts] can be used to trigger a more graceful reboot. However, most keys are disabled by default on NixOS. To enable:
 
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
  boot.kernel.sysctl."kernel.sysrq" = 1;
}
</nowiki>}}
 
Useful shortcuts, triggered using {{ic|Alt+SysRq+<key>}}:
 
* {{ic|h}}: Print help to the system log.
* {{ic|f}}: Trigger the kernel oom killer.
* {{ic|s}}: Sync data to disk before triggering the reset options below.
* {{ic|e}}: SIGTERM all processes except PID 0.
* {{ic|i}}: SIGKILL all processes except PID 0.
* {{ic|b}}: Reboot the system.
 
Check <code>journalctl</code> to see if you are triggering the shortcuts correctly, which might be different for your keyboard, as noted in the Wikipedia page.
 
Also see {{nixos:option|services.earlyoom.enable}} and {{nixos:option|systemd.oomd.enable}}.


=== Custom configuration ===
=== Custom configuration ===


It is sometimes desirable to change the configuration of your kernel, while keeping the kernel version itself managed through Nixpkgs. To do so, you can add the configuration to a dummy {{nixos:option|boot.kernelPatches}},<ref>[https://logs.nix.samueldr.com/nixos/2018-05-09#1525898458-1525898534; #nixos 2018-05-09]</ref><ref>[https://github.com/NixOS/nixpkgs/blob/03d4694e6110fa9c16e88ee74085ea2068c27494/nixos/modules/misc/crashdump.nix#L63-L73 <tt>nixos/modules/misc/crashdump.nix#L63-L73</tt>]</ref> which will then be merged and applied to the current kernel. As with kernel configuration with NixOS, drop the <tt>CONFIG_</tt> prefix from the kernel configuration names.
It is sometimes desirable to change the configuration of your kernel, while keeping the kernel version itself managed through Nixpkgs.


This example is from the {{nixos:option|boot.crashDump.enable}} option:
One way is to use {{nixos:option|boot.kernelPatches}}<ref>[https://logs.nix.samueldr.com/nixos/2018-05-09#1525898458-1525898534; #nixos 2018-05-09]</ref><ref>[https://github.com/NixOS/nixpkgs/blob/03d4694e6110fa9c16e88ee74085ea2068c27494/nixos/modules/misc/crashdump.nix#L63-L73 <tt>nixos/modules/misc/crashdump.nix#L63-L73</tt>]</ref>. For example, {{nixos:option|boot.crashDump.enable}} is configured as shown below. Note that the {{ic|CONFIG_}} prefix is not used in the configuration names.


<syntaxhighlight lang=nix>
<syntaxhighlight lang=nix>
Line 94: Line 141:
</syntaxhighlight>
</syntaxhighlight>


Another way to build a kernel with custom configuration is to create a overlay and use either <tt>extraConfig</tt> or <tt>structuredExtraConfig</tt>. This allows for more flexibility, since you can basically override any kernel option available here <ref>[https://github.com/NixOS/nixpkgs/blob/e6db435973160591fe7348876a5567c729495175/pkgs/os-specific/linux/kernel/generic.nix#L11-L56<tt>pkgs/os-specific/linux/kernel/generic.nix#L11-L56</tt>]</ref>. Using <tt>structuredExtraConfig</tt> is recommended since there is some sanity check if the options you're passing to your kernel is correct. Also, setting <tt>ignoreConfigErrors</tt> is useful to avoid <tt>error: unused option</tt> during build.
Another way is to create a overlay to configure either {{ic|extraConfig}} or {{ic|structuredExtraConfig}}. This allows for more flexibility, since you can basically override any available kernel option<ref>[https://github.com/NixOS/nixpkgs/blob/e6db435973160591fe7348876a5567c729495175/pkgs/os-specific/linux/kernel/generic.nix#L11-L56<tt>pkgs/os-specific/linux/kernel/generic.nix#L11-L56</tt>]</ref>. Using {{ic|structuredExtraConfig}} is recommended as it checks if the configured option are correct. You may also want to set {{ic|ignoreConfigErrors}} to avoid {{ic|error: unused option}} during builds.


<syntaxhighlight lang=nix>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{
{
   nixpkgs = {
   nixpkgs = {
     overlays = [
     overlays = [
       (self: super: {
       (self: super: {
         linuxZenWMuQSS = pkgs.linuxPackagesFor (pkgs.linux_zen.kernel.override {
         linuxZenWMuQSS = pkgs.linuxPackagesFor (pkgs.linuxKernel.kernels.linux_zen.override {
           structuredExtraConfig = with lib.kernel; {
           structuredExtraConfig = with lib.kernel; {
             SCHED_MUQSS = yes;
             SCHED_MUQSS = yes;
Line 111: Line 158:
   };
   };
}
}
</syntaxhighlight>
</nowiki>}}


=== Pinning a kernel version ===
=== Pinning a kernel version ===


<syntaxhighlight lang=nix>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
boot.kernelPackages = pkgs.linuxPackagesFor (pkgs.linux_4_19.override {
{
  boot.kernelPackages = pkgs.linuxPackagesFor (pkgs.linuxKernel.kernels.linux_4_19.override {
     argsOverride = rec {
     argsOverride = rec {
       src = pkgs.fetchurl {
       src = pkgs.fetchurl {
             url = "mirror://kernel/linux/kernel/v4.x/linux-${version}.tar.xz";
             url = "mirror://kernel/linux/kernel/v${lib.versions.major version}.x/linux-${version}.tar.xz";
             sha256 = "0ibayrvrnw2lw7si78vdqnr20mm1d3z0g6a0ykndvgn5vdax5x9a";
             sha256 = "0ibayrvrnw2lw7si78vdqnr20mm1d3z0g6a0ykndvgn5vdax5x9a";
       };
       };
       version = "4.19.60";
       version = "4.19.60";
       modDirVersion = "4.19.60";
       modDirVersion = "4.19.60";
      };
    };
   });
   });
</syntaxhighlight>
}
 
</nowiki>}}


=== Debugging a failed configuration  ===
=== Debugging a failed configuration  ===


As dependencies between kernel configurations items need to be addressed manually, use <code>--keep-failed</code> to inspect the intermediate nix config file after for instance the error
As dependencies between kernel configurations need to be addressed manually, use [https://nixos.org/manual/nix/stable/command-ref/nix-build#opt-keep-failed --keep-failed] to inspect the intermediate nix config file after any build failures:
{{bc|note: keeping build directory '/tmp/nix-build-linux-config-4.19.0-mptcp_v0.94.1.drv-0'}}
{{bc|note: keeping build directory '/tmp/nix-build-linux-config-4.19.0-mptcp_v0.94.1.drv-0'}}
by opening {{ic|/tmp/nix-build-linux-config-4.19.0-mptcp_v0.94.1.drv-0/.attr-0}}.


== Embedded Linux Cross-compile xconfig and menuconfig ==
== Embedded Linux Cross-compile ==
This section details the on how to get an environment that can be used to configure and cross-compile the kernel for embedded Linux development. The kernel source won't come from nix unpack phase but rather gotten externally(such as from the chip vendor github). So first clone the kernel source, then use the shell.nix below to set up the dev environment(example below is for aarch64):
 
To configure and cross-compile Linux kernels for embedded development, often distributed separately instead of using the stock kernel, you can setup a development environment as shown below:  


<syntaxHighlight lang=nix>
{{file|shell.nix|nix|3=let
let
   pkgs = import <nixpkgs> { };
   pkgs = import <nixpkgs> { };
in
in
Line 146: Line 193:
   targetPkgs = pkgs: (with pkgs;
   targetPkgs = pkgs: (with pkgs;
     [
     [
       pkgconfig
       pkg-config
       ncurses
       ncurses
       qt5.qtbase
       qt5.qtbase
Line 163: Line 210:
     exec bash
     exec bash
   '';
   '';
}).env
}).env}}
</syntaxHighlight>


Finally, cd to the kernel source dir and do development normally.
Clone the kernel sources, enter the environment using {{ic|nix-shell}}, and then do development normally.


== make menuconfig ==
== make menuconfig ==
Line 230: Line 276:
=== Packaging out-of-tree kernel modules ===
=== Packaging out-of-tree kernel modules ===
There are a couple of steps that you will most likely need to do a couple of things. Here is an annotated example:
There are a couple of steps that you will most likely need to do a couple of things. Here is an annotated example:
<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
{ stdenv, lib, fetchFromGitHub, kernel, kmod }:
{ stdenv, lib, fetchFromGitHub, kernel, kmod }:


stdenv.mkDerivation rec {
stdenv.mkDerivation rec {
   name = "v4l2loopback-dc-${version}-${kernel.version}";
   pname = "v4l2loopback-dc";
   version = "1.6";
   version = "1.6";


Line 241: Line 287:
     repo = "droidcam";
     repo = "droidcam";
     rev = "v${version}";
     rev = "v${version}";
     sha256 = "1d9qpnmqa3pfwsrpjnxdz76ipk4w37bbxyrazchh4vslnfc886fx";
     hash = "1d9qpnmqa3pfwsrpjnxdz76ipk4w37bbxyrazchh4vslnfc886fx";
   };
   };


Line 254: Line 300:
   ];
   ];


   meta = with lib; {
   meta = {
     description = "A kernel module to create V4L2 loopback devices";
     description = "A kernel module to create V4L2 loopback devices";
     homepage = "https://github.com/aramg/droidcam";
     homepage = "https://github.com/aramg/droidcam";
     license = licenses.gpl2;
     license = lib.licenses.gpl2;
     maintainers = [ maintainers.makefu ];
     maintainers = [ lib.maintainers.makefu ];
     platforms = platforms.linux;
     platforms = lib.platforms.linux;
   };
   };
}
}
</syntaxHighlight>
</syntaxhighlight>


1. For kernel modules it is necessary to disable <code>pic</code> in compiler hardenings as the kernel need different compiler flags.
1. For kernel modules it is necessary to disable <code>pic</code> in compiler hardenings as the kernel need different compiler flags.
Line 343: Line 389:
and the module will be automatically loaded after a reboot. If you want instead to load it at stage 1 (before the root is even mounted), you need to add it to <code>boot.initrd.availableKernelModules</code> and <code>boot.initrd.kernelModules</code>.
and the module will be automatically loaded after a reboot. If you want instead to load it at stage 1 (before the root is even mounted), you need to add it to <code>boot.initrd.availableKernelModules</code> and <code>boot.initrd.kernelModules</code>.


Note that if you don't reboot, you can still load manually the module using <code>modprobe yourmodulename></code>, and to automatically enable a module during configuration switch/reboot, you can put <code>modprobe yourmodulename || true</code> inside the script of a systemctl service (it is for example what does wireguard).
Note that if you don't reboot, you can still load manually the module using <code>modprobe <yourmodulename></code>, and to automatically enable a module during configuration switch/reboot, you can put <code>modprobe yourmodulename || true</code> inside the script of a systemctl service (it is for example what does wireguard).


Finally, if you want to define some options by default (used when you load manually a module using <code>modprobe</code>, or when the system boots), you can specify them in:
Finally, if you want to define some options by default (used when you load manually a module using <code>modprobe</code>, or when the system boots), you can specify them in:
Line 377: Line 423:
Save the following as <code>shell.nix</code> for your nix-shell:
Save the following as <code>shell.nix</code> for your nix-shell:


<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
{ pkgs ? import <nixpkgs> {} }:
{ pkgs ? import <nixpkgs> {} }:
let
let
Line 386: Line 432:
   # the override is optional if you need for example more build dependencies
   # the override is optional if you need for example more build dependencies
   nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.gllvm ];
   nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.gllvm ];
}
})
</syntaxHighlight>
</syntaxhighlight>


Then you can run the following commands (in the nix-shell <code>$makeFlags</code> will contain
Then you can run the following commands (in the nix-shell <code>$makeFlags</code> will contain
Line 503: Line 549:
After packaging the kernel module you can add it to your system just like an out-of-tree kernel module, it will replace the default module provided by the linux package because it has the same name:
After packaging the kernel module you can add it to your system just like an out-of-tree kernel module, it will replace the default module provided by the linux package because it has the same name:


<syntaxHighlight lang=nix>
<syntaxhighlight lang="nix">
{pkgs, config, ...}:
{pkgs, config, ...}:
let
let
Line 515: Line 561:
     (amdgpu-kernel-module.overrideAttrs (_: {
     (amdgpu-kernel-module.overrideAttrs (_: {
       patches = [ ./patches/amdgpu-foo-bar.patch ];
       patches = [ ./patches/amdgpu-foo-bar.patch ];
     })
     }))
   ];
   ];
}
}
</syntaxHighlight>
</syntaxhighlight>
 
== Sysctls ==
 
Example of configuring kernel sysctls:
<syntaxhighlight lang="nix">
{
  boot.kernel.sysctl."net.ipv4.tcp_ecn" = 1;
}
</syntaxhighlight>


== Troubleshooting ==
== Troubleshooting ==
=== Build fails ===
=== Build fails ===
==== Too high ram usage ====
==== Too high ram usage ====
turn off `DEBUG_INFO_BTF`
turn off <code>DEBUG_INFO_BTF</code>


== See also ==
== See also ==

Latest revision as of 20:09, 20 November 2024

By default, the latest LTS linux kernel is installed (Linux Kernel Version History).

Configuration

You can change the installed kernel using boot.kernelPackages. To use the latest release:

/etc/nixos/configuration.nix
{
  boot.kernelPackages = pkgs.linuxPackages_latest;
}

Then rebuild your system and reboot to use your new kernel:

$ sudo nixos-rebuild boot
$ sudo reboot

List available kernels

You can list available kernels using nix repl (previously nix-repl) by typing the package name and using the tab completion:

$ nix repl
nix-repl> :l <nixpkgs>
Added 12607 variables.

nix-repl> pkgs.linuxPackages
pkgs.linuxPackages                           pkgs.linuxPackages_custom
pkgs.linuxPackages-libre                     pkgs.linuxPackages_custom_tinyconfig_kernel
pkgs.linuxPackages-rt                        pkgs.linuxPackages_hardened
pkgs.linuxPackages-rt_latest                 pkgs.linuxPackages_latest
pkgs.linuxPackagesFor                        pkgs.linuxPackages_latest-libre
pkgs.linuxPackages_4_14                      pkgs.linuxPackages_latest_hardened
pkgs.linuxPackages_4_19                      pkgs.linuxPackages_latest_xen_dom0
pkgs.linuxPackages_4_19_hardened             pkgs.linuxPackages_latest_xen_dom0_hardened
pkgs.linuxPackages_4_9                       pkgs.linuxPackages_lqx
pkgs.linuxPackages_5_10                      pkgs.linuxPackages_rpi0
pkgs.linuxPackages_5_10_hardened             pkgs.linuxPackages_rpi02w
pkgs.linuxPackages_5_15                      pkgs.linuxPackages_rpi1
pkgs.linuxPackages_5_15_hardened             pkgs.linuxPackages_rpi2
pkgs.linuxPackages_5_18                      pkgs.linuxPackages_rpi3
pkgs.linuxPackages_5_19                      pkgs.linuxPackages_rpi4
pkgs.linuxPackages_5_4                       pkgs.linuxPackages_rt_5_10
pkgs.linuxPackages_5_4_hardened              pkgs.linuxPackages_rt_5_15
pkgs.linuxPackages_6_0                       pkgs.linuxPackages_rt_5_4
pkgs.linuxPackages_6_1                       pkgs.linuxPackages_rt_6_1
pkgs.linuxPackages_6_1_hardened              pkgs.linuxPackages_testing
pkgs.linuxPackages_6_2                       pkgs.linuxPackages_testing_bcachefs
pkgs.linuxPackages_6_3                       pkgs.linuxPackages_xanmod
pkgs.linuxPackages_6_4                       pkgs.linuxPackages_xanmod_latest
pkgs.linuxPackages_6_5                       pkgs.linuxPackages_xanmod_stable
pkgs.linuxPackages_6_6                       pkgs.linuxPackages_xen_dom0
pkgs.linuxPackages_6_6_hardened              pkgs.linuxPackages_xen_dom0_hardened
pkgs.linuxPackages_6_7                       pkgs.linuxPackages_zen
pkgs.linuxPackages_6_8

Custom kernel modules

Note that if you deviate from the default kernel version, you should also take extra care that extra kernel modules must match the same version. The safest way to do this is to use config.boot.kernelPackages to select the correct module set:

/etc/nixos/configuration.nix
{ config, ... }:
{
  boot.extraModulePackages = with config.boot.kernelPackages; [ bbswitch ];
}

Customizing kernel module parameters

You can use boot.extraModprobeConfig, which is analogous to creating modprobe.conf files in /etc/modprobe.d/ in regular Linux distributions. This can be used for both built-in and loadable kernel modules.

/etc/nixos/configuration.nix
{
  boot.extraModprobeConfig = ''
    # example settings
    options yourmodulename optionA=valueA optionB=valueB # syntax
    options thinkpad_acpi  fan_control=1                 # example #1 kernel module parameter
    options usbcore        blinkenlights=1               # example #2 kernel module parameter
  '';
}

boot.kernelParams can be used to set additional kernel command line arguments at boot time. It can only be used for built-in modules. For example:

/etc/nixos/configuration.nix
{
  boot.kernelParams = [
    "quiet"
    "splash"
    "usbcore.blinkenlights=1"
  ];
}

Enable SysRq

The Linux kernel is known[1] to not handle out-of-memory situation properly and can freeze for a long time, often leaving no option but doing a hard reboot. The SysRq shortcuts can be used to trigger a more graceful reboot. However, most keys are disabled by default on NixOS. To enable:

/etc/nixos/configuration.nix
{
  boot.kernel.sysctl."kernel.sysrq" = 1;
}

Useful shortcuts, triggered using Alt+SysRq+<key>:

  • h: Print help to the system log.
  • f: Trigger the kernel oom killer.
  • s: Sync data to disk before triggering the reset options below.
  • e: SIGTERM all processes except PID 0.
  • i: SIGKILL all processes except PID 0.
  • b: Reboot the system.

Check journalctl to see if you are triggering the shortcuts correctly, which might be different for your keyboard, as noted in the Wikipedia page.

Also see services.earlyoom.enable and systemd.oomd.enable.

Custom configuration

It is sometimes desirable to change the configuration of your kernel, while keeping the kernel version itself managed through Nixpkgs.

One way is to use boot.kernelPatches[2][3]. For example, boot.crashDump.enable is configured as shown below. Note that the CONFIG_ prefix is not used in the configuration names.

{
      boot.kernelPatches = [ {
        name = "crashdump-config";
        patch = null;
        extraConfig = ''
                CRASH_DUMP y
                DEBUG_INFO y
                PROC_VMCORE y
                LOCKUP_DETECTOR y
                HARDLOCKUP_DETECTOR y
              '';
        } ];
}

Another way is to create a overlay to configure either extraConfig or structuredExtraConfig. This allows for more flexibility, since you can basically override any available kernel option[4]. Using structuredExtraConfig is recommended as it checks if the configured option are correct. You may also want to set ignoreConfigErrors to avoid error: unused option during builds.

/etc/nixos/configuration.nix
{
  nixpkgs = {
    overlays = [
      (self: super: {
        linuxZenWMuQSS = pkgs.linuxPackagesFor (pkgs.linuxKernel.kernels.linux_zen.override {
          structuredExtraConfig = with lib.kernel; {
            SCHED_MUQSS = yes;
          };
          ignoreConfigErrors = true;
        });
      })
    ];
  };
}

Pinning a kernel version

/etc/nixos/configuration.nix
{
  boot.kernelPackages = pkgs.linuxPackagesFor (pkgs.linuxKernel.kernels.linux_4_19.override {
    argsOverride = rec {
      src = pkgs.fetchurl {
            url = "mirror://kernel/linux/kernel/v${lib.versions.major version}.x/linux-${version}.tar.xz";
            sha256 = "0ibayrvrnw2lw7si78vdqnr20mm1d3z0g6a0ykndvgn5vdax5x9a";
      };
      version = "4.19.60";
      modDirVersion = "4.19.60";
    };
  });
}

Debugging a failed configuration

As dependencies between kernel configurations need to be addressed manually, use --keep-failed to inspect the intermediate nix config file after any build failures:

note: keeping build directory '/tmp/nix-build-linux-config-4.19.0-mptcp_v0.94.1.drv-0'

Embedded Linux Cross-compile

To configure and cross-compile Linux kernels for embedded development, often distributed separately instead of using the stock kernel, you can setup a development environment as shown below:

shell.nix
let
  pkgs = import <nixpkgs> { };
in
(pkgs.buildFHSUserEnv {
  name = "kernel-build-env";
  targetPkgs = pkgs: (with pkgs;
    [
      pkg-config
      ncurses
      qt5.qtbase
      # new gcc usually causes issues with building kernel so use an old one
      pkgsCross.aarch64-multiplatform.gcc8Stdenv.cc
      (hiPrio gcc8)
    ]
    ++ pkgs.linux.nativeBuildInputs);
  runScript = pkgs.writeScript "init.sh" ''
    export ARCH=arm64
    export hardeningDisable=all
    export CROSS_COMPILE=aarch64-unknown-linux-gnu-
    export PKG_CONFIG_PATH="${pkgs.ncurses.dev}/lib/pkgconfig:${pkgs.qt5.qtbase.dev}/lib/pkgconfig"
    export QT_QPA_PLATFORM_PLUGIN_PATH="${pkgs.qt5.qtbase.bin}/lib/qt-${pkgs.qt5.qtbase.version}/plugins"
    export QT_QPA_PLATFORMTHEME=qt5ct
    exec bash
  '';
}).env

Clone the kernel sources, enter the environment using nix-shell, and then do development normally.

make menuconfig

It is (currently) not possible to run make menuconfig in the checked out linux kernel sources. This is because ncurses is not part of your working environment when you start it with nix-shell '<nixpkgs>' -A linuxPackages.kernel.

This nix-shell hack adds ncurses as a build dependency to the kernel:

$ nix-shell -E 'with import <nixpkgs> {}; linux.overrideAttrs (o: {nativeBuildInputs=o.nativeBuildInputs ++ [ pkg-config ncurses ];})'
[nix-shell] $ unpackPhase && cd linux-*
[nix-shell] $ patchPhase
[nix-shell] $ make menuconfig

(thanks to sphalerite)

make xconfig

Similarly to make menuconfig, you need to import qt in the environment:

$ nix-shell -E 'with import <nixpkgs> {}; linux.overrideAttrs (o: {nativeBuildInputs=o.nativeBuildInputs ++ [ pkg-config qt5.qtbase ];})'

If the source was unpacked and an initial config exists, you can run make xconfig KCONFIG_CONFIG=build/.config

Requesting a change in the default nixos kernel configuration

Please provide a comparison with other distributions' kernel: - arch: https://github.com/archlinux/svntogit-packages/blob/packages/linux/trunk/config - debian: https://salsa.debian.org/kernel-team/linux/blob/master/debian/config/config and the ARCH specific ones

Booting a kernel from a custom source

The following example shows how to configure NixOS to compile and boot a kernel from a custom source, and with custom configuration options.

{ pkgs, ... }:

{
  boot.kernelPackages = let
      linux_sgx_pkg = { fetchurl, buildLinux, ... } @ args:

        buildLinux (args // rec {
          version = "5.4.0-rc3";
          modDirVersion = version;

          src = fetchurl {
            url = "https://github.com/jsakkine-intel/linux-sgx/archive/v23.tar.gz";
            # After the first build attempt, look for "hash mismatch" and then 2 lines below at the "got:" line.
            # Use "sha256-....." value here.
            hash = "";
          };
          kernelPatches = [];

          extraConfig = ''
            INTEL_SGX y
          '';

          extraMeta.branch = "5.4";
        } // (args.argsOverride or {}));
      linux_sgx = pkgs.callPackage linux_sgx_pkg{};
    in 
      pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor linux_sgx);
}

Out-of-tree kernel modules

Packaging out-of-tree kernel modules

There are a couple of steps that you will most likely need to do a couple of things. Here is an annotated example:

{ stdenv, lib, fetchFromGitHub, kernel, kmod }:

stdenv.mkDerivation rec {
  pname = "v4l2loopback-dc";
  version = "1.6";

  src = fetchFromGitHub {
    owner = "aramg";
    repo = "droidcam";
    rev = "v${version}";
    hash = "1d9qpnmqa3pfwsrpjnxdz76ipk4w37bbxyrazchh4vslnfc886fx";
  };

  sourceRoot = "source/linux/v4l2loopback";
  hardeningDisable = [ "pic" "format" ];                                             # 1
  nativeBuildInputs = kernel.moduleBuildDependencies;                       # 2

  makeFlags = [
    "KERNELRELEASE=${kernel.modDirVersion}"                                 # 3
    "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"    # 4
    "INSTALL_MOD_PATH=$(out)"                                               # 5
  ];

  meta = {
    description = "A kernel module to create V4L2 loopback devices";
    homepage = "https://github.com/aramg/droidcam";
    license = lib.licenses.gpl2;
    maintainers = [ lib.maintainers.makefu ];
    platforms = lib.platforms.linux;
  };
}

1. For kernel modules it is necessary to disable pic in compiler hardenings as the kernel need different compiler flags.

2. In addition to other dependencies in nativeBuildInputs you should include kernel.moduleBuildDependencies as this propagates additional libraries required during the build.

3. Some kernel modules try guess the kernel version based on the running kernel via uname. Usually they save this information in a makefile variable like KERNELRELEASE. If this is the case you can override the kernel version via makeFlags. The right kernel version string can be found in kernel.modDirVersion.

4. You need to find out how the build environment (Makefile in general) finds the kernel tree. This is sometimes KDIR and sometimes KERNEL_DIR.

5. Lastly it is required to give the kernel build system the right location where to install the kernel module. This is done by setting INSTALL_MOD_PATH to $out Otherwise an error like mkdir: cannot create directory '/lib': Permission denied is generated.

You can then call your program using something like let yourprogram = config.boot.kernelPackages.callPackage ./your-derivation.nix {}; in … (or if you want to compile it for the default kernel used by nix you can use pkgs.linuxPackages.callPackage but be aware that if you install it on a system running another kernel it will not work) and install the module in the kernel using boot.extraModulePackages = [ yourprogram ];.

Developing out-of-tree kernel modules

See also: NixOS Manual, 12.2. Developing kernel modules

If you work on an out-of-tree kernel module the workflow could look as follow:

#include <linux/module.h>
#define MODULE_NAME "hello"
static int __init hello_init(void)
{
    printk(KERN_INFO "hello world!\n");
    return 0;
}
static void __exit hello_cleanup(void) {
    printk(KERN_INFO "bye world!\n");
}
module_init(hello_init);
module_exit(hello_cleanup);
obj-m += hello.o
$ nix-shell '<nixpkgs>' -A linux.dev
$ make -C $(nix-build -E '(import <nixpkgs> {}).linux.dev' --no-out-link)/lib/modules/*/build M=$(pwd) modules
$ insmod ./hello.ko
$ dmesg | grep hello
[   82.027229] hello world!

If wishing to develop out-of-tree kernel modules for kernels aside from the default variant shipped under pkgs.linuxPackages, you can replace linux.dev with (for instance) linuxPackages_latest.kernel.dev.

If you want a local development environment and are only interested in Module.symvers, then you can do instead:

$ cp $(nix-build -E '(import <nixpkgs> {}).linux.dev' --no-out-link)/lib/modules/*/build/Module.symvers .

Loading out-of-tree kernel modules

As far as I understand, if you developed a kernel module, you should end up with having some .ko files inside a subfolder inside $out/lib/modules/${kernel.modDirVersion}. Now, if you want to make your module loadable inside the kernel by modprobe, you should do:

boot.extraModulePackages = [ yourmodulename ];

Then, the user can load it using:

$ sudo modprobe yourmodulename

or unload it using

$ sudo modprobe -r yourmodulename

However, if you want to autoload your module at startup in stage 2, you need to do:

boot.kernelModules = [ "yourmodulename" ];

and the module will be automatically loaded after a reboot. If you want instead to load it at stage 1 (before the root is even mounted), you need to add it to boot.initrd.availableKernelModules and boot.initrd.kernelModules.

Note that if you don't reboot, you can still load manually the module using modprobe <yourmodulename>, and to automatically enable a module during configuration switch/reboot, you can put modprobe yourmodulename || true inside the script of a systemctl service (it is for example what does wireguard).

Finally, if you want to define some options by default (used when you load manually a module using modprobe, or when the system boots), you can specify them in:

boot.extraModprobeConfig = ''
  options yourmodulename optionA=valueA optionB=valueB
'';

If you have developed a Nix package for your module, and want to only add the module to your configuration.nix instead of complete rebuilding the system based on your local nixpkgs, you need to update boot.kernelPackages as well, so kernel and modules can match each other:

{
  boot = {
    kernelPackages = pkgs.wip.linuxPackages;
    extraModulePackages = with config.boot.kernelPackages; [ yourmodulename ];
  }
  nixpkgs.config = {
    # Allow unfree modules
    allowUnfree = true;
    packageOverrides = pkgs: {
      wip = import (fetchGit { url = "/home/user/nixpkgs"; shallow = true;}) {
        config = config.nixpkgs.config;
      };
    };
  };
};

Cross-compiling Linux from source

Save the following as shell.nix for your nix-shell:

{ pkgs ? import <nixpkgs> {} }:
let
  # more platforms are defined here: https://github.com/NixOS/nixpkgs/blob/master/lib/systems/examples.nix
  aarch64 = pkgs.pkgsCross.aarch64-multiplatform;
in
aarch64.linux.overrideAttrs (old: {
  # the override is optional if you need for example more build dependencies
  nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.gllvm ];
})

Then you can run the following commands (in the nix-shell $makeFlags will contain the necessary cross-compiling arguments)

$ nix-shell
# to configure
$ nix-shell> make $makeFlags defconfig -j $(nproc)
# to build
$ nix-shell> make $makeFlags -j $(nproc)

Compiling Linux with clang

Save this as shell.nix

{ pkgs ? import <nixpkgs> {} }:
pkgs.linux.override {
   # needs to be clang11Stdenv or newer
   stdenv = clang11Stdenv;
}

Then you can use:

$ nix-shell
# to configure
$ nix-shell> make $makeFlags defconfig -j $(nproc)
# to build
$ nix-shell> make $makeFlags -j $(nproc)

Overriding kernel packages

In order to override linuxPackages, use the extend attribute. Example:

linuxPackages.extend (self: super: {
  xpadneo = super.xpadneo.overrideAttrs (o: rec {
    src = pkgs.fetchFromGitHub {
      # Custom override goes here.
    };
  });
});

Here is a fully worked example to enable debugging for the zfsUnstable module:

 nixpkgs.overlays = [
    (self: super: {
      linuxPackages = super.linuxPackages.extend (lpself: lpsuper: {
        zfsUnstable = super.linuxPackages.zfsUnstable.overrideAttrs (oldAttrs: {
          configureFlags = oldAttrs.configureFlags ++ [ "--enable-debug" ];
        });
      });
    })
  ];

Patching a single In-tree kernel module

If you wish to patch a single kernel module, you can avoid rebuilding the entire linux kernel by packaging that module applying patches to it.

Packaging a single kernel module

Here is an example for how to package the amdgpu kernel module.

{ pkgs
, lib
, kernel ? pkgs.linuxPackages_latest.kernel
}:

pkgs.stdenv.mkDerivation {
  pname = "amdgpu-kernel-module";
  inherit (kernel) src version postPatch nativeBuildInputs;

  kernel_dev = kernel.dev;
  kernelVersion = kernel.modDirVersion;

  modulePath = "drivers/gpu/drm/amd/amdgpu";

  buildPhase = ''
    BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build

    cp $BUILT_KERNEL/Module.symvers .
    cp $BUILT_KERNEL/.config        .
    cp $kernel_dev/vmlinux          .

    make "-j$NIX_BUILD_CORES" modules_prepare
    make "-j$NIX_BUILD_CORES" M=$modulePath modules
  '';

  installPhase = ''
    make \
      INSTALL_MOD_PATH="$out" \
      XZ="xz -T$NIX_BUILD_CORES" \
      M="$modulePath" \
      modules_install
  '';

  meta = {
    description = "AMD GPU kernel module";
    license = lib.licenses.gpl3;
  };
}

Replacing default kernel modules

After packaging the kernel module you can add it to your system just like an out-of-tree kernel module, it will replace the default module provided by the linux package because it has the same name:

{pkgs, config, ...}:
let
  amdgpu-kernel-module = pkgs.callPackage ./amdgpu-kernel-module.nix {
    # Make sure the module targets the same kernel as your system is using.
    kernel = config.boot.kernelPackages.kernel;
  };
in
{
  boot.extraModulePackages = [
    (amdgpu-kernel-module.overrideAttrs (_: {
      patches = [ ./patches/amdgpu-foo-bar.patch ];
    }))
  ];
}

Sysctls

Example of configuring kernel sysctls:

{
  boot.kernel.sysctl."net.ipv4.tcp_ecn" = 1;
}

Troubleshooting

Build fails

Too high ram usage

turn off DEBUG_INFO_BTF

See also

Kernel Debugging with QEMU

References