CSE 422S: Studio 5

Loadable Kernel Modules


There was a big book with plain red leather covers; its tall pages were now almost filled. At the beginning there were many leaves covered with Bilbo's thin wandering hand; but most of it was written in Frodo's firm flowing script. It was divided into chapters but Chapter 80 was unfinished, and after that were some blank leaves.

"Why, you have nearly finished it, Mr. Frodo!" Sam exclaimed. "Well, you have kept at it, I must say."

"I have quite finished, Sam," said Frodo. "The last pages are for you."

The Return of the King, Book VI, Chapter 9

In this studio, you will:

  1. Build and install kernel modules
  2. Write a kernel module and use it to observe changes in a kernel variable

Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete. We encourage you to please work in groups of 2 or 3 people on each studio (and the groups are allowed to change from studio to studio) though if you would prefer to complete any studio by yourself that is allowed.

As you work through these exercises, please record your answers, and when you finish them each and every person who worked on them should please log into Canvas, select this course in this semester, and then upload a file containing them and also upload any other files the assignment asks for, and submit those files for this studio assignment (there should be a separate submission from each person who worked together on them).

Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.


Required Exercises

  1. As the answer to the first exercise, list the names of the people who worked together on this studio.

  2. On the Linux Lab cluster, please create a new directory to hold your kernel modules, e.g. /project/scratch01/compile/your-username/modules and cd into it. Save a copy of simple_module.c (a simple template for writing kernel modules in this course, based on the "Hello, World!" module shown on pages 338 and 339 of Robert Love's Linux Kernel Development, Third Edition) into that directory. In that directory also create a Makefile that contains the line

    obj-m := simple_module.o

    and save that file. You should then build the module by issuing the commands

    module add raspberry
    LINUX_SOURCE=path to your Linux kernel source code

    (note that the path above should end in something like linux_source/linux)

    and finally compile via

    make -C $LINUX_SOURCE ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=$PWD modules

    which if successful should produce a kernel module file named simple_module.ko

    As the answer to this exercise, show the output that was produced by make.

  3. Boot up your Raspberry Pi, open up a terminal window, create a directory to hold your kernel modules, and use sftp to get the simple_module.ko file you produced in the previous exercise.

    First, clear out the contents of the system log using the command

    sudo dmesg --clear

    and then use the insmod utility to load your kernel module into the kernel, as in:

    sudo insmod simple_module.ko

    If you recieved no error messages, then your module has been successfully loaded. To confirm this, you can check the system log by issuing the command

    dmesg

    which prints out the system log, which now should show the message that was printed when the module loaded. As the answer to this exercise, please show the message that appears in the system log.

  4. To confirm your module was loaded, you can also issue the command

    lsmod

    to see a listing of all currently loaded kernel modules. Verify that your module appears in the list, and as the answer to this exercise, copy the output that was produced by lsmod.

  5. The basic utility for removing modules from the kernel is called rmmod. When using this tool you can either specify the module name (as shown in lsmod) or you can specify a .ko file, as in

    sudo rmmod simple_module.ko

    Remove your module now, and verify its removal as before. As the answer to this exercise, copy the output of lsmod and the line of the system log, which show that the module was unloaded.

  6. One major reason for using kernel modules (as opposed to running a userspace program with root permissions) is that module code has direct access to all of the kernel's resources. Userspace programs, in contrast, must use system calls to access such resources, and even then, most of the kernel remains opaque to user processes.

    One kernel variable we've talked about previously is the jiffies counter. Recall that this variable keeps track of how many timer interrupts (also called ticks) have occured since system boot. Copy simple_module.c to a new file called jiffies_module.c and modify that new file, so that the system log messages that are generated when the module is loaded and unloaded also give the value of the jiffies variable (hint: it's an unsigned long) to the system log. Note that although this jiffies variable is not readily available to userspace programs, it is available directly when in kernelspace.

    Update your Makefile, build your new kernel module and use sftp to copy the jiffies_module.ko file that was produced for it, over to your Raspberry Pi.

    On your Raspberry Pi, load and unload your new kernel module, and as the answer to this exercise please copy and paste the system log message that shows the values of the jiffies variable when your module was loaded and when it was unloaded, and say how many ticks occurred between those messages.

  7. Things to turn in

    Please submit


    Optional Enrichment Exercises

  8. Module init and exit functions are supposed to use the "0/-E" return convention. This means that these functions should return a 0 on success, or in the event of a failure they return a negative value that is one of the error codes found in /include/uapi/asm-generic/errno-base.h. Modify your init function to return positive and negative values, respectively. As the answer to this exercise please describe briefly what happens when you load the module, and what you see in the system logs because of that.

  9. Kernel symbols that are available to be used in loadable modules are called exported symbols and are identified by the EXPORT_SYMBOL macro. You can see a list of all kernel symbols by looking at the file /proc/kallsyms, e.g. cat /proc/kallsyms. You might notice that this is very similar to the symbol table of a traditional application. (Which you can print with the program nm, if you've never done that before. Try it on any binary!) In fact, their syntax is identical, so you can use the command man 1 nm to find some more information about how to decode the contents of /proc/kallsyms.

    Symbols that have been exported can be found in this list with the prefixes __kstrtab_, and __ksymtab_. These prefixes denote a special kernel symbol that stores the name of the exported symbol and a struct that stores information about the symbol, respectively. See the definition of EXPORT_SYMBOL to see how these are generated. As the answer to this exercise, please list a few of those symbols that are available for use in a kernel module.

  10. The optional enrichment exercises in the Linux System Calls studio had you create a system call to retrieve a value directly from the Cycle Count Register (CCNT) on the Raspberry Pi's ARM CPU. Today, we will implement a kernel module to allow userspace programs to read the CCNT value without having to make a system call.

    If you did not complete this enrichment exercise in the System Calls studio, please download this driver file, and place it in the arch/arm/include/asm directory in your Linux kernel source tree.

    Now, please download the kernel module code, enable_ccnt.c, into the kernel modules directory you created in Exercise 2. Follow the instructions in Exercises 2 and 3 to compile the kernel module, transfer it to your Raspberry Pi with sftp, and load it using insmod.

    To verify that it loaded correctly, issue the command:

    dmesg | tail

    You should see messages written to the kernel log indicating that the Cycle Counter has been enabled, and showing a current cycle count value.

    Next, retrieve the same driver file onto your Raspberry Pi, and create a program that #includes it. Have the program call the function pmccntr_get(), which has the signature:

    unsigned long long pmccntr_get(void)

    In particular, call this function twice to return two cycle counts in a row. As the answer to this exercise, please say how many CPU cycles it takes to run this function. If you completed the enrichment exercise in the System Calls studio, compare this cycle duration to the number of cycles it took to make the system call.


Page updated Monday, October 12, 2020, by Marion Sudvarg and Chris Gill.