Writing and Inserting a 'Hello World' Kernel Module for the Beaglebone Black (Buildroot)

This blog follows my previous one on Setting Up Networking and an HTTP Server on the BeagleBone Black with Buildroot.

This is part of my independent study course on Embedded Linux and follows from many of my past blogs.

My final unit is an intro to Drivers for Linux systems. This blog post will only be on the practical methods of how to write and use a basic driver for a Linux system. Much of the theoretical knowledge from this can be found by reading the free book Linux Device Drivers, Third Edition.

Hello World!

I assume that you, the reader, have taken at least one programming course (It would be quite silly if you were looking to program and insert a Linux Kernel Module if you hadn't). So as you know, 'Hello world!' is the first exercise many of us follow when trying something new. So, we're going to keep that tradition alive with our first Kernel Module.

Our code for this exercise will be basic familiar C, with some new macros and header files you may not have used before:

#include <linux/module.h>    /* Needed by all modules */
#include <linux/kernel.h>    /* Needed for KERN_INFO */

int init_module(void){
    printk(KERN_INFO "Hello World!\n");

    return 0;
}

void cleanup_module(void){
    printk(KERN_INFO "Goodbye World.\n");
}

MODULE_LICENSE("GPL");

As you see, main() has been replaced by init_module(). They essentially act as the same thing, init_module() is the entry point for the file once it has been inserted into the kernel.

printk looks strikingly similar to printf with the addition of the KERNEL_INFO macro. This macro can be one of many things. One could find all of the options here, and more about printk on this kernel.org page.

Copy this code, and name the file hello.c

Compiling This Code

If you've been following my other blog posts, you might know that there is some setup that needs to be done to compile this code for our ARM board. The additional trouble comes here when we need special Linux header files to link to our code (linux/module.h and linux/kernel.h).

The Makefile

A Makefile will make this considerably easier, as there are a few compile steps needed:

CC = $(CROSS_COMPILE)gcc

obj-m := hello.o
KDIR := ~/buildroot/output/build/linux-6.0.9

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

Create a file named Makefile in the same directory as your code file, and copy this into it.

You will need to change the KDIR variable to be specific to your setup. This variable should be the path to the Linux source files that buildroot compiles for your board. These can be found at buildroot/output/build/linux-YOURVERSION. This points our compiler to the object files we need to link against.

If you were writing modules for your desktop computer, KDIR should point at the kernel headers specific to the version your PC is running on. But this is just an interesting tidbit, and we are writing for only our board today.

The Cross-compiler

We want to use the same compiler that buildroot uses to compile all the programs and Linux kernel. It might be possible to use another ARM compiler, but when it comes to compiling for different boards, and all of the minute configurations that can be done to these compilers, let's play it safe and use the same one.

If you're following my configuration from my earlier blog, you can find your compiler in buildroot/output/host/bin/. The one we need is the arm-none-linux-gnueabihf-gcc.

We need to tell our Makefile to use this compiler, otherwise, it will default to the gcc we have installed on our Linux desktop.

To compile our code, run the following command in the directory which contains the code file, and Makefile (of course changing the path to be valid on your machine):

make CROSS_COMPILE=~/buildroot/output/host/bin/arm-none-linux-gnueabihf- ARCH=arm

We didn't include the gcc on the end, because that is implied with the Makefile

Running this, your code should compile, and you should see a bunch of new object files, but the most important one for us is the hello.ko file. This is a compiled kernel object file which we can use on our buildroot Beagelbone board.

Inserting The Kernel Module

Copy this file onto the board. If you have the correct configuration, you can scp or sftp this file. Otherwise, you can copy this to the SD card.

In the terminal on the board, insert the module with insmod hello.ko. Check the kernel output with cat /var/log/messages. You should see your Hello World!

To remove this module, simply rmmod hello.ko. Check the output again and you'll see Goodbye world.

Congratulations! You just wrote your first kernel code.