How to add a Linux kernel driver module as a Buildroot package?

Fully automated out-of-tree QEMU example

A version of the setup below is also present here: https://github.com/cirosantilli/linux-kernel-module-cheat/tree/753cbe68ff50bea0982e1791c8a2178a999d8377/buildroot_packages/kernel_modules and documented at: https://cirosantilli.com/linux-kernel-module-cheat/kernel-modules-buildroot-package The setup below is full extracted from that repository however, and will work standalone.

Source tree:

  • buildroot/: Buildroot 2017.02, ideally as a git submodule
  • kernel_module/: external package with some modules, ideally tracked in the git repository
    • Config.in
    • external.mk
    • external.desc
    • Makefile
    • hello.c: hello world module
    • overlay/etc/inittab: just a minor fix to make the shell work, nothing related to the kernel module itself

kernel_module/Config.in

config BR2_PACKAGE_KERNEL_MODULE
        bool "kernel_module"
        depends on BR2_LINUX_KERNEL
        help
                Linux Kernel Module Cheat.

kernel_module/external.mk

KERNEL_MODULE_VERSION = 1.0
KERNEL_MODULE_SITE = $(BR2_EXTERNAL_KERNEL_MODULE_PATH)
KERNEL_MODULE_SITE_METHOD = local
$(eval $(kernel-module))
$(eval $(generic-package))

kernel_module/external.desc

name: KERNEL_MODULE

kernel_module/Makefile

obj-m += $(addsuffix .o, $(notdir $(basename $(wildcard $(BR2_EXTERNAL_KERNEL_MODULE_PATH)/*.c))))
ccflags-y := -DDEBUG -g -std=gnu99 -Wno-declaration-after-statement

.PHONY: all clean

obj-m += $(addsuffix .o, $(notdir $(basename $(wildcard $(BR2_EXTERNAL_KERNEL_MODULE_PATH)/*.c))))
ccflags-y := -DDEBUG -g -std=gnu99 -Wno-declaration-after-statement

.PHONY: all clean

all:
    $(MAKE) -C '$(LINUX_DIR)' M='$(PWD)' modules

clean:
    $(MAKE) -C '$(LINUX_DIR)' M='$(PWD)' clean

kernel_module/hello.c

#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");

static int myinit(void)
{
    printk(KERN_INFO "hello init\n");
    return 0;
}

static void myexit(void)
{
    printk(KERN_INFO "hello exit\n");
}

module_init(myinit)
module_exit(myexit)

overlay/etc/inittab

::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
::sysinit:/bin/hostname -F /etc/hostname
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

Usage:

sudo apt install make gcc file bzip2 build-essential wget cpio python unzip rsync bc

git clone https://github.com/buildroot/buildroot
cd buildroot
git checkout 2017.2
make BR2_EXTERNAL="$(pwd)/../kernel_module" qemu_x86_64_defconfig
echo '
BR2_PACKAGE_KERNEL_MODULE=y
BR2_TARGET_ROOTFS_EXT2_EXTRA_BLOCKS=1024
BR2_ROOTFS_OVERLAY="overlay"
' >> .config
make olddefconfig
make BR2_JLEVEL="$(nproc)" all

qemu-system-x86_64 -M pc -kernel output/images/bzImage -drive file=output/images/rootfs.ext2,if=virtio,format=raw -append 'root=/dev/vda console=ttyS0' -net nic,model=virtio -nographic -serial mon:stdio -net user

QEMU opens up, then run:

modprobe hello
modprobe -r hello

dmesg shows:

hello init
hello exit

The key line is $(eval $(kernel-module)) in external.mk, which sets everything up for us, and installs the modules where modprobe will find them (/lib/modules/*/extra/hello.ko), including modules.dep for inter-module dependencies: How to call exported kernel module functions from another module?

How to GDB step debug the kernel modules: How to debug Linux kernel modules with QEMU?

To autoload modules at startup, use BR2_ROOTFS_OVERLAY="../rootfs_overlay" and a rootfs_overlay/etc/init.d/S99modules file that does the modprobe.

In-tree example: https://github.com/cirosantilli/buildroot/tree/9580078b98f885ca94e4dfc896265a8a491f6ae1 It is less clean, but also works.

Tested on a Ubuntu 16.04 hoste.

Leave a Comment