Concepts

This page is going to elaborate on some important concepts in autokernel.

Modules

Modules are blocks in the autokernel configuration which are used to write the actual kernel configuration. A module can set symbol values, merge external kconf files, assert expressions, use (include) other modules and add commandline strings. They are intended to provide a level of encapsulation for groups of symbols.

Example module

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
module example {
    # Asserts that the configured kernel is at least on version 4.0
    assert $kernel_version >= 4.0: "this kernel is too old!";

    # Merge in the x86_64 defconfig
    merge "{KERNEL_DIR}/arch/x86/configs/x86_64_defconfig";

    # Sets DEFAULT_MMAP_MIN_ADDR if X86 is set
    if X86 {
        set DEFAULT_MMAP_MIN_ADDR 65536;
    }

    # Include another module
    use some_dependency;
}

module some_dependency {
    # ...
}

Pinning symbol values

The first important concept is pinning. As soon as a symbol’s value is changed or observed, it will be pinned, meaning the value is then fixed. In the beginning, all symbols will start with their default values, as specified by the kernel’s Kconfig.

Successive assigments to these symbols will become hard errors, if they would change the pinned value. This allows modules to use logic based on symbol values, without imposing implicit ordering constraints, or surprise pitfalls down the road. Wrong ordering will lead to errors instead of silently breaking previous assumptions.

Note

Conflicts are always errors. This ensures that the same conditions always has the same outcome, no matter where it stands in the configuration.

Pinning Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Sets and pins NET to [y] (cause: explicit assignment)
set NET y;

# Pins USB to its current value (cause: evaluation in condition)
if USB {
    set EXAMPLE y;
}

# Does not pin BT, because no statement depends on the condition
if BT { }

# Does nothing if IWLWIFI is already pinned. Otherwise assigns
# *without* pinning. Useful to set new defaults for values
# but still allowing explicit changes.
try set IWLWIFI y;

Conflict Example

1
2
3
4
5
6
7
8
9
# If NET is enabled, also enable TUN. This pins NET.
if NET {
    set TUN y;
}

# Assume NET was [y]. In that case NET is pinned to [y] in line 3.
# This would break the assumption in line 3, as a re-evaluation of
# the condition would have a different result.
set NET n; # error: confilict

Implicit vs. explicit changes

There are explicit and implict assignments of symbol values. All direct assignments via set are explicit. An implicit assignment occurrs, when an explicit assignment triggers a change in a symbols that depends on the assigned symbol.

Note

Explicit changes will pin the value of a symbols, while implicit changes do not.

Implicit assignments also occurr when using the merge statement. They can also be forced by using try set instead of just set. This should only be used in special occasions, like when you want to set a new default value for a symbol while still allowing the user to override it.

Correct usage of try set

It’s a common pattern to use try set directly followed by a conditional on the same symbol. This way you can ensure a module works with either setting, but add a default in case the user didn’t care:

1
2
3
4
5
6
7
# By default disable DEVMEM
try set DEVMEM n;

# If the user has still enabled it, at least enable STRICT mode
if DEVMEM {
    set STRICT_DEVMEM y;
}

Warning

Do not use try set to resolve conflicts! A conflict always means that there is something wrong with your configuration or ordering. Only use try set to set new defaults.

Explicit assignments

1
2
3
4
5
# Explicitly sets NET to n
set NET n;

# Explicitly sets symbols mentioned in the given kconf file
merge "{KERNEL_DIR}/arch/x86/configs/x86_64_defconfig";

Implicit assignments

1
2
3
4
5
6
# Implicitly sets NET to n
try set NET n;

# Implicitly assigns a lot of other options
# (all that indirectly depend on MODULES)
set MODULES n;