L25: Why Every Kernel Developer Must Know Kconfig

 

Kconfig Language — Writing Custom Linux Kernel Menu Entries
Free Linux Kernel Programming Course  ·  Chapter 2  ·  Lecture 25
⏱ ~20 min read
🎯 Beginner Friendly
🐧 Linux 6.x Updated
🅾 100% Free

Topics Covered in This Lecture:

Kconfig Language
bool / tristate / int / string
depends on
select
default values
range keyword
help text
Custom menu entry
Kbuild system
.config file
HAVE_* pattern
IS_ENABLED() macro

📷 Suggested Image for This Post

Use a screenshot of make menuconfig running in a Linux terminal showing the Kernel Hacking menu. Annotate with arrows pointing to a bool option [*] and a tristate option <M>. Alt text: “Linux kernel menuconfig interface showing bool and tristate Kconfig options — free linux kernel development course”. This kind of real terminal screenshot builds trust and ranks well for image search on linux kernel configuration keywords.

Why Every Kernel Developer Must Know Kconfig

When you run make menuconfig on the Linux kernel source, hundreds of options appear — “Enable SMP support”, “Compile with debug info”, “Support for USB devices”. Have you ever asked yourself: who defines these options? How does selecting one automatically pull in another? How does the build system know which files to compile?

 

The answer is Kconfig — a small but powerful configuration language built into the Linux kernel source tree. Every driver, every subsystem, every toggleable feature has a matching entry in a file simply called Kconfig. As a kernel developer or embedded engineer, you will eventually need to add your own entry — for example, to make a custom driver conditionally compilable.

 

This tutorial teaches you exactly that. We walk through every keyword, show you how the pieces connect, and build real intuition through inline diagrams and practical examples. All examples are updated for Linux 6.x.

📁 Where Do Kconfig Files Live in the Kernel Source Tree?

Kconfig files are distributed across almost every directory in the kernel tree. There is one at the root that ties everything together, and each subsystem has its own. The Kbuild system reads all of them to build the full menu hierarchy you see in menuconfig.

 

Kconfig File Locations — Linux Kernel Source Tree
Path What It Configures
Kconfig Top-level — ties all sub-menus together
init/Kconfig General kernel setup (preemption, timers, memory)
drivers/Kconfig Entry point for all device driver menus
drivers/usb/Kconfig USB subsystem options
drivers/net/Kconfig Network driver options
lib/Kconfig.debug Kernel Hacking / debugging options
arch/x86/Kconfig x86-specific architecture options
arch/arm64/Kconfig ARM64-specific architecture options
scripts/kconfig/ The tools that parse and display Kconfig files

The Kconfig tools live under scripts/kconfig/. The key programs there are mconf (powers menuconfig), nconf (powers nconfig), and conf (handles all non-GUI config targets like defconfig and oldconfig).

🔬 Anatomy of a Kconfig Entry

Every configuration option follows a predictable pattern. Understanding the skeleton first makes all the individual keywords easier to absorb.

 

Kconfig Entry — Structure at a Glance
Line What It Is Required?
config MY_FEATURE Declares the symbol name. Becomes CONFIG_MY_FEATURE in .config Yes
    tristate "My Feature" Type + prompt text shown in menuconfig Yes
    depends on SOME_OPTION Hides this entry unless SOME_OPTION is enabled No
    default y Default value when user has not changed it No
    select HELPER_LIB Auto-enables HELPER_LIB when this is enabled No
    help Block of help text shown when user presses Help No (but strongly recommended)

 

config MY_FEATURE
    tristate "My Feature Description"
    depends on SOME_OTHER_OPTION
    default m
    help
      This text is shown when the user presses the Help button
      in menuconfig. Explain what the feature does and when to
      enable it. If unsure, say M.

 

The symbol name after config becomes CONFIG_MY_FEATURE everywhere — in .config, in include/generated/autoconf.h, and in your Makefiles. Your C source code uses #ifdef CONFIG_MY_FEATURE or IS_ENABLED(CONFIG_MY_FEATURE) to conditionally compile code.

📦 Config Option Types: bool, tristate, int, string

The type keyword is the most important line after config. It defines what values the option can hold and how it appears in menuconfig.

 

Kconfig Types — Comparison Table
Type Possible Values menuconfig Display Typical Use
bool Y or N [*] or [ ] Features that must be in-kernel or absent — no module option
tristate Y, M, or N <*>, <M>, or < > Drivers and subsystems that can be built as loadable modules
int Any integer (optionally bounded by range) Number input Buffer sizes, timeouts, limits, counts
string Text string Text input Default hostname, embedded firmware paths

 

1. bool — On or Off Only

A bool option has exactly two states. When enabled, it generates CONFIG_X=y in .config. When disabled, it appears as # CONFIG_X is not set. There is no module state.

 

config DEBUG_STACKOVERFLOW
    bool "Check for stack overflows"
    depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW
    help
      Enable runtime warnings when the kernel stack space
      on any CPU drops below a safe threshold. Useful for
      catching stack overflow bugs during development.

 

2. tristate — Built-in, Module, or Off

A tristate option is the most common type for drivers. Y means compiled into the kernel image. M means compiled as a separate .ko file loadable at runtime. N means not compiled at all.

 

config USB_STORAGE
    tristate "USB Mass Storage support"
    depends on USB && SCSI
    help
      Say Y to build USB mass storage support into the kernel.
      Say M to build it as a module (usb-storage.ko) that can
      be loaded on demand. Say N to exclude it entirely.

 

3. int — A Numeric Value with Optional Range

An int option stores a whole number. You typically combine it with range to prevent users from entering invalid values.

 

config LOG_BUF_SHIFT
    int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
    range 12 25
    default 17
    help
      Sets the size of the printk ring buffer as a power of two.
      Value 17 means 2^17 = 128 KB. Larger values help when
      debugging boot issues where messages scroll by fast.

 

4. string — A Text Value

config DEFAULT_HOSTNAME
    string "Default hostname"
    default "(none)"
    help
      The system hostname set at boot time before any
      init scripts run. Override it in /etc/hostname later.

🔑 All Important Kconfig Keywords — Quick Reference

Kconfig Keyword Reference — Linux 6.x
Keyword Purpose Example
bool "desc" On/Off option, Y or N only bool "Enable feature X"
tristate "desc" Y (built-in), M (module), or N tristate "My Driver"
int "desc" Integer numeric value int "Buffer size in KB"
string "desc" Text string value string "Default hostname"
range x y Minimum and maximum for int type range 0 8192
default <val> Value used when user does not change it default y / default 2048
default <val> if COND Conditional default — first matching wins default 2048 if KASAN
depends on <expr> Hide this option unless condition is true depends on NET && INET
select <sym> Auto-enable another symbol when this is enabled select CRC32
select <sym> if COND Conditional auto-enable select CRC32 if USB
help Help text block shown on pressing Help in menuconfig (multiline text block)
source "path/Kconfig" Include another Kconfig file into the menu tree source "drivers/usb/Kconfig"
menu "title" / endmenu Creates a named submenu in menuconfig menu "Networking support"

🔗 Understanding depends on — Forward Dependencies

depends on is the gatekeeper keyword. It hides an option from menuconfig until all listed conditions are satisfied. Think of it as a prerequisite list. The user must first enable the required option, then this one becomes visible.

 

# Both must be Y for this option to appear
depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW

# Either one being Y is sufficient
depends on X86 || ARM64

# Negation — hide this option when COMPILE_TEST is enabled
depends on DEBUG_KERNEL && !COMPILE_TEST

# Grouping — FOO1 AND FOO2 AND (FOO3 OR FOO4)
depends on FOO1 && FOO2 && (FOO3 || FOO4)

 

How depends on Controls menuconfig Visibility
DEBUG_KERNEL HAVE_DEBUG_STACKOVERFLOW DEBUG_STACKOVERFLOW visible?
Y Y ✔ YES — option appears in menu
N Y ✘ NO — dependency not met
Y N ✘ NO — dependency not met
N N ✘ NO — dependency not met

When a dependency is not met, the option is completely absent from the menu — not greyed out, not present at all. It simply does not appear.

⚡ Understanding select — Reverse Dependencies

While depends on works forward (I need X before I can exist), select works backward (when I am enabled, automatically enable X without asking the user). It is used for low-level helper libraries that your driver needs internally but that the user should not have to know about.

 

config MY_USB_DRIVER
    tristate "My custom USB device driver"
    depends on USB
    select CRC32
    help
      Driver for the MyDevice USB gadget.
      CRC32 is selected automatically — the user does not
      need to enable it separately.

 

depends on vs select — Which Direction Does Each Work?
Keyword Direction Effect Best Used For
depends on X Forward — X must already be ON Hides this option until X is enabled by user High-level feature prerequisites
select X Reverse — forces X ON automatically Silently enables X when this option is enabled Low-level libs with no complex deps (CRC32, CRYPTO_HASH)

 

Important rule: Never select an option that itself has complex depends on requirements. That can create unsatisfied dependency chains and trigger the dreaded “recursive dependency detected” Kconfig error. Use depends on for complex prerequisites, reserve select for simple low-level helpers.

📐 Conditional Default Values — Architecture-Aware Defaults

The default keyword sets what value an option gets when the user has not explicitly changed it. What makes this powerful is that you can write multiple default lines, each with a different condition. The Kconfig parser evaluates them from top to bottom and uses the first one whose condition is true.

 

config FRAME_WARN
    int "Warn for stack frames larger than (bytes)"
    range 0 8192
    default 2048 if KASAN
    default 1280 if (!64BIT && PARISC)
    default 1024 if (!64BIT && !PARISC)
    default 2048 if 64BIT
    help
      Ask GCC to emit a warning at compile time when any
      function uses a stack frame larger than this size.
      Setting it to 0 disables the warning entirely.

 

Conditional Default Evaluation — Top to Bottom, First Match Wins
Condition Checked If True → Default Used Reason
KASAN enabled? 2048 bytes KASAN adds overhead so larger frames are expected
32-bit PARISC? 1280 bytes PARISC calling convention uses more stack
32-bit non-PARISC? 1024 bytes Typical 32-bit limit
64-bit? 2048 bytes 64-bit pointers and alignment need more room

This pattern is common throughout arch/ Kconfig files where each architecture has different reasonable defaults. It is far cleaner than C preprocessor tricks.

👻 Hidden Symbols and the HAVE_* Pattern

A Kconfig entry with no prompt string — just a type keyword with no quoted text after it — is a hidden symbol. Users cannot see or change it in menuconfig. It exists purely as an internal flag for the Kconfig dependency system to use.

 

# Hidden symbol — no prompt, no user interaction
# Architectures that support this feature select it
config HAVE_DEBUG_STACKOVERFLOW
    bool

# User-visible option that only appears when the architecture
# actually supports it
config DEBUG_STACKOVERFLOW
    bool "Check for stack overflows"
    depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW

 

HAVE_* Pattern — Architectures Advertise Capabilities
Architecture Kconfig HAVE_DEBUG_STACKOVERFLOW set? DEBUG_STACKOVERFLOW visible in menuconfig?
arch/x86/Kconfig — selects it Y ✔ YES
arch/arm/Kconfig — does not select it N ✘ NO
arch/mips/Kconfig — does not select it N ✘ NO

The HAVE_* convention means user-visible options only appear on hardware that can actually support them. You will see HAVE_PERF_EVENTS, HAVE_IOREMAP_PROT, HAVE_ARCH_TRACEHOOK, and dozens more throughout the kernel. Always remember: HAVE_* symbols are set by architectures, not by users.

✍ Writing Your First Custom Kconfig Entry — Step by Step

Suppose you are writing a character device driver for an embedded board and want users to be able to choose whether it is compiled in, built as a module, or excluded. Here is the exact workflow.

 

Step 1 — Create the Kconfig file in your driver directory

# File: drivers/myboard/Kconfig

config MYBOARD_CHARDEV
    tristate "MyBoard Character Device Driver"
    depends on HAS_IOMEM
    default m
    help
      Enable support for the MyBoard character device interface.

      Say Y to compile it directly into the kernel image.
      Say M to build it as a loadable module (myboard.ko).
      Say N to exclude it entirely.

      If unsure, say M.

 

Step 2 — Include your Kconfig from the parent Kconfig

# File: drivers/Kconfig  — add one line:
source "drivers/myboard/Kconfig"

 

Step 3 — Hook it into your Makefile

# File: drivers/myboard/Makefile
obj-$(CONFIG_MYBOARD_CHARDEV) += myboard.o

 

What the Build System Does with Each Config Value
.config value Makefile expands to Build result
CONFIG_MYBOARD_CHARDEV=y obj-y += myboard.o Linked into vmlinux kernel image
CONFIG_MYBOARD_CHARDEV=m obj-m += myboard.o Built as myboard.ko loadable module
# CONFIG_MYBOARD_CHARDEV is not set obj- += myboard.o Not compiled at all

📖 Three Real Kernel Examples Explained (Linux 6.x)

These examples come from lib/Kconfig.debug — the file that generates the “Kernel Hacking” menu. Reading real kernel Kconfig entries is the fastest way to get comfortable with the syntax.

 

Example 1 — Simple bool with two dependencies

config DEBUG_INFO
    bool "Compile the kernel with debug info"
    depends on DEBUG_KERNEL && !COMPILE_TEST
    help
      Say Y to include DWARF debugging symbols in the kernel image.
      The image will be significantly larger. Essential when using
      gdb, crash, or SystemTap for kernel debugging.

The !COMPILE_TEST dependency prevents debug info from being enabled during automated compile-testing runs, which would slow CI builds dramatically without providing any benefit.

 

Example 2 — int with range and conditional defaults

config FRAME_WARN
    int "Warn for stack frames larger than (bytes)"
    range 0 8192
    default 2048 if KASAN
    default 1024
    help
      GCC will emit a build-time warning for any function whose
      stack frame exceeds this number of bytes. Set to 0 to
      disable the warning. Requires GCC 4.4 or newer.

In Linux 6.x, the KASAN_EXTRA and GCC_PLUGIN_LATENT_ENTROPY conditions from the old book have been consolidated. The default for KASAN is now simply 2048, and all other cases fall back to 1024.

 

Example 3 — Hidden capability symbol + user-visible option

config HAVE_DEBUG_STACKOVERFLOW
    bool
    # No prompt — set by architectures that support it

config DEBUG_STACKOVERFLOW
    bool "Check for stack overflows"
    depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW
    help
      Say Y to get runtime kernel warnings whenever free stack
      space on any CPU drops below the architecture-defined
      minimum threshold.

HAVE_DEBUG_STACKOVERFLOW has no quoted text after bool, making it invisible in menuconfig. Architecture Kconfig files use select HAVE_DEBUG_STACKOVERFLOW to advertise capability. The user-visible DEBUG_STACKOVERFLOW then only appears on supported architectures.

📄 What the .config File Looks Like After menuconfig

After saving your choices in make menuconfig, everything lands in a plain text file called .config at the root of the kernel source. You can open it with any text editor. Here is what a fragment looks like:

 

#
# Kernel hacking
#
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_STACKOVERFLOW is not set
CONFIG_FRAME_WARN=2048
# CONFIG_MYBOARD_CHARDEV is not set
CONFIG_LOG_BUF_SHIFT=17

 

From .config, the build system generates include/generated/autoconf.h. Your C source files include it indirectly through <linux/kconfig.h>. You then use the symbols two ways:

 

/* Classic way — preprocessor skips the block entirely when off */
#ifdef CONFIG_DEBUG_STACKOVERFLOW
    check_stack_usage();
#endif

/* Modern Linux 6.x way — compiler still sees and type-checks
   the block even when the option is off.
   Dead code is eliminated by constant folding. No runtime cost. */
if (IS_ENABLED(CONFIG_DEBUG_STACKOVERFLOW)) {
    check_stack_usage();
}

 

CONFIG_ Symbol — Journey from Kconfig to C Code
Stage File / Location What It Contains
1. User choice make menuconfig User selects Y / M / N
2. Config file .config CONFIG_X=y or # CONFIG_X is not set
3. C header include/generated/autoconf.h #define CONFIG_X 1
4. Makefile drivers/mydir/Makefile obj-y += mydriver.o
5. C source Any .c file #ifdef CONFIG_X or IS_ENABLED(CONFIG_X)

📏 Kconfig Indentation Rules — Get This Right or Patches Get Rejected

The Linux kernel has strict rules about whitespace in Kconfig files. Violating them either causes parse errors or gets your patch rejected by maintainers during code review.

 

Kconfig Indentation Rules
Line Type Indentation Example
config SYMBOL line No indentation — starts at column 0 config MY_DRIVER
Keyword lines inside a config block One TAB character [TAB]tristate "My Driver"
Help text lines One TAB + two spaces [TAB] Say Y here if...

 

# CORRECT — tabs for keywords, tab+spaces for help text
config MY_DRIVER
	tristate "My Driver"
	depends on USB
	help
	  Say Y or M to enable the My Driver.
	  If unsure, say M.

# WRONG — spaces instead of tab before keywords
config MY_DRIVER
    tristate "My Driver"    <-- spaces here will fail or be rejected

 

Enable “show whitespace” in your editor (VSCode, vim, emacs all support this) to verify tabs versus spaces before submitting any Kconfig patch.

Chapter 2 Complete — What You Have Covered

This lecture wraps up Chapter 2. Here is the full picture of what was covered across all five parts of this chapter:

 

Chapter 2 — Full Coverage Map
Part Topic Key Skills Gained
Part 1 Getting the Kernel Source kernel.org download, stable vs LTS vs mainline, version numbering (6.x.y), extracting tarballs
Part 2 Kernel Source Tree Layout Purpose of arch/, drivers/, fs/, include/, kernel/, lib/, mm/ directories
Part 3 Configuring the Kernel make menuconfig, defconfig, oldconfig, tinyconfig, starting from distro config
Part 4 Kbuild System Internals How Kbuild reads Kconfig, generates autoconf.h, uses obj-y / obj-m in Makefiles
Part 5 (This) Kconfig Language Syntax Writing Kconfig entries, all keywords, HAVE_* pattern, IS_ENABLED(), .config flow

 

Coming in Chapter 3: We will actually build the Linux 6.x kernel from source — running make, understanding the build output, installing modules, updating the bootloader, and booting into your custom kernel for the first time.

📷 Second Suggested Image for This Post

Create a simple side-by-side comparison image showing the same option in three states as it appears in make menuconfig: [ ] for bool off, [*] for bool on, and <M> for tristate module. Label each with the corresponding .config line it produces. Alt text: “Linux kernel menuconfig bool vs tristate option states — free linux kernel programming tutorial EmbeddedPathashala”.

❓ Frequently Asked Questions — Kconfig and Kernel Configuration

FAQ — Kconfig Syntax for Beginners
Q: Can I just edit .config directly instead of using menuconfig? Yes. You can open .config in any text editor and change values. After editing, always run make oldconfig so the build system validates your changes and fills in any new options introduced since the config was last generated.
Q: What is the difference between make menuconfig, make nconfig, and make xconfig? All three do the same thing — they let you interactively configure the kernel — but with different interfaces. menuconfig uses ncurses (terminal-based). nconfig is a newer ncurses interface with function-key shortcuts. xconfig uses a Qt GUI. For servers and embedded work, menuconfig is the standard.
Q: Why does my new Kconfig option not appear in menuconfig? Most likely one of three reasons: (1) you forgot to add source "path/to/your/Kconfig" in the parent Kconfig file; (2) a depends on condition is not being met; or (3) there is an indentation error (spaces instead of tabs) causing a silent parse failure. Check all three.
Q: Can a tristate option ever be forced to Y even if built as M? Yes. If another option that is Y uses select to pull in your tristate option, the select keyword forces it to Y regardless of what the user had set. This is one reason to be careful with select on tristate symbols.
Q: Where is Kconfig documentation in the kernel source? The official reference lives at Documentation/kbuild/kconfig-language.rst inside the kernel source tree. You can also read it online at docs.kernel.org under the kbuild section. It covers every keyword including some advanced ones like visible if, imply, and macro language features added in recent kernels.

🎯 Interview Questions — Kconfig and Linux Kernel Configuration

These are commonly asked in embedded Linux, BSP, and kernel driver engineering interviews. Read the question first, think through your answer, then expand to read the explanation.

 

Interview Q&A — Linux Kernel Kconfig (Free Linux Kernel Course)
Q1: What is the difference between bool and tristate in Kconfig? A bool option has two states: Y (compiled into the kernel) or N (not compiled). A tristate has three states: Y (built-in), M (compiled as a loadable .ko module), or N (excluded). Tristate is used for drivers because it gives users the flexibility to load functionality only when needed, saving memory and boot time.
Q2: What is the difference between depends on and select? depends on X is a forward dependency — this option stays hidden until X is enabled by the user. select X is a reverse dependency — when this option is enabled, X is automatically forced on without user action. Use depends on for high-level prerequisites. Use select only for simple low-level helpers like CRC32, avoiding it on symbols with their own complex dependency chains.
Q3: What does CONFIG_MY_DRIVER=m in .config mean for the Makefile? The line obj-$(CONFIG_MY_DRIVER) += my_driver.o in the Makefile expands to obj-m += my_driver.o. Kbuild then compiles the source into my_driver.ko. The module can be inserted at runtime with modprobe my_driver or insmod my_driver.ko without rebuilding or rebooting the kernel.
Q4: What is the HAVE_* pattern and why is it used? HAVE_* symbols are hidden (no prompt) capability flags. Each CPU architecture’s Kconfig file uses select HAVE_X to declare “this hardware supports feature X”. User-visible options then use depends on HAVE_X so they only appear in menuconfig on supported architectures. This prevents users from enabling features their hardware cannot provide.
Q5: How do you add a new Kconfig option for a driver you are writing? Three steps: (1) Create a Kconfig file in your driver directory with a config MY_DRIVER entry — type (tristate), prompt, depends on, default, and help text. (2) Add source "drivers/mydir/Kconfig" to the parent Kconfig so Kbuild finds it. (3) Add obj-$(CONFIG_MY_DRIVER) += my_driver.o to your Makefile so the driver is compiled according to the user’s choice.
Q6: What happens when a depends on condition is not satisfied? The option is completely absent from menuconfig — not greyed out, but invisible. In .config it either keeps its previous value or appears as # CONFIG_X is not set. If you manually set it in .config, running make oldconfig may reset it back if the dependency is still unmet.
Q7: Where does the kernel store the result of all CONFIG_ choices? In two files: .config at the kernel source root (human-readable, used by Makefiles) and include/generated/autoconf.h (C header with #define CONFIG_X 1 lines, used by all kernel C source files via <linux/kconfig.h>).
Q8: What is IS_ENABLED() and why is it better than #ifdef CONFIG_X? IS_ENABLED(CONFIG_X) is defined in <linux/kconfig.h>. Unlike #ifdef, which makes the compiler skip a block entirely when the option is off, IS_ENABLED() lets the compiler parse and type-check the code even when the option is disabled. This catches bugs — wrong types, missing functions, undeclared variables — that #ifdef would silently hide. The compiler eliminates the dead branch via constant folding, so there is zero runtime cost.
Q9: What is the purpose of make oldconfig? make oldconfig reads an existing .config and re-runs Kconfig to ask only about symbols that are new or changed compared to what is already in the file. It is the right command to run after pulling a newer kernel source on top of your existing config — you only answer questions about genuinely new options.
Q10: What is a recursive dependency error in Kconfig and how do you fix it? A recursive dependency error (circular dependency) occurs when symbol A depends on symbol B, and B directly or indirectly depends on A. Kconfig cannot determine a valid value for either. The fix is to break the cycle by changing one of the select statements to a depends on, or by introducing an intermediate HAVE_* symbol that the architecture selects unconditionally.

Ready to Build the Kernel? Chapter 3 is Next!

You have now completed all of Chapter 2. In Chapter 3 we compile the Linux 6.x kernel from source, install modules, update the bootloader, and boot into your custom kernel for the very first time.

← PREV_LEC
NEXT_LEC →

 

Leave a Reply

Your email address will not be published. Required fields are marked *