Build a kernel module

Linux device drivers reside in loadable kernel modules. A kernel module is a collection of C language routines which implement the device driver functionality while adhering to the Linux conventions and interface for driver software.

Pretty much any functionality can be captured in a loadable kernel module, not just software that communicates with devices like the keyboard, mouse, SD card, etc. Before there was Performance Events for Linux (PERF), there was the profiling infrastructure called “Oprofile”. Oprofile uses the performance counters to profile kernel- and user-space software. Unlike PERF, which uses a system call (SYSCALL) for user/kernel communication, oprofile used a kernel module to access the performance counters and a user-space daemon process to collect performance event data in the form of samples. Our first approach to Raspberry Pi (ARM11) performance measurement uses a loadable kernel module named “aprofile.ko” to set up access to the hardware performance counters.

This page describes the process of building, loading and unloading the aprofile.ko kernel module. (The internal design of the module is discussed elsewhere.) We are doing an “out-of-tree” build. In other words, the source for the kernel module is somewhere in our home directory structure, not the kernel source directory like the standard device drivers that come with Linux.

Get ready — prepare to build

Raspbian Wheezy is distributed as a prebuilt binary image. In order to build a kernel module, we need the kernel headers (or kernel source) that match the binary image. The headers provide vital definitions that are needed in order to compile the source code for the module. Also, Linux performs a safety check called “version matching” when it loads a finished kernel module. The kernel version and module version must match or, at best, Linux complains, or, at worst, Linux refuses to load the module.

First, you need to know the version number of the kernel itself. You discover the version using the uname command:

uname -a

This command displays a string like:


Linux raspberrypi 3.6.11+ #371 PREEMPT Thu Feb 7 16:31:35 GMT 2013 armv6l GNU/Linux

The kernel version number is “3.6.11+”.

You will need to find the kernel headers (or full source code) for “3.6.11+”. Ordinarilly, you can download the kernel headers or source using Synaptic. Unfortunately, the latest version of the Linux headers (linux-header-XXX, where XXX is the kernel version) and full Linux source code (linux-source-XXX) available through Synaptic is 3.6.9. This won’t do because the source code doesn’t match the installed kernel image.

Luckily, the full kernel source is available from github.com, which is the official repository for Linux code. Perform the following commands. (You will need to sudo them.)


cd /usr/src
wget https://github.com/raspberrypi/linux/archive/rpi-3.6.y.tar.gz
tar xvz rpi-3.6.y.tar.gz
mv linux-rpi-3.6.y linux
ln -s /usr/src/linux /lib/modules/3.6.11+/build

This sequence of commands: 1. changes the working directory to /usr/src, 2. downloads the full Linux source code, 3. decompresses and unpacks the source code in the compressed TAR file, 4. renames the source directory to “linux”, and 5. makes a link from /lib/modules/3.6.11+/build to /usr/src/linux.

The compressed source code is about 110MBytes in size. This is the full source for the kernel including all of the headers. Strictly speaking, we just need the headers, but you might want to explore the kernel, other kernel modules, ARM-specific support code, or the PERF subsystem. It’s all there.

The standard place for kernel source is within /usr/src. You may have more than one copy of the source in this directory assuming that each copy of the source is in a separate directory. Usually, the source directory is named with the version which it contains. In the commands above, we renamed the source directory to simply “linux”. Kernel modules are stored in /lib/modules/3.6.11+. You need a link named “build” in this directory that points to the matching Linux source directory.

The next set of steps prepare the source tree for module building. (Don’t forget to sudo!)


cd /lib/modules/3.6.11+/build
make mrproper
gzip -dc /proc/config.gz > .config
make modules_prepare

These commands 1. go to the build directory, 2. clean up the source tree, 3. copy and decompress the current kernel configuration, and 4. set up the source tree to build kernel modules. The last step takes several minutes to execute.

The build directory must have a copy of the matching module version information before building. Module version information is stored in a file named /usr/src/linux/Module.symvers. This file is created during the kernel build — a process that would take 10+ hours on the Raspberry Pi. Fortunately, Module.symvers can also be downloaded from github.com.


wget https://github.com/raspberrypi/firmware/raw/master/extra/Module.symvers

This file must match the installed kernel! I strongly recommend saving a copy of this file in your home directory just in case you need it again.

Finally, you need to generate the module dependency and map files.


depmod -a

Now you’re ready to build the kernel module.

Get set — make the module

We’re going to use a Makefile to build and install the kernel module named aprofile.ko. Here is the Makefile.


TARGET = aprofile.ko

MDIR = arch/arm/aprofile

DRIVER_OBJS = aprof.o

CURRENT = $(shell uname -r)
KDIR = /lib/modules/$(CURRENT)/build
PWD = $(shell pwd)
DEST = /lib/modules/$(CURRENT)/kernel/$(MDIR)

obj-m := aprofile.o
aprofile-objs := $(DRIVER_OBJS)

default:
$(MAKE) -C $(KDIR) M=$$PWD

install:
@if test -f $(DEST)/$(TARGET).orig; then\
echo "Backup of aprofile.ko already exists."; \
else \
echo "Creating a backup of aprofile.ko."; \
mv -v $(DEST)/$(TARGET) $(DEST)/$(TARGET).orig; \
fi
su -c "cp $(TARGET) $(DEST) && /sbin/depmod -a"

revert:
@echo "Reverting to the original aprofile.ko."
@mv -v $(DEST)/$(TARGET).orig $(DEST)/$(TARGET)

clean:
rm -f *.o aprofile.ko .*.cmd .*.flags *.mod.c

-include $(KDIR)/Rules.make

The first few lines in the Makefile define the TARGET file for the completed module (aprofile.ko), where to put the completed module (/lib/modules/3.6.11+/kernel/arch/arm/aprofile), and the object files that are part of the module (aprof.o). The next four lines define the paths to the kernel build directory and the destination module directory. The next three lines define the build rules. By default, make will build the kernel module.

The install rules make a backup copy (aprofile.ko.orig) of any existing aprofile.ko file. The install rules copy the new module (aprofile.ko) to the destination directory and update the module dependencies.

The revert rules copy the backup (original) module to aprofile.ko. Revert backs up to the old aprofile.ko, just in case the new module implementation didn’t work out so well.

The clean rule deletes the intermediate work products. Finally, the Makefile includes the kernel build rules.

Just type make in the directory with the Makefile and the module source code.


make

Make goes to work and produces output showing the sequence of steps that it performed to build the module.


make -C /lib/modules/3.6.11+/build M=$PWD
make[1]: Entering directory `/usr/src/linux'
LD /home/pjd/Module/built-in.o
CC [M] /home/pjd/Module/aprof.o
LD [M] /home/pjd/Module/aprofile.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/pjd/Module/aprofile.mod.o
LD [M] /home/pjd/Module/aprofile.ko
make[1]: Leaving directory `/usr/src/linux'

Then, take a deep breath and install. Remember to sudo!


sudo make install

Go — use the module

You need to load the kernel module using the modprobe command.


sudo modprobe aprofile

The lsmod command shows the list of currently loaded modules. Enter “lsmod”, then look for “aprofile” in the list of modules. Unload the module by specifying the “-r” option to modprobe.


sudo modprobe -r aprofile

The aprofile module announces its load and unload in the kernel log (/var/log/kern.log). Enter “dmesg | tail” to see the last few kernel log entries and to see if aprofile has properly loaded or unloaded.

Copyright © Paul J. Drongowski