Kernel Modules: Introduction

This blog post details the experiences of writing “Hello World!” kernel module.

Problem Statement:
– This kernel module should be able be loaded and unloaded successfully.
– This driver should be able to be built using arbitary kernel source headers.
– The checkpatch.pl script should show no errors/warnings.
– This driver should be compatible with earlier versions of 2.6.x kernel.
– An option to take kernel headers path using environment variable.
– The driver should print a “Hello World !” message when loaded.
– The driver should print the message to the kernel debug level.

The hello world kernel module has two important apis.

init_module, cleanup_module which gets called when the kernel module gets loaded and unloaded respectively.

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

int init_module(void)
{
    pr_debug("Hello World!\n");
    return 0;
}

void cleanup_module(void)
{
  /* This api is called when module gets unloaded. Do nothing for now */
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Suman Kumar<suman.cluster@gmail.com>");

The Makefile below is used to compile the Hello world driver.

ifneq ($(KERNELRELEASE),)
            obj-m := hello.o
            CFLAGS_hello.o := -O2 -DMODULE -D__KERNEL__ -DDEBUG
else
            obj-m := hello.o
            CFLAGS_hello.o := -O2 -DMODULE -D__KERNEL__ -DDEBUG
            KERNELDIR ?= /lib/modules/$(shell uname -r)/build
            PWD := $(shell pwd)
all:
                 $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
clean:
                 $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean
endif

Compile, load and unload the HelloWorld driver

make all
sudo dmesg -c
sudo insmod hello.ko

Testing:

The dmesg or cat /proc/kmsg or cat /var/log/messages

dmesg
[25635.014614] Hello World!
lsmod | grep hello
hello       12506  0

Unload

sudo rmmod hello

Verification:

A) The inclusion of #include <linux/init.h> resolves the below error.

[ 4427.052816] hello: module verification failed: signature and/or required key missing - tainting kernel

B) If vim is the editor of choice, below command formats it in linux kernel coding style.

:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab

C) Kernel Coding Guidelines compliance verification.

source/kernel/scripts/checkpatch.pl --file --terse hello.c
source/kernel/scripts/checkpatch.pl --file --terse Makefile

D) The KERNELRELEASE and KERNELDIR paths

“This makefile is read twice on a typical build. When the makefile is invoked from the command line, it notices that the KERNELRELEASE variable has not been set. It locates the kernel source directory by taking advantage of the fact that the symbolic link build in the installed modules directory points back at the kernel build tree. If you are not actually running the kernel that you are building for, you can supply a KERNELDIR= option on the command line, set the KERNELDIR environment variable, or rewrite the line that sets KERNELDIR in the makefile. Once the kernel source tree has been found, the makefile invokes the default: target, which runs a second make command (parameterized in the makefile as $(MAKE))to invoke the kernel build system as described previously. On the second reading, the makefile sets obj-m, and the kernel makefiles take care of actually building the module.”

E) KERNELDIR environment variable can be used to set kernel headers of arbitary kernel source to be used for compilation.

export KERNELDIR=/usr/src/linux-headers-3.13.0-54-generic
make clean
make all

Delete the environment variable using below command to compile module with installed kernel header sources.

unset KERNELDIR

Now, the default kernel headers present in /usr/src/linux-headers-3.13.0-53-generic are used.

F) Kernel Debug log compliance

Most of the driver writers are used to printk. More efficient optimized logging methods exist for kernel developers.

checkpatch.pl shows a warning message shown below, if printk is used. Use pr_* macros instead.

hello.c:33: WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(...  to printk(KERN_DEBUG ...
total: 0 errors, 1 warnings, 44 lines checked

Depending on what you are coding you should use a different print style:

printk(): never

pr_debug(): always good

dev_dbg(): prefered when you have a struct device object

netdev_dbg(): prefered when you have a struct netdevice object

[something]_dbg(): prefered when you have a that something object

G) pr_debug does not output the logs by default. Enable it in the Makefile with DEBUG flag.

Refer to https://www.kernel.org/doc/local/pr_debug.txt

CFLAGS_hello.o := -DDEBUG

H) Lastly, a note on copyrights. Adding the “or (at your option) any later version.” in the copyright header assures we are compatible with GPL v3 license terms.

/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/&gt;
*
* eud-task01-hello.c
* This kernel module prints string “Hello World!” when loaded.
*/

I) Debug

modinfo -h

modprobe

lsmod

Advertisements