You configure the OpenBSD kernel via text files. Like 4.4BSD, OpenBSD doesn’t offer a fancy graphical kernel configuration utility or menu-driven system. Each kernel configuration is on a single line, along with a label indicating the type of entry and a description. Pound signs (#
) mark comments.
Kernel configuration entries fall into four general categories: options, device drivers, pseudo-devices, and keywords.
Options are hardware-independent kernel functions. Options handle things like filesystems, networking protocols, and compatibility layers.
Option entries look like this:
option FFS # UFS option INET # IP + ICMP + TCP + UDP option CRYPTO # Cryptographic framework
To learn more about options, read the options(4)
man page.
Device drivers give the kernel the necessary software to interact with a piece of hardware. If you want your kernel to support a piece of hardware, it must include the appropriate device driver.
Device driver kernel configuration entries can be quite long. They might include flags or settings that tell the kernel where to find the device and how to initialize it. (ISA cards usually have a hard-coded IRQ and/or memory address.)
Device drivers have no common label, but their entry starts with the device name.
mainbus0 at root cpu0 at mainbus? fxp* at pci? # EtherExpress 10/100B ethernet wd* at wdc? flags 0x0000 ec0 at isa? port 0x250 iomem 0xd8000 irq 9 # 3C503 ethernet
Pseudo-devices behave much like devices, but have no real hardware attached to them. Pseudo-devices are frequently abstractions that can be opened, read from, written to, and closed in the same way as real hardware.
For example, the loopback interface is a pseudo-device used for network connections to the local machine. (Your computer has no loopback network card, but the loopback interface behaves just like a real network card with an unusual MTU value.)
Pseudo-devices are labeled with pseudo-device
.
pseudo-device loop # network loopback pseudo-device pf # packet filter pseudo-device gre # GRE encapsulation interface
Finally, a handful of other keywords appear only once or rarely. These one-offs change how the kernel runs or how it’s built, and defy easy categorization. The following keywords may appear:
The machine
keyword tells the kernel which architecture it should run on.
The makeoptions
keyword tells the compiler how to build the kernel.
The include
keyword means pull in another configuration file.
The maxusers
value sets the size of some in-kernel tables.
You’ll find even less common keywords scattered in different kernel configurations.
machine amd64 makeoptions DEBUG="-g" # compile full symbol table include "../../../conf/GENERIC" maxusers 80 # estimated number of users
All of these affect the kernel in wildly different ways. You’ll find several of these keywords in any kernel, even GENERIC.
Let’s look at an actual kernel configuration. OpenBSD divides kernel configuration into machine-independent and machine-dependent files.
The machine-independent kernel configuration files are in /usr/src/sys/conf. The file /usr/src/sys/conf/GENERIC contains the machine-independent kernel configuration, which describes all of the features that OpenBSD supports on all hardware platforms. Every GENERIC kernel contains the configuration in this file. If you change this file, it will affect every kernel built that includes this file.
The machine-independent configuration file doesn’t contain device drivers; instead, devices are tied to particular hardware. This file won’t contain any special building instructions, because they vary from platform to platform. Nor will it include hard-coded system limits, data structure sizes, and so on, as OpenBSD running on a 25-year-old VAX has considerably fewer resources than a brand-new amd64 system. The /usr/src/sys/conf/GENERIC file contains mostly options and pseudo-devices. Every OpenBSD kernel must support a filesystem, or it won’t be able to write to disk or anything disk-like.
A kernel based on this file doesn’t yet know what sort of hardware the filesystem will run on, but it knows how to make a filesystem. It doesn’t know what kind of network card it will have, but once you give it a network card, it can create a TCP data stream and serve your web pages. You’ll need the machine-dependent configuration to make a kernel that can function in the real world.
Each platform has its own machine-dependent kernel directory under /usr/src/sys/arch. Here’s where you’ll find a subdirectory for every platform OpenBSD supports, as well as a directory for any platforms under development. Separate directories contain platform-specific code, as well as further conf subdirectories for the kernel configuration file.
I’m using amd64 as an example, so the kernel configuration directory is /usr/src/sys/arch/amd64/conf. While we’ll focus on the common i386 and amd64 architectures, the kernel-building process is the same across all hardware platforms.
A traditional kernel configuration filename is in all capital letters. You’ll see the GENERIC configuration, as well as the RAMDISK* files used for the installation disks. (The GENERIC.MP kernel is the multiprocessor kernel.) We’ll start with the GENERIC kernel configuration file:
machine amd64 include "../../../conf/GENERIC"
The first entry in this kernel configuration defines the machine.The machine definition tells the kernel configuration parser the kind of hardware you’re running, and defines core hardware characteristics and constraints, such as how many bits are in an integer and how much memory the system can support.
The second entry pulls in the machine-independent kernel configuration (described in the previous section), defining all of the protocols and tools that make OpenBSD OpenBSD. The amd64 kernel inherits the filesystems and network stacks from this entry.
Following these two lines you’ll see the devices OpenBSD supports on amd64 hardware. Take a moment and skim the file. It’s the same mix of devices and attachments as described earlier in this chapter.
In order to build your own kernel, you’ll need a configuration file. Here, we’ll look at how to create your configuration file. (Do not just edit either GENERIC kernel file.)
If your kernel adds only a couple of items to the GENERIC kernel, use the GENERIC configuration as a basis for your new one. For example, here’s the multiprocessor kernel configuration, GENERIC.MP:
include "arch/amd64/conf/GENERIC" option MULTIPROCESSOR # Multiple processor support cpu* at mainbus?
The multiprocessor kernel builds on GENERIC, adding only one option and one device attachment. You can use this model to define your own kernel configuration.
For example, suppose you want to enable the experimental SCSI multipathing feature on a kernel. You could create a kernel configuration file in your platform directory, and simply copy the commented-out multipathing entries from the machine-independent GENERIC kernel, like this:
include "arch/amd64/conf/GENERIC" mpath0 at root scsibus* at mpath?
This creates a custom kernel that closely resembles GENERIC, with these two extra devices.
To strip options from your kernel, use the rmoption
keyword. For example, to create a minimal kernel based on GENERIC, you could use the rmoption
keyword to remove some kernel options, as in this example:
include "arch/amd64/conf/GENERIC" rmoption NTFS rmoption HIBERNATE …
One advantage to creating a configuration by including the default kernel is that when you update your source code, your custom kernel configuration will probably still be valid. However, the more options you remove from the kernel, the greater the chance that the kernel will fail to compile, or if it compiles, that it might not boot. And if it boots, it might eat your hard drive.
When removing options, keep in mind that some options are more important than you might think. For example, removing the INET6
option (aka IPv6) can create a nonfunctional system. Removing options doesn’t save you much memory, and it might cripple any number of programs.
If you want to remove a lot of stuff from a machine-dependent kernel configuration, while retaining the options for base OpenBSD functions, copy the machine-dependent GENERIC configuration file to a new text file and make your changes in that file.
If you want to commit wholesale butchery on the kernel, you’ll want a configuration that includes both the machine-independent and machine-dependent parts. Start by copying the existing GENERIC kernel configurations into one file, in the platform’s kernel configuration. Here, I call my new kernel TREBLE, after the hostname:
# cd /usr/src/sys/arch/amd64/conf # cp ../../../conf/GENERIC TREBLE # cat GENERIC >> TREBLE
Before making any other changes, remove the line that includes the machine-independent kernel configuration file. Then slice out everything that makes the system functional, and try to build the new kernel. Next, add stuff back in until the kernel builds. (Although removing drivers won’t save much memory, doing so will make booting a tiny bit faster.)
Every device driver and option in the kernel uses memory. If you’re trying to cram OpenBSD onto a tiny computer, or you’re doing any sort of embedded development, you might want to build a custom kernel that includes as few device drivers as possible by editing /var/run/dmesg.boot, where every entry matches a line in the kernel configuration.
The simplest way to trim out unnecessary device drivers is to remove everything that’s not in your computer. The kernel includes dozens of network card drivers, but you need only one or two. If you’re unsure about a device, keep it in the configuration. (The ACPI and BIOS devices in particular are tightly interrelated, and you’ll probably have a really hard time building a bootable custom kernel without the complete set of ACPI and BIOS devices.)
If removing device drivers doesn’t create a sufficiently small kernel for you, try removing machine-independent options. Many of these options are interdependent, however, and removing them can create a kernel you can’t compile. If you can compile the kernel, it might not boot, and if it boots, it might not function correctly.
Is your custom kernel configuration internally consistent? To test your kernel and prepare the files needed to compile it, use config(8)
.
While still in the kernel configuration directory, give config
the kernel configuration filename as an argument, like this:
# config TREBLE
If you get any error messages, read them. For example, config
might tell you that you need to run make clean
before building your new kernel, or that your kernel configuration is internally inconsistent and will not compile. If there’s a problem, config
will often give a line number where you made an error. Follow any advice config
offers.
The following are some of the more common types of errors.
One common way that config
fails is if you’re missing a device that’s needed by another device. Here’s an example:
# config TREBLE
TREBLE:36: cpu0 at mainbus? is orphaned
(nothing matching mainbus? declared)
TREBLE:37: bios0 at mainbus0 is orphaned
(no mainbus0 declared)
TREBLE:38: ioapic* at mainbus? is orphaned
(nothing matching mainbus? declared)
TREBLE:82: pci* at mainbus0 is orphaned
(no mainbus0 declared)
*** Stop.
Your configuration attaches various devices to mainbus0
, but there’s no mainbus0
entry in your configuration. Kernels that include devices that aren’t attached don’t make sense and cannot compile.
To address this, examine your hardware again. Figure out how these devices are supposed to attach to the system, and fix your kernel configuration.
Another common problem is including nonexistent device drivers, which generates the following error.
# config TREBLE
TREBLE:36: cpe0: unknown device `cpe'
*** Stop.
config
shows me the error and the line number where it occurs. There is no cpe
device, but there is a cpu
device. My bad.
The error checking performed by config
does not guarantee that your kernel will compile or run as expected. The only errors it catches are ones where the configuration is either internally inconsistent or flat-out wrong. The first real test comes when you try to actually build your configured kernel.