Skip to main content
Most Qualcomm® Linux drivers are compiled directly from the kernel source tree. Some drivers are maintained outside that tree—called out-of-tree or Dynamically Loadable Kernel Module (DLKM) drivers—and are built separately using either a standalone Makefile or the Yocto build system. The Qualcomm kernel graphics support layer (KGSL) GPU driver is an example: its recipe at recipes-graphics/kgsl-dlkm/kgsl-dlkm_git.bb builds and installs the driver as an out-of-tree module.

Build and autoload modules

Standalone Makefile

Create a Makefile that delegates to the kernel build system via $(MAKE) -C:
all: modules
obj-m := hello.o

SRC := $(shell pwd)

modules:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules $(KBUILD_OPTIONS)

modules_install:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
Set KERNEL_SRC to the path of the configured kernel source tree before invoking make.

Yocto recipe

Integrate an out-of-tree module into the Yocto build by inheriting the module class. The class handles make modules and make modules_install automatically.
DESCRIPTION = "${SUMMARY}"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/${LICENSE};md5=801f80980d171dd6425610833a22dbe6"

inherit module

SRC_URI += "file://Makefile \
            file://hello.c  \
            file://COPYING  \
            "
S = "${WORKDIR}"

EXTRA_OEMAKE += "MACHINE='${MACHINE}'"
MAKE_TARGETS = "modules"
MODULES_INSTALL_TARGET = "modules_install"

# Autoload the module on boot
KERNEL_MODULE_AUTOLOAD += "hello"

# The inherit of module.bbclass names packages with the "kernel-module-" prefix
RPROVIDES_${PN} += "kernel-module-hello"
The KERNEL_MODULE_AUTOLOAD variable writes the module name into /etc/modules-load.d/ in the target rootfs, causing systemd-modules-load to insert it on every boot.

Real-world example: KGSL

The KGSL recipe illustrates a production out-of-tree module:
inherit module

DESCRIPTION = "Qualcomm KGSL driver for managing Adreno GPU"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://adreno.c;beginline=1;endline=1;md5=fcab174c20ea2e2bc0be64b493708266"

PV = "0.0+git"
SRCREV = "553c972604f739564d6bb70e18e3857c041984b1"
SRC_URI = " \
    git://github.com/qualcomm-linux/kgsl.git;branch=gfx-kernel.le.0.0;protocol=https \
    file://kgsl.rules \
"

do_install:append() {
    install -m 0644 ${WORKDIR}/sources/kgsl.rules -D ${D}${nonarch_base_libdir}/udev/rules.d/kgsl.rules
}

KERNEL_MODULE_PROBECONF += "msm_kgsl"
module_conf_msm_kgsl = "blacklist msm_kgsl"

FILES:${PN} += "${nonarch_base_libdir}/udev/rules.d"

COMPATIBLE_MACHINE = "^$"
COMPATIBLE_MACHINE:aarch64 = "(.*)"
Note the blacklist msm_kgsl entry: it prevents the upstream in-tree stub from loading when the out-of-tree KGSL module is present. For more information about out-of-tree modules in Yocto, see Working with Out-of-Tree Modules.

Module versioning strategy

Kernel symbol versioning (MODVERSIONS)

When CONFIG_MODVERSIONS=y is set in the kernel configuration, the kernel embeds a CRC checksum for each exported symbol. A module is loaded only when its per-symbol CRCs match those of the running kernel, preventing silently binary-incompatible modules from being inserted. To verify whether MODVERSIONS is active on the target:
zcat /proc/config.gz | grep CONFIG_MODVERSIONS
A module built against a different kernel tree or a different defconfig will produce a load-time error if any CRC mismatches:
insmod: ERROR: could not insert module hello.ko: Invalid module format

vermagic compatibility

Every .ko file embeds a vermagic string that encodes the kernel version, SMP flag, and compiler version used at build time. The running kernel rejects any module whose vermagic does not match exactly. Inspect a module’s vermagic before deployment:
modinfo hello.ko | grep vermagic
Table: vermagic fields
FieldExampleDescription
Kernel version6.12.0Must match uname -r on the target
SMPSMPSymmetric multiprocessing flag
preemptpreemptPreemption model
Compilergcc-14Toolchain used to build the kernel

Version-pinning in Yocto

To ensure an out-of-tree module is always built against the same kernel version as the running image, set DEPENDS and RDEPENDS to the kernel recipe in the module’s .bb file:
DEPENDS += "virtual/kernel"
RDEPENDS_${PN} += "kernel-${KERNEL_VERSION}"
The Yocto module class automatically sets KERNEL_SRC and KERNELRELEASE to values from the selected kernel recipe, so the module Makefile picks up the correct headers and symbol tables without any manual configuration.