Skip to content

Commit

Permalink
doc: consolidate info on manual linux kernel configs
Browse files Browse the repository at this point in the history
The Nixpkgs documentation on the linux kernel builders focused on
using and extending kernels that were already packaged, but never
mentioned that it's possible to also build a kernel almost "from
scratch".
The NixOS documentation went a bit deeper on manual linux kernel
configs, but that information wasn't particularly NixOS-specific.

This commit consolidates the information related to building the
kernel on Nixpkgs's documentation, while keeping any additional
NixOS-specific information on NixOS's documentation.

An additional README.md was created for contributor-facing
documentation.
  • Loading branch information
DanielSidhion committed Oct 27, 2023
1 parent 200aa03 commit a8bc568
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 81 deletions.
116 changes: 98 additions & 18 deletions doc/builders/packages/linux.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,120 @@

The Nix expressions to build the Linux kernel are in [`pkgs/os-specific/linux/kernel`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/linux/kernel).

The function that builds the kernel has an argument `kernelPatches` which should be a list of `{name, patch, extraConfig}` attribute sets, where `name` is the name of the patch (which is included in the kernel’s `meta.description` attribute), `patch` is the patch itself (possibly compressed), and `extraConfig` (optional) is a string specifying extra options to be concatenated to the kernel configuration file (`.config`).
The function [`pkgs.buildLinux`](https://github.com/NixOS/nixpkgs/blob/f9d4f5f23ce7ccb7ad91d6d2150e367baf7014f6/pkgs/os-specific/linux/kernel/generic.nix) builds a kernel with [common configuration values](hthttps://github.com/NixOS/nixpkgs/blob/f9d4f5f23ce7ccb7ad91d6d2150e367baf7014f6/pkgs/os-specific/linux/kernel/common-config.nix).
This is the preferred option unless you have a very specific use case.
Most kernels packaged in Nixpkgs are built that way, and it will also generate kernels suitable for NixOS.
[`pkgs.linuxManualConfig`](https://github.com/NixOS/nixpkgs/blob/f9d4f5f23ce7ccb7ad91d6d2150e367baf7014f6/pkgs/os-specific/linux/kernel/manual-config.nix) requires a complete configuration to be passed, and has less additional features than `pkgs.buildLinux`, such as the use of common configuration values and exposing the `features` attribute, as explained below.

The kernel derivation exports an attribute `features` specifying whether optional functionality is or isn’t enabled. This is used in NixOS to implement kernel-specific behaviour. For instance, if the kernel has the `iwlwifi` feature (i.e., has built-in support for Intel wireless chipsets), then NixOS doesn’t have to build the external `iwlwifi` package:
Both functions have an argument `kernelPatches` which should be a list of `{name, patch, extraConfig}` attribute sets, where `name` is the name of the patch (which is included in the kernel’s `meta.description` attribute), `patch` is the patch itself (possibly compressed), and `extraConfig` (optional) is a string specifying extra options to be concatenated to the kernel configuration file (`.config`).

The kernel derivation created with `pkgs.buildLinux` exports an attribute `features` specifying whether optional functionality is or isn’t enabled. This is used in NixOS to implement kernel-specific behaviour.

:::{.example #ex-skip-package-from-kernel-feature}

# Skipping an external package because of a kernel feature

For instance, if the kernel has the `iwlwifi` feature (i.e., has built-in support for Intel wireless chipsets), then NixOS doesn’t have to build the external `iwlwifi` package:

```nix
modulesTree = [kernel]
++ pkgs.lib.optional (!kernel.features ? iwlwifi) kernelPackages.iwlwifi
++ ...;
```

How to add a new (major) version of the Linux kernel to Nixpkgs:
:::

1. Copy the old Nix expression (e.g., `linux-2.6.21.nix`) to the new one (e.g., `linux-2.6.22.nix`) and update it.
If you are using a kernel packaged in Nixpkgs, you can customize it by overriding its arguments. For details on how each argument affects the generated kernel, refer to [the source](https://github.com/NixOS/nixpkgs/blob/f9d4f5f23ce7ccb7ad91d6d2150e367baf7014f6/pkgs/os-specific/linux/kernel/generic.nix) for `pkgs.buildLinux`.

2. Add the new kernel to the `kernels` attribute set in `linux-kernels.nix` (e.g., create an attribute `kernel_2_6_22`).
:::{.example #ex-overriding-kernel-derivation}

3. Now we’re going to update the kernel configuration. First unpack the kernel. Then for each supported platform (`i686`, `x86_64`, `uml`) do the following:
# Overriding the kernel derivation

1. Make a copy from the old config (e.g., `config-2.6.21-i686-smp`) to the new one (e.g., `config-2.6.22-i686-smp`).
Assuming you are using the kernel from `pkgs.linux_latest`:

2. Copy the config file for this platform (e.g., `config-2.6.22-i686-smp`) to `.config` in the kernel source tree.
```nix
pkgs.linux_latest.override {
ignoreConfigErrors = true;
autoModules = false;
kernelPreferBuiltin = true;
extraStructuredConfig = with lib.kernel; {
DEBUG_KERNEL = yes;
FRAME_POINTER = yes;
KGDB = yes;
KGDB_SERIAL_CONSOLE = yes;
DEBUG_INFO = yes;
};
}
```

3. Run `make oldconfig ARCH={i386,x86_64,um}` and answer all questions. (For the uml configuration, also add `SHELL=bash`.) Make sure to keep the configuration consistent between platforms (i.e., don’t enable some feature on `i686` and disable it on `x86_64`).
:::

4. If needed, you can also run `make menuconfig`:
## Manual kernel configuration {#sec-manual-kernel-configuration}

```ShellSession
$ nix-env -f "<nixpkgs>" -iA ncurses
$ export NIX_CFLAGS_LINK=-lncurses
$ make menuconfig ARCH=arch
```
Sometimes it may not be desirable to use kernels built with `pkgs.buildLinux`, especially if most of the common configuration has to be altered or disabled to achieve a kernel as expected by the target use case.
An example of this is building a kernel for use in a VM or micro VM. You can use `pkgs.linuxManualConfig` in these cases. It requires the `src`, `version`, and `configfile` attributes to be specified.

5. Copy `.config` over the new config file (e.g., `config-2.6.22-i686-smp`).
:::{.example #ex-using-linux-manual-config}

4. Test building the kernel: `nix-build -A linuxKernel.kernels.kernel_2_6_22`. If it compiles, ship it! For extra credit, try booting NixOS with it.
# Using `pkgs.linuxManualConfig` with a specific source, version, and config file

5. It may be that the new kernel requires updating the external kernel modules and kernel-dependent packages listed in the `linuxPackagesFor` function in `linux-kernels.nix` (such as the NVIDIA drivers, AUFS, etc.). If the updated packages aren’t backwards compatible with older kernels, you may need to keep the older versions around.
```nix
{ pkgs, ... }: {
version = "6.1.55";
src = pkgs.fetchurl {
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${version}.tar.xz";
hash = "sha256:1h0mzx52q9pvdv7rhnvb8g68i7bnlc9rf8gy9qn4alsxq4g28zm8";
};
configfile = ./path_to_config_file;
linux = pkgs.linuxManualConfig {
inherit version src configfile;
allowImportFromDerivation = true;
};
}
```

If necessary, the version string can be slightly modified to explicitly mark it as a custom version. If you do so, ensure the `modDirVersion` attribute matches the source's version, otherwise the build will fail.

```nix
{ pkgs, ... }: {
version = "6.1.55-custom";
modDirVersion = "6.1.55";
src = pkgs.fetchurl {
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${modDirVersion}.tar.xz";
hash = "sha256:1h0mzx52q9pvdv7rhnvb8g68i7bnlc9rf8gy9qn4alsxq4g28zm8";
};
configfile = ./path_to_config_file;
linux = pkgs.linuxManualConfig {
inherit version modDirVersion src configfile;
allowImportFromDerivation = true;
};
}
```

:::

Additional attributes can be used with `linuxManualConfig` for further customisation. You're encouraged to read [`pkgs/os-specific/linux/kernel/manual-config.nix`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/linux/kernel/manual-config.nix) to understand how to use them.

To edit the `.config` file for Linux X.Y, proceed as follows:

```ShellSession
$ nix-shell '<nixpkgs>' -A linuxKernel.kernels.linux_X_Y.configEnv
$ unpackPhase
$ cd linux-*
$ make nconfig
```

## Developing kernel modules {#sec-linux-kernel-developing-modules}

When developing kernel modules it's often convenient to run
edit-compile-run loop as quickly as possible. See below snippet as an
example of developing `mellanox` drivers.

```ShellSession
$ nix-build '<nixpkgs>' -A linuxPackages.kernel.dev
$ nix-shell '<nixpkgs>' -A linuxPackages.kernel
$ unpackPhase
$ cd linux-*
$ make -C $dev/lib/modules/*/build M=$(pwd)/drivers/net/ethernet/mellanox modules
# insmod ./drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
```
66 changes: 3 additions & 63 deletions nixos/doc/manual/configuration/linux-kernel.chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,77 +84,17 @@ available parameters, run `sysctl -a`.

## Building a custom kernel {#sec-linux-config-customizing}

You can customize the default kernel configuration by overriding the arguments for your kernel package:

```nix
pkgs.linux_latest.override {
ignoreConfigErrors = true;
autoModules = false;
kernelPreferBuiltin = true;
extraStructuredConfig = with lib.kernel; {
DEBUG_KERNEL = yes;
FRAME_POINTER = yes;
KGDB = yes;
KGDB_SERIAL_CONSOLE = yes;
DEBUG_INFO = yes;
};
}
```

See `pkgs/os-specific/linux/kernel/generic.nix` for details on how these arguments
affect the generated configuration. You can also build a custom version of Linux by calling
`pkgs.buildLinux` directly, which requires the `src` and `version` arguments to be specified.
Refer to the Nixpkgs manual for the various ways of [building a custom kernel](https://nixos.org/nixpkgs/manual#sec-linux-kernel).

To use your custom kernel package in your NixOS configuration, set

```nix
boot.kernelPackages = pkgs.linuxPackagesFor yourCustomKernel;
```

Note that this method will use the common configuration defined in `pkgs/os-specific/linux/kernel/common-config.nix`,
which is suitable for a NixOS system.

If you already have a generated configuration file, you can build a kernel that uses it with `pkgs.linuxManualConfig`:

```nix
let
baseKernel = pkgs.linux_latest;
in pkgs.linuxManualConfig {
inherit (baseKernel) src modDirVersion;
version = "${baseKernel.version}-custom";
configfile = ./my_kernel_config;
allowImportFromDerivation = true;
}
```

::: {.note}
The build will fail if `modDirVersion` does not match the source's `kernel.release` file,
so `modDirVersion` should remain tied to `src`.
:::

To edit the `.config` file for Linux X.Y, proceed as follows:

```ShellSession
$ nix-shell '<nixpkgs>' -A linuxKernel.kernels.linux_X_Y.configEnv
$ unpackPhase
$ cd linux-*
$ make nconfig
```

## Developing kernel modules {#sec-linux-config-developing-modules}

When developing kernel modules it's often convenient to run
edit-compile-run loop as quickly as possible. See below snippet as an
example of developing `mellanox` drivers.

```ShellSession
$ nix-build '<nixpkgs>' -A linuxPackages.kernel.dev
$ nix-shell '<nixpkgs>' -A linuxPackages.kernel
$ unpackPhase
$ cd linux-*
$ make -C $dev/lib/modules/*/build M=$(pwd)/drivers/net/ethernet/mellanox modules
# insmod ./drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
```
[Moved](https://nixos.org/nixpkgs/manual#sec-linux-kernel-developing-modules) to the Nixpkgs manual.

## ZFS {#sec-linux-zfs}

Expand All @@ -163,7 +103,7 @@ available Linux kernel. It is recommended to use the latest available LTS that's
with ZFS. Usually this is the default kernel provided by nixpkgs (i.e. `pkgs.linuxPackages`).

Alternatively, it's possible to pin the system to the latest available kernel
version *that is supported by ZFS* like this:
version _that is supported by ZFS_ like this:

```nix
{
Expand Down
27 changes: 27 additions & 0 deletions pkgs/os-specific/linux/kernel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# How to add a new (major) version of the Linux kernel to Nixpkgs:

1. Copy the old Nix expression (e.g., `linux-2.6.21.nix`) to the new one (e.g., `linux-2.6.22.nix`) and update it.

2. Add the new kernel to the `kernels` attribute set in `linux-kernels.nix` (e.g., create an attribute `kernel_2_6_22`).

3. Now we’re going to update the kernel configuration. First unpack the kernel. Then for each supported platform (`i686`, `x86_64`, `uml`) do the following:

1. Make a copy from the old config (e.g., `config-2.6.21-i686-smp`) to the new one (e.g., `config-2.6.22-i686-smp`).

2. Copy the config file for this platform (e.g., `config-2.6.22-i686-smp`) to `.config` in the kernel source tree.

3. Run `make oldconfig ARCH={i386,x86_64,um}` and answer all questions. (For the uml configuration, also add `SHELL=bash`.) Make sure to keep the configuration consistent between platforms (i.e., don’t enable some feature on `i686` and disable it on `x86_64`).

4. If needed, you can also run `make menuconfig`:

```ShellSession
$ nix-env -f "<nixpkgs>" -iA ncurses
$ export NIX_CFLAGS_LINK=-lncurses
$ make menuconfig ARCH=arch
```

5. Copy `.config` over the new config file (e.g., `config-2.6.22-i686-smp`).

4. Test building the kernel: `nix-build -A linuxKernel.kernels.kernel_2_6_22`. If it compiles, ship it! For extra credit, try booting NixOS with it.

5. It may be that the new kernel requires updating the external kernel modules and kernel-dependent packages listed in the `linuxPackagesFor` function in `linux-kernels.nix` (such as the NVIDIA drivers, AUFS, etc.). If the updated packages aren’t backwards compatible with older kernels, you may need to keep the older versions around.

0 comments on commit a8bc568

Please sign in to comment.