config/drivers.conf: which drivers are compiled in

y or n per optional driver (virtio-gpu, stdvga, e1000, rtl8139, native r8169, AC'97, AHCI, NVMe), all y except the native
r8169, overridable with make DRV_X=n. Only drivers nothing else calls can be optional; the registry #ifdefs them and the
Makefile leaves their objects out. Built with every one off (links, none of their symbols present), with r8169 on, and
by default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
Nexoniarz 2026-10-26 08:59:55 +01:00
commit 1334f446cc
6 changed files with 139 additions and 16 deletions

View file

@ -38,17 +38,34 @@ KERN_DEV = $(addprefix $(BUILD)/kernel/dev/, \
# already eight files with four called usb_something, and each driver added made # already eight files with four called usb_something, and each driver added made
# the directory harder to read. The compile rule uses `mkdir -p $(dir $@)`, so # the directory harder to read. The compile rule uses `mkdir -p $(dir $@)`, so
# the depth costs nothing. # the depth costs nothing.
# Which of them are compiled in is config/drivers.conf: y or n per driver, y by
# default, overridable on the command line (make DRV_E1000=n). Only the drivers
# that nothing else calls can be left out; the rest are always linked.
-include config/drivers.conf
DRV_LIST = VGA_PCI VIRTIO_GPU E1000 RTL8139 R8169 AC97 AHCI NVME
DRV_DEFAULT_R8169 = n
$(foreach d,$(DRV_LIST),$(eval DRV_$(d) ?= $(if $(DRV_DEFAULT_$(d)),$(DRV_DEFAULT_$(d)),y)))
$(foreach d,$(DRV_LIST),$(if $(filter y n,$(DRV_$(d))),,$(error config/drivers.conf: DRV_$(d) must be y or n, not '$(DRV_$(d))')))
DRV_DEFS = $(foreach d,$(DRV_LIST),$(if $(filter y,$(DRV_$(d))),-DCONFIG_DRV_$(d)=1))
# Rewritten only when the choice changes, so registry.o is rebuilt then and not otherwise.
DRV_STAMP = $(BUILD)/kernel/drivers/.config
$(DRV_STAMP): FORCE
@mkdir -p $(dir $@)
@echo '$(DRV_DEFS)' | cmp -s - $@ || echo '$(DRV_DEFS)' > $@
$(BUILD)/kernel/drivers/registry.o: $(DRV_STAMP)
$(BUILD)/kernel/drivers/registry.o: CFLAGS += $(DRV_DEFS)
KERN_DRIVERS = $(BUILD)/kernel/drivers/registry.o KERN_DRIVERS = $(BUILD)/kernel/drivers/registry.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_pci.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_fb.o KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_fb.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/virtio_gpu/virtio_gpu.o KERN_DRIVERS += $(if $(filter y,$(DRV_VGA_PCI)),$(BUILD)/kernel/drivers/gpu/vga/vga_pci.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/net/e1000/e1000.o KERN_DRIVERS += $(if $(filter y,$(DRV_VIRTIO_GPU)),$(BUILD)/kernel/drivers/gpu/virtio_gpu/virtio_gpu.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/net/r8169/r8169.o KERN_DRIVERS += $(if $(filter y,$(DRV_E1000)),$(BUILD)/kernel/drivers/net/e1000/e1000.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/net/rtl8139/rtl8139.o KERN_DRIVERS += $(if $(filter y,$(DRV_R8169)),$(BUILD)/kernel/drivers/net/r8169/r8169.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/audio/ac97.o KERN_DRIVERS += $(if $(filter y,$(DRV_RTL8139)),$(BUILD)/kernel/drivers/net/rtl8139/rtl8139.o)
KERN_DRIVERS += $(if $(filter y,$(DRV_AC97)),$(BUILD)/kernel/drivers/audio/ac97.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/storage/ahci/ahci.o KERN_DRIVERS += $(if $(filter y,$(DRV_AHCI)),$(BUILD)/kernel/drivers/storage/ahci/ahci.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/storage/nvme/nvme.o KERN_DRIVERS += $(if $(filter y,$(DRV_NVME)),$(BUILD)/kernel/drivers/storage/nvme/nvme.o)
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/hid/usb_hid.o KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/hid/usb_hid.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/storage/usb_storage.o KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/storage/usb_storage.o
@ -1825,3 +1842,6 @@ distclean: clean
# build output: Alpine's usr/lib/sysctl.d is a directory, and including it # build output: Alpine's usr/lib/sysctl.d is a directory, and including it
# stopped every make with "Is a directory". # stopped every make with "Is a directory".
-include $(shell find $(BUILD) \( -path $(BUILD)/aroot -o -path $(BUILD)/iso -o -path $(BUILD)/ports \) -prune -o -type f -name '*.d' -print 2>/dev/null) -include $(shell find $(BUILD) \( -path $(BUILD)/aroot -o -path $(BUILD)/iso -o -path $(BUILD)/ports \) -prune -o -type f -name '*.d' -print 2>/dev/null)
FORCE:
.PHONY: FORCE

45
config/drivers.conf Normal file
View file

@ -0,0 +1,45 @@
# Which drivers are compiled into the kernel.
#
# y compiled in, and enabled: it is offered every device it can drive
# n not compiled in: the code is not linked and the kernel never knows it
#
# Everything is y unless it says otherwise. Change a line, or override one for
# a single build:
#
# make DRV_E1000=n DRV_NVME=n
#
# A changed value rebuilds only kernel/drivers/registry.o and the link.
#
# These are the drivers that can be left out cleanly: nothing else in the kernel
# calls them, they are only offered a device by the registry
# (kernel/drivers/registry.c). The others cannot be, because the rest of the
# kernel calls into them directly, and are not listed:
#
# vga_fb the boot framebuffer: the console and the crash screen draw on it
# xhci, usb core, usb hid / storage / hub
# the keyboard and the root disk can arrive over USB, bio.c and
# the hotplug thread call in, and so does the I2C-HID touchpad
# PS/2, IDE, the 16550 UART
# fixed-address legacy devices (kernel/dev/)
#
# Linux's own drivers, run in a process by ldlsd, are not here: they are files in
# the image (make ldls-r8169), not code in the kernel.
# --- display --------------------------------------------------------------
DRV_VGA_PCI = y # the stdvga/Bochs card (the framebuffer driver stays regardless)
DRV_VIRTIO_GPU = y # QEMU's virtio-gpu: mode setting and an explicit flush
# --- network ----------------------------------------------------------------
DRV_E1000 = y # Intel 8254x and 8257x
DRV_RTL8139 = y # Realtek 8139 (the one Realtek part QEMU emulates)
# The native RTL8168/8111 driver. Off by default because the laptop's NIC is
# driven by Linux's r8169 under LDLS (etc/schinit/ldls-r8169.job), and a driver
# here would claim the device first. Turn it on for a machine without that job.
DRV_R8169 = n
# --- storage ----------------------------------------------------------------
DRV_AHCI = y # SATA
DRV_NVME = y # NVMe SSDs
# --- audio ------------------------------------------------------------------
DRV_AC97 = y # Intel AC'97 (QEMU's and old boards')

View file

@ -9,6 +9,17 @@ Drivers that live in a process rather than in the kernel are described below
--- ---
## Which drivers are compiled in
`config/drivers.conf` lists the drivers that can be left out of a kernel, `y` (compiled in and offered every device it can
drive) or `n` (not linked at all), all `y` unless a line says otherwise — the one exception is the native `r8169`, `n`
because Linux's runs in its place under LDLS. Override one for a build with `make DRV_E1000=n DRV_NVME=n`. A changed
choice rebuilds only `registry.o` and the link. The list is short on purpose: a driver is optional only if nothing else in
the kernel calls it (display: `vga_pci`, `virtio_gpu`; network: `e1000`, `rtl8139`, `r8169`; storage: `ahci`, `nvme`;
audio: `ac97`). `vga_fb`, the USB stack and the legacy PS/2, IDE and UART devices are always there, because the console, the
keyboard and the root disk can depend on them. A kernel with every optional driver off builds and links; whether such a
kernel is useful on any particular machine is the person who turned them off to say.
## What stays in the kernel, and what may leave ## What stays in the kernel, and what may leave
"Hybrid" is used to mean everything from "a monolith with a nice device model" to "Hybrid" is used to mean everything from "a monolith with a nice device model" to

View file

@ -61,6 +61,10 @@ ours.
2. Declare it in `kernel/drivers/registry.c` and add it to the array. 2. Declare it in `kernel/drivers/registry.c` and add it to the array.
3. Add the object to `KERN_DRIVERS` in the `Makefile`. 3. Add the object to `KERN_DRIVERS` in the `Makefile`.
3½. If nothing else in the kernel calls it, make it optional: a `DRV_<NAME>` line in
`config/drivers.conf`, the name in `DRV_LIST` in the Makefile, the object behind
`$(filter y,$(DRV_<NAME>))`, and an `#ifdef CONFIG_DRV_<NAME>` around its line in `registry.c`.
A driver other code calls (`vga_fb`, the USB stack) cannot be optional.
4. Fill in the **class vtable** for what it is, and point `.ops` at it. That is 4. Fill in the **class vtable** for what it is, and point `.ops` at it. That is
the whole of what the rest of the kernel sees: the whole of what the rest of the kernel sees:
@ -113,7 +117,7 @@ That is data; the logic stays ours.
- [ ] One directory under `kernel/drivers/<class>/<name>/` - [ ] One directory under `kernel/drivers/<class>/<name>/`
- [ ] Exports a `struct pci_driver` or `struct usb_driver` - [ ] Exports a `struct pci_driver` or `struct usb_driver`
- [ ] Declared in `kernel/drivers/registry.c` **and** added to its array - [ ] Declared in `kernel/drivers/registry.c` **and** added to its array
- [ ] Object added to `KERN_DRIVERS` in the `Makefile` - [ ] Object added to `KERN_DRIVERS` in the `Makefile`, and an option in `config/drivers.conf` if it can be left out
- [ ] Class vtable filled in and `.ops` pointed at it - [ ] Class vtable filled in and `.ops` pointed at it
- [ ] `.attached` implemented if it can drive more than one card - [ ] `.attached` implemented if it can drive more than one card
- [ ] `devclass` set, if it is a USB driver - [ ] `devclass` set, if it is a USB driver

View file

@ -17,6 +17,9 @@ hundred megabytes, kept in `dist/`) and compiles Xorg's dependencies not at all:
packages are Alpine's binaries, pinned in `ports/alpine/world.lock`. See packages are Alpine's binaries, pinned in `ports/alpine/world.lock`. See
[packages](../architecture/packages.md). [packages](../architecture/packages.md).
Which drivers go into the kernel is `config/drivers.conf` (all of the optional ones by default);
`make DRV_E1000=n` leaves one out for a build. See [devices](../architecture/devices.md).
## Prerequisites ## Prerequisites
| Need | Why | Windows | Linux | macOS | BSD | | Need | Why | Windows | Linux | macOS | BSD |

View file

@ -12,9 +12,10 @@
// a driver silently dropped by the linker looks exactly like one that was // a driver silently dropped by the linker looks exactly like one that was
// never written. At this size, being able to read the list wins. // never written. At this size, being able to read the list wins.
// //
// To add a driver: write kernel/drivers/<name>.c exporting a struct // To add a driver: write kernel/drivers/<class>/<name>/<name>.c exporting a
// pci_driver, declare it here, and add it to the array and to the Makefile's // struct pci_driver, declare it here, add it to the array and to the Makefile's
// KERN_DRIVERS. // KERN_DRIVERS -- and, if nothing else in the kernel calls it, give it a
// DRV_<NAME> option in config/drivers.conf so it can be left out.
#include "core/types.h" #include "core/types.h"
#include "core/defs.h" #include "core/defs.h"
@ -28,14 +29,34 @@
#include "dev/usb.h" #include "dev/usb.h"
#include "dev/device.h" #include "dev/device.h"
// Which of these exist in this kernel is config/drivers.conf: the Makefile
// leaves a disabled driver's object out and defines CONFIG_DRV_<NAME> for the
// ones it keeps. The ones with no option (xHCI) are always here.
#ifdef CONFIG_DRV_VGA_PCI
extern const struct pci_driver vga_pci_driver; extern const struct pci_driver vga_pci_driver;
#endif
extern const struct pci_driver xhci_driver; extern const struct pci_driver xhci_driver;
#ifdef CONFIG_DRV_VIRTIO_GPU
extern const struct pci_driver virtio_gpu_driver; extern const struct pci_driver virtio_gpu_driver;
#endif
#ifdef CONFIG_DRV_E1000
extern const struct pci_driver e1000_driver; extern const struct pci_driver e1000_driver;
#endif
#ifdef CONFIG_DRV_AC97
extern const struct pci_driver ac97_driver; extern const struct pci_driver ac97_driver;
#endif
#ifdef CONFIG_DRV_RTL8139
extern const struct pci_driver rtl8139_driver; extern const struct pci_driver rtl8139_driver;
#endif
#ifdef CONFIG_DRV_R8169
extern const struct pci_driver r8169_driver;
#endif
#ifdef CONFIG_DRV_AHCI
extern const struct pci_driver ahci_driver; extern const struct pci_driver ahci_driver;
#endif
#ifdef CONFIG_DRV_NVME
extern const struct pci_driver nvme_driver; extern const struct pci_driver nvme_driver;
#endif
// Order matters here in one respect: virtio-gpu is offered the bus before the // Order matters here in one respect: virtio-gpu is offered the bus before the
// VBE framebuffer driver, so that on a machine with both, the device with real // VBE framebuffer driver, so that on a machine with both, the device with real
@ -43,23 +64,42 @@ extern const struct pci_driver nvme_driver;
// claims the stdvga card underneath, which is what the boot splash and the // claims the stdvga card underneath, which is what the boot splash and the
// text console are drawn on. // text console are drawn on.
static const struct pci_driver *drivers[] = { static const struct pci_driver *drivers[] = {
#ifdef CONFIG_DRV_VIRTIO_GPU
&virtio_gpu_driver, &virtio_gpu_driver,
#endif
#ifdef CONFIG_DRV_VGA_PCI
&vga_pci_driver, &vga_pci_driver,
#endif
#ifdef CONFIG_DRV_E1000
&e1000_driver, &e1000_driver,
// &r8169_driver -- the RTL8168 is driven by Linux's own r8169 through #endif
// LDLS (etc/schinit/ldls-r8169.job), which a kernel driver here would // The RTL8168 is driven by Linux's own r8169 through LDLS
// claim first. The native driver stays in the tree as the fallback. // (etc/schinit/ldls-r8169.job), which a kernel driver here would claim
// first -- so DRV_R8169 is off by default. The native driver stays in the
// tree as the fallback for a machine without that job.
#ifdef CONFIG_DRV_R8169
&r8169_driver,
#endif
#ifdef CONFIG_DRV_RTL8139
&rtl8139_driver, &rtl8139_driver,
#endif
&xhci_driver, &xhci_driver,
#ifdef CONFIG_DRV_AHCI
&ahci_driver, &ahci_driver,
#endif
#ifdef CONFIG_DRV_NVME
&nvme_driver, &nvme_driver,
#endif
#ifdef CONFIG_DRV_AC97
&ac97_driver, &ac97_driver,
#endif
0, // keeps the array non-empty when everything optional is off
}; };
void void
pci_bind_all(void) pci_bind_all(void)
{ {
for (unsigned i = 0; i < sizeof(drivers) / sizeof(drivers[0]); i++) for (unsigned i = 0; drivers[i]; i++)
pci_bind(drivers[i]); pci_bind(drivers[i]);
} }