Eudyptula Challenge task 5
Contents
In this post, I want to share what I learnt by doing task 5 of the Eudyptula challenge.
The Eudyptula Challenge is a set of 20 tasks designed to help people get started with Linux kernel development.
Task 5 of the challenge is to make a hello world kernel module get loaded automatically when a USB keyboard is plugged in.
Loadable kernel modules
Linux kernel allows us to load modules to the kernel while the kernel is running. Once loaded, the module’s code executes in kernel mode and can access all the kernel’s global symbols.
Loadable kernel modules reduce the memory footprint of the kernel as most modules are loaded only when they are needed. It also reduces the attack surface. Any vulnerability in a module can only be exploited if the module is loaded.
Device drivers are either compiled with the kernel or are made available as kernel modules. Some of these kernel modules are loaded at boot time while others are loaded on demand, for instance, when a hardware device is plugged in.
Dynamic loading of kernel modules
1. udev
Whenever the kernel detects new hardware that is hotplugged into the system, it generates and sends a uevent to a userspace daemon called udevd. The uevent message contains information about the hardware ID of the device and the bus to which the device is connected to. We can view the stream of uevent messages using udevadm:
|
|
udevd processes these uevent messages and is responsible for
- creating or deleting device files in /dev directory and setting appropriate permissions for them
- executing scripts based on rules defined in /etc/udev/rules.d
- loading the kernel modules required by the device by invoking modprobe.
The udev daemon generates the modalias for the device by encoding the information from the uevent and passes that modalias to modprobe. For example, the alias of my USB keyboard is usb:v03F0p0324d0104dc00dsc00dp00ic03isc01ip01in00. (We can find that from the modalias file in the sysfs directory corresponding to the device.)
|
|
2. modprobe
Modprobe is a userspace utility used for loading kernel modules. The role of modprobe is to find and load all modules that a device might need by using the modalias passed by udevd.
But, How does modprobe know which modules to load?
Each driver module can specify the list of devices that it is written for using the MODULE_DEVICE_TABLE macro. During compilation, the module device table of each driver is encoded as a modalias for that driver.
We can find the modalias of a module using modinfo:
|
|
The modalias of every module is present in the module.alias and module.builtin.alias files present in /lib/modules/`uname -r`.
|
|
So now all modprobe has to do to load the correct modules is to go through the module.alias file and load modules with a modalias that matches the modalias passed by udevd.
We can list all modules that match with a modalias by using modprobe’s -R flag.
|
|
Difference between modprobe and insmod
modprobe and insmod are the two userspace utilities used to load kernel modules. The differences between the two are:
modprobe | insmod |
---|---|
Resolves module dependencies | Only loads the specified .ko (kernel object) file |
Loads module from default module path (/lib/modules/`uname -r`) | Loads module from user specified location |
Module parameters can be provided in config file in /etc/modprobe.d | Module parameters are given as commmand-line arguments |
Before loading a module, modprobe will load all modules that the current module depends on. modprobe finds dependencies between modules by looking at the /lib/modules/`uname -r`/modules.dep file.
Solution for the task
1. Adding a modalias
The solution for the task was simply to use the MODULE_DEVICE_TABLE macro to set the modalias of the hello world module to correspond to all USB keyboards.
To find the device table entry to use, I looked at the source code of the current USB keyboard driver in my system. The driver that handles USB keyboards is usbhid (hid stands for human interface device).
To find the driver that is being used for a device, we can use udevadm.
|
|
Usually, for every module, we can find a directory or C file with the module name in the kernel source. The source code of usbhid module is present under drivers/hid/usbhid. In that directory, the file usbkbd.c contains driver code for USB keyboards. We can find the device table entry for USB keyboards in that file.
The following snippet creates the device table with an entry corresponding to USB keyboards (taken from usbkbd.c) and registers it with the MODULE_DEVICE_TABLE macro.
|
|
I compiled the hello world module after adding the above snippet. After compilation, running modinfo shows us an alias entry.
|
|
2. Make modprobe detect the new driver
To make modprobe detect our module, we have to copy the module (.ko file) into the default module path (/lib/modules/`uname -r`) and use depmod to update the module.alias file. When used with the -A flag, depmod will only check files that are more recent than the module.alias file.
|
|
Now modprobe will be able to load our module and will load it automatically when a USB keyboard is plugged in.
References
Author Abdun Nihaal
LastMod 12-10-2021