September 29, 2018

Linux Kernel Module Development - Part 1


Introduction

I plan on playing around with the Linux Kernel a little bit. Most of the work will probably be related to writing Linux Kernel Modules (LKMs). As I find it easier to learn by doing things, I will structure these experiments in the following way:

  • Set a goal
  • Figure out what is needed
  • Write the code
  • Document what is learned while figuring things out and coding
  • Document functions/macros used
  • Finish up

All the experiments will be developed targeting the x86_64 platform unless otherwise noted, and the target system will be running Xubuntu. The kernel version used on each experiment will be mentioned at the introduction of each post.

This post will detail how to setup the environment to compile and run the code that is produced. If you already have an environment setup where you can compile a kernel module, then feel free to skip this post completely. The other posts in the series will not contain these steps and will follow the steps mentioned above.

I am writing these things mostly for my future reference, but I figured I could share it to help anyone that is interested to learn as well. If that is your case, know that the following things are assumed about you:

  • You know your way around a shell
  • You know how to code in C (at least enough to write simple things)
  • You know enough to get a VM with Linux up and running
  • You know how to SSH into a Linux box

Setting up the environment

The following sections describe how I have setup my environment. In order to make life easier, I got a VM running where I will perform all development and testing of the code.

To make it easier and faster for me to connect to the VM, it will have an SSH server running that will be configured to accept connections from my box using a key pair without password (so I don’t have to be typing a password all the time).

Install a Linux Distribution

For now, I will start with Xubuntu 18.04. For each post I will mention the version of Xubuntu and the Kernel version I am using for each LKM.

I just setup a typical Xubuntu VM here by performing the default installation. I am using Qemu for this, but it should work fine using VMWare of VirtualBox.

Setup environment

With the VM installed, initialize it and take note of its IP address. Still in the VM, install the openssh-server package as well as the other packages that will be needed to compile the kernel modules:

$ sudo apt-get install openssh-server
$ sudo apt-get install make gcc libelf-dev

In the host machine perform the following steps to configure it to connect to the guest via SSH using a key pair.

Generate the key (do not setup a password for the key as the goal here is to make it easy to connect to this VM).

$ mkdir ~/.ssh/
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/kdev

Edit the ~/.ssh/config file and add the following entry to it (replace the value for the Hostname and User directives with the IP address of your VM and the user that you created when you installed the operating system):

Host kdev
    Hostname 192.168.146.130
    Port 22
    User your_username
    IdentityFile ~/.ssh/kdev

From the host machine, transfer the ~/.ssh/kdev.pub file to the VM:

$ scp ~/.ssh/kdev.pub kdev:/tmp/

SSH into the guest VM with your user and do the following:

$ cd ~
$ mkdir .ssh
$ cat /tmp/kdev.pub >> ~/.ssh/authorized_keys
$ chmod 644 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys
$ rm /tmp/kdev.pub

Now disconnect from the VM, and back in the host do:

$ ssh kdev

You should be logged into the VM as the user your_username without any password being requested.

Install the kernel source (optional)

If you want to have a local copy of the kernel source in order to consult it, or for the case you want to recompile the kernel yourself, then follow the steps below. This is not needed to compile the kernel modules, as the headers are already installed, but it is a good idea to be able to easily navigate the source.

In order to get the kernel source for the current kernel, start by updating all the packages:

$ sudo apt-get update 
$ sudo apt-get upgrade

As root, edit the /etc/apt/sources.list file and uncomment the deb-src repositories and then run the following commands as a regular user:

$ mkdir ~/code
$ cd ~/code
$ sudo apt-get update
$ sudo apt-get install dpkg-dev
$ apt-get source linux
$ sudo apt-get build-dep linux-image-$(uname -r)

You should have the source code for the kernel now under $HOME/code/.

Test the environment

Create a simple module just to make sure that everything can be compiled and is working as expected.

Use the following example code to make sure everything is setup properly.

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

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void) {
    printk(KERN_ALERT "Hello world from the kernel!\n");
    return 0;
}

static void hello_exit(void) {
    printk(KERN_ALERT "Goodbye!\n");
}

module_init(hello_init);
module_exit(hello_exit);

Save the above code in a file called hello.c.

Now create a file called Makefile in the same directory where you saved the hello.c file and add the following to it:

obj-m := hello.o

Now use the following command to compile the module:

$ make -C /lib/modules/$(uname -r)/build M=`pwd` modules

If everything went okay, you should have a file hello.ko in the current directory now.

To test that the module can be loaded and unloaded properly, use:

$ sudo insmod hello.ko
$ sudo rmmod hello
$ dmesg |tail

The dmesg command above should show something like the following:

[  184.658626] hello: loading out-of-tree module taints kernel.
[  184.658661] hello: module verification failed: signature and/or required key missing - tainting kernel
[  184.659164] Hello world from the kernel!
[  195.104651] Goodbye!

At this point you should be good to get started developing LKMs!

Finishing up

This is just an introduction to get the environment ready. As mentioned before, the next posts will focus only on a specific goal and will dive directly into the code.

Thanks for reading!