2024-08-08
How Linux kernel driver modules for hardware get loaded (I think)
Once upon a time, a long time ago, the kernel modules for your hardware got loaded during boot because they were listed explicitly as 'load these modules' in configuration files somewhere. You can still explicitly list modules this way (and you may need to for things like IPMI drivers), but most hardware driver modules aren't loaded like this any more. Instead they get loaded through udev, through what I believe is two mechanisms.
The first mechanism is that as the kernel inventories things like
PCIe devices, it generates udev events with 'MODALIAS
' set in
them in a way that incorporates the PCIe vendor and device/model
numbers. At the same time, kernel modules declare all of the PCIe
vendor and model values that they support, which are turned into
(somewhat wild carded) module aliases that you can inspect with
'modinfo', for example:
$ modinfo bnxt_en description: Broadcom BCM573xx network driver license: GPL alias: pci:v000014E4d0000D800sv*sd*bc*sc*i* alias: pci:v000014E4d00001809sv*sd*bc*sc*i* [...] alias: pci:v000014E4d000016D8sv*sd*bc*sc*i* [...]
(The other parts of the pci MODALIAS value are apparently, in order, the subsystem vendor, the subsystem device/model, the base class, the sub class, and the 'programming interface'. See the Arch Wiki entry on modalias.)
As I understand things (and udev rules), when udev processes a kernel udev event with a MODALIAS set, it will attempt to load a kernel module that matches the name. Usually this will be done through wild card matching against aliases, as in the case of Broadcom BCM573xx cards; a supported card will have its PCIe vendor and device listed as an alias, so udev will wind up loading bnxt_en for it.
The second mechanism is through something called the Auxiliary
'bus'. To
put my own spin on it, this is a way for core hardware drivers to
declare (possibly only under some situations) that loading an
additional driver can enable extra functionality. When the main
driver loads and registers itself, it will register a pseudo-device
on the 'auxiliary bus'. This bus registration generates a udev event
with a MODALIAS
that starts with 'auxiliary:' and apparently is
generally formatted as 'auxiliary:<core driver>.<some-feature>',
for example 'auxiliary:bnxt_en.rdma'. When this pseudo-device
is registered, the udev event goes out from the kernel, is picked
up by udev, and triggers an attempt to load whatever kernel module
has declared that name as an alias. For example:
$ modinfo bnxt_re [...] description: Broadcom NetXtreme-C/E RoCE Driver [...] alias: auxiliary:bnxt_en.rdma depends: ib_uverbs,ib_core,bnxt_en [...]
(Inside the kernel, the two kernel modules use this pseudo-device on the auxiliary bus to connect with each other.)
As far as I know, the main kernel driver modules don't explicitly publish information on what auxiliary bus things they may trigger; the information exists only in their code. You can attempt to go the other way by looking for modules that declare themselves as auxiliaries for something else. This is most conveniently done by looking for 'auxiliary:' in /lib/modules/<version>/modules.alias.
(Your results may depend on the specific kernel versions and build options involved, and perhaps what additional packages have been added. On my Fedora 40 machine with 6.9.12, there are 37 auxiliary: aliases; on an Ubuntu 24.04 machine with '6.8.0-39', there are 49, with the extras coming from the peci_cputemp and peci_dimmtemp kernel modules.)
PS: PCI(e) devices aren't the only thing that this kernel module alias facility is used for. There are a whole collection of USB modaliases, a bunch of 'i2c' and 'of' ones, a number of 'hid' ones, and so on.