Topics Covered in This Lecture:
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.
| 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.
| 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.
| 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
| 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)
| 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.
| 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.
| 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
| 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
| .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();
}
| 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.
| 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:
| 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
| 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.
| 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.
