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:
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:
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).
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.
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.
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/
.
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!
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!