Build a kernel module (redux)

Now that the Raspberry Pi 2 (AKA “gen 2”) is here, it’s time to build a loadable kernel module for it.

A loadable kernel module is a good way to implement a device driver. Maybe you’ve constructed some fantastic new device for the Raspberry Pi and you need a driver for it. Or, maybe you want to obtain user-space access to the performance counters. Encapsulate your solution as a loadable kernel module and you can dynamically load and unload the driver as you need it. Dynamic load/unload is really handy during the development and debug stage as you do not need to reboot the system to try a new version of the driver.

The discussion on this page assumes that you have read my earlier article on building a kernel module for the gen 1 RPi. Like that article, we are building the module “out-of-tree,” that is, in our own user directory outside of the Linux source tree.

Please note that some of the information below is speculative because I still have not successfully built a loadable kernel module for RPi2. It’s not clear if this is due to “pilot error” or early bugs in Raspbian Wheezy.

Get the source for your kernel

You need the source for the specific version of the kernel that you are running. In this discussion, we are building for version 3.18.7-v7+ build on 16 February 2015. Use the command:

uname -a

to obtain the version information for your kernel.

One way to obtain the source is to check it out from github.com. The other way (shown here) is to obtain a tarball from github.com. I suggest using the get-kernel-source.sh script.

#!/bin/bash

REV=`zcat /usr/share/doc/raspberrypi-bootloader/changelog.Debian.gz | grep '* firmware as of' | head -n 1 | sed  -e 's/\  \*\ firmware as of \(.*\)$/\1/'`

rm -rf rasp-tmp
mkdir -p rasp-tmp
mkdir -p rasp-tmp/linux

wget https://raw.github.com/raspberrypi/firmware/$REV/extra/git_hash -O rasp-tmp/git_hash
wget https://raw.github.com/raspberrypi/firmware/$REV/extra/Module.symvers -O rasp-tmp/Module.symvers

SOURCEHASH=`cat rasp-tmp/git_hash` 

wget https://github.com/raspberrypi/linux/tarball/$SOURCEHASH -O rasp-tmp/linux.tar.gz

cd  rasp-tmp
tar -xzf linux.tar.gz

OSVERSION=`uname -r`

mv raspberrypi-linux* /usr/src/linux-source-$OSVERSION
ln -s /usr/src/linux-source-$OSVERSION /lib/modules/$OSVERSION/build

cp Module.symvers /usr/src/linux-source-$OSVERSION/

zcat /proc/config.gz > /usr/src/linux-source-$OSVERSION/.config

cd /usr/src/linux-source-$OSVERSION/
make oldconfig
make prepare
make scripts
cd ~

This script finds the correct version of the source and Module.symvers file for the kernel, downloads the tarball and decompresses the tarball into /usr/src. It also performs some initial preparation of the source tree. This script is really well done and I tip my hat to the author.

Module7.symvers

The Raspbian Wheezy build produces two symvers files: Module.symvers and Module7.symvers. Although no one has said anything officially (Arrrg!), I believe that the Module7.symvers file is the correct file to use with the RPi2 (Broadcom BCM2836 ARMv7). the kernel would not even attempt to load the “Hello World” module when it is built with Module.symvers. When I copied Module7.symvers to the build directory and renamed it, the kernel would at least attempt to load the module — before dying. This is where my work is literally and figuratively hung.

The script needs to be modified to download Module7.symvers or you may do this by hand, e.g.,

wget https://raw.github.com/raspberrypi/firmware/master/extra/Module7.symvers -O Module7.symvers

You need to replace “master” with the revision that you want unless you really want the latest Module7.symvers file. This latter case is if you are working with the most recently checked-in source. I have also snagged Module7.symvers with the following command:

wget https://github.com/raspberrypi/firmware/raw/47bd0f0f46/extra/Module7.symvers

Here, 47bd0f0f46 identifies the branch which is associated with 3.18.7-V7+ on 16 February 2015.

Troubleshooting modprobe failure

Once you have built and installed the kernel module, you need to load it using the modprobe command:

sudo modprobe aprofile

“aprofile” is the name of my module and you should substitute the name of your own module.

Currently, this command is killing the kernel. When this happens, use the dmesg command to display the most recent kernel messages. Here is output from the kernel after modprobe fails.

[ 2622.691093] Unable to handle kernel paging request at virtual address fe11623c
[ 2622.698354] pgd = b3818000
[ 2622.701073] [fe11623c] *pgd=00000000
[ 2622.704675] Internal error: Oops: 5 [#1] PREEMPT SMP ARM
[ 2622.709988] Modules linked in: aprofile(O+) snd_bcm2835 snd_pcm snd_seq snd_seq_device snd_timer snd evdev joydev hid_logitech uio_pdrv_genirq uio
[ 2622.723244] CPU: 1 PID: 3260 Comm: modprobe Tainted: G           O   3.18.7-v7+ #755
[ 2622.730987] task: b87578c0 ti: b384a000 task.ti: b384a000
[ 2622.736406] PC is at load_module+0x18a8/0x2004
[ 2622.740859] LR is at load_module+0x1894/0x2004
[ 2622.745303] pc : [<80094dd0>]    lr : [<80094dbc>]    psr: 90000013
[ 2622.745303] sp : b384be88  ip : 7f08b230  fp : b384bf44
[ 2622.756764] r10: 7f08b0c0  r9 : 00000000  r8 : 7f08b0cc
[ 2622.761979] r7 : 8053bd84  r6 : b8495cc4  r5 : fe116228  r4 : b384bf48
[ 2622.768494] r3 : 00000001  r2 : b384be70  r1 : b85525f0  r0 : 807f6a58
[ 2622.775010] Flags: NzcV  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[ 2622.782132] Control: 10c5387d  Table: 3381806a  DAC: 00000015
[ 2622.787872] Process modprobe (pid: 3260, stack limit = 0xb384a238)
[ 2622.787878] Stack: (0xb384be88 to 0xb384c000)

I do not know (yet) why the paging request is failing. However, please note a few points. modprobe is “tainted.” The single letter “G” indicates that the aprofile module is licensed under GPL. Unfortunately, not too much information here.

Here is a table of single character taint codes:

  • P: A module with a Proprietary license has been loaded, i.e. a module that is not licensed under the GNU General Public License (GPL) or a compatible license. This may indicate that source code for this module is not available to the Linux kernel developers or to SUSE developers.
  • G: The opposite of ‘P’: the kernel has been tainted (for a reason indicated by a different flag), but all modules loaded into it were licensed under the GPL or a license compatible with the GPL.
  • F: A module was loaded using the Force option “-f” of insmod or modprobe, which caused a sanity check of the versioning information from the module (if present) to be skipped.
  • R: A module which was in use or was not designed to be removed has been forcefully Removed from the running kernel using the force option “-f” of rmmod.
  • S: The Linux kernel is running with Symmetric MultiProcessor support (SMP), but the CPUs in the system are not designed or certified for SMP use.
  • M: A Machine Check Exception (MCE) has been raised while the kernel was running. MCEs are triggered by the hardware to indicate a hardware related problem, for example the CPU’s temperature exceeding a threshold or a memory bank signaling an uncorrectable error.
  • B: A process has been found in a Bad page state, indicating a corruption of the virtual memory subsystem, possibly caused by malfunctioning RAM or cache memory.

When the kernel is tainted, the remaining debug information may itself be unreliable and incorrect.

Determining if the kernel is tainted

The kernel remembers the (un)tainted state of the kernel in /proc/sys/kernel/tainted. If the kernel is untainted, then this file contains “0”. If the kernel is tainted, then this file contains a non-zero value which is the sum of one or more taint flags. After the failure above, this file contains 4224:

cat /proc/sys/kernel/tainted
4224

which is the sum of 4096 and 128. This value indicates that an out-of-tree module was loaded and that the system died. Gee, do you think? Until I get more information, I’m pretty much stuck at this point.

The file https://www.kernel.org/doc/Documentation/sysctl/kernel.txt contains a list of the taint codes:

   1 - A module with a non-GPL license has been loaded, this
       includes modules with no license.
       Set by modutils >= 2.4.9 and module-init-tools.
   2 - A module was force loaded by insmod -f.
       Set by modutils >= 2.4.9 and module-init-tools.
   4 - Unsafe SMP processors: SMP with CPUs not designed for SMP.
   8 - A module was forcibly unloaded from the system by rmmod -f.
  16 - A hardware machine check error occurred on the system.
  32 - A bad page was discovered on the system.
  64 - The user has asked that the system be marked "tainted".  This
       could be because they are running software that directly modifies
       the hardware, or for other reasons.
 128 - The system has died.
 256 - The ACPI DSDT has been overridden with one supplied by the user
        instead of using the one provided by the hardware.
 512 - A kernel warning has occurred.
1024 - A module from drivers/staging was loaded.
2048 - The system is working around a severe firmware bug.
4096 - An out-of-tree module has been loaded.
8192 - An unsigned module has been loaded in a kernel supporting module
       signature.
16384 - A soft lockup has previously occurred on the system.