4. Kernel Modules

4.1. Introduction

We generally do not make changes directly to the core kernel code. We mostly write small modules to be added to the kernel. This way we can separate out our changes from the mainline kernel.

If you see - the file system are thus modules only, as not all the Linux installations do not need all the file systems and the user can load them on demand.

In this chapter we will see how to write kernel modules.

4.1.1. Why this chapter

This chapter is the most important building block of the whole document. So spend some time with it. Do not just copy paste the code.

Type the code and then run it. Read the comments and type them as well so as to understand what the comments mean.

The more time you spend here - the lesser time you will spend later.

4.1.2. What will you learn

We will learn

  • How to write a basic kernel module.
  • How to write functions and make it available to other modules. Sort of library.
  • How to pass parameters to the modules which is getting loaded.

4.1.3. Concepts and Keywords involved

4.1.4. Prerequisites

C Programming.

4.2. What are Kernel Modules

  • Pieces of code that can be loaded and unloaded into the kernel upon demand.
  • Extends the functionality of the kernel without the need to reboot the system.
  • Device drivers which allows the kernel to access hardware connected to the system. If we plug any new device the kernel cannot use it properly unless we have the right drivers in place. The kernel detects the device type and loads the respective modules from the code base. Thus device drivers are generally modules (apart from the basic / legacy drivers.)

4.3. Kernel Module Advantages

  • Without modules, we would have to build monolithic kernels.
  • Adding new functionality would require adding the code in the kernel image which will need recompilation.
  • Monolithic kernel leads to larger kernel images resulting in loading code which is not required.
  • Rebooting machine whenever new functionality is added.
  • Example: till the time you don’t have to mount a Ext2 file system why will you load code for reading ext2 file system.

4.4. Tools for Kernel Modules

Before getting into writing a module we must first see the tools/commands which we will use to get the modules working.

4.4.1. insmod

  • insmod - insert module.

4.4.2. modprobe

  • modprobe -r - remove the module present in the kernel.

4.4.3. rmmod

  • rmmod - remove the module present in the kernel.

4.4.4. lsmod

  • lsmod - list the modules inserted in the kernel.

4.4.5. modinfo

  • modinfo - prints the information of the module.

4.4.6. dmesg

  • dmesg

4.4.7. syslog

  • syslog

4.4.8. Some System Calls

  • init_module(), query_module(), create_module(), delete_module() - system calls called by the various commands to insert/delete module to the kernel space.

4.5. New Functions

There are few new things which you will see in the code.

4.5.1. printk()

  • printk() - this is the kernel counter part of the user space printf() function. Its syntax is printk(LEVEL "FORMAT_SPECIFIER", parameters). The LEVEL is used to specify the severity of the message you wish to print. See different_log_levels_printk-label.

4.5.2. module_init()

  • module_init() - module_init() is the first function which will be called when the module is loaded. This gives us a entry point to the kernel module. Here we can initialize the global variables, setup functions, setup enviornment and other stuff. The use of this will be clear in the next chapters.

4.5.3. module_exit()

  • module_exit() - module_exit() is the function which is called when the module is being unloaded from the system. Here we can free the allocated memory, free the locks and do other cleanup actions. This will be clear in the coming chapters.

4.6. Hello World Kernel Module

4.6.1. Introduction

We will write our first kernel module. We will code it, compile it and then insert the module in the kernel. When the module gets inserted the kernel gets the functionality provided by the module. In this Hello World module, their is no functionality provided to the kernel. The module just gets inserted and it prints Hello World. You can see the message printed by the kernel module by running the dmesg tool. dmesg shows you the buffer of the kernel.

4.6.2. Code

4.6.2.1. FILE: mymodule.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
 * <PD> Hello World Module </PD>
 */

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

/*
 * This is the starting point of the kernel module's code execution.
 * Right now we will just print a hello and return from here.
 */

static int __init my_init(void)
{
	printk(KERN_INFO "Hello from Hello Module");
	return 0;
}

/*
 * This is the exit point of the kernel module. When the module is removed 
 * this function is called to do any sort of cleanup activities and other such
 *  stuff.
 * 
 * For example you have made a tree where you keep someinformation - you would 
 * like to place the code for removing the nodes of the tree here.
 */

static void __exit my_exit(void)
{
	printk(KERN_INFO "Bye from Hello Module");
}

module_init(my_init);
module_exit(my_exit);

MODULE_DESCRIPTION("Sample Hello World Module");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");

4.6.2.2. FILE: Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MODULE_FILENAME=mymodule

obj-m +=  $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko

export KROOT=/lib/modules/$(shell uname -r)/build

modules:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules

modules_install:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules_install

clean: 
	@$(MAKE) -C $(KROOT) M=$(PWD) clean
	rm -rf   Module.symvers modules.order

insert: modules
	sudo insmod $(KO_FILE)

remove:
	sudo rmmod $(MODULE_FILENAME)

printlog:
	sudo dmesg -c 
	sudo insmod $(KO_FILE)
	dmesg

4.6.3. Common Questions

4.6.3.1. How to run the code

To insert the module we need to use the insmod command with the module name. We need to be root while doing it.

In the Makefile we have a target insert. This target calls the insmod command to insert the module into the kernel.

4.6.3.2. How the code gets executed

On inserting the module the function registered as module_init() gets called. In our case it is my_init().

This function just prints a message to the logs and returns.

4.6.3.3. How to remove the module

To remove the module we need to use the rmmod command with the module name. We need to be root while doing it.

rmmod mymodule

4.6.3.4. How the module gets removed/unloaded

When the rmmod command is invoked the function registered with module_exit() is called. In our case it is my_exit(). The function just prints the messages in the logs and then returns.

4.7. Module Which Does Some Calculations

4.7.1. Introduction

The above module did not do anything and just printed some messages. We will now write a module which will do some calculations.

The intention of this excercise it to show that its plain C code and you can do the regular stuff here as well.

4.7.2. Code

4.7.2.1. FILE: mycalc.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
 * <PD> Module to demostrate how to do some calculations. </PD>
 */
#include <linux/module.h>
#include <linux/init.h>

struct sample {
    int x, y;
};

void print_sample_struct(struct sample temp) {
    printk (KERN_INFO "\nx is %d y is %d", temp.x, temp.y);
}

void calculator(void) {
    int a = 15, b = 3;
    int *ptra = &a;
    int i;

    struct sample temp;
    temp.x = 10;
    temp.y = 20;

    printk (KERN_INFO "\nSum is           %d", a+b);
    printk (KERN_INFO "\nDifference is    %d", a-b);
    printk (KERN_INFO "\nProduct is       %d", a*b);
    printk (KERN_INFO "\nDivison is       %d", a/b);
    printk (KERN_INFO "\nMod is           %d", a%b);
    printk (KERN_INFO "\nBitwise NOT of %d Is   %d", a, ~a);
    printk (KERN_INFO "\nBitwise OR is    %d", a|b);
    printk (KERN_INFO "\nBitwise AND is   %d", a&b);
    printk (KERN_INFO "\nBitwise XOR Is   %d", a^b);
    printk (KERN_INFO "\nLogical OR Is    %d", a||b);
    printk (KERN_INFO "\nLogical AND Is   %d", a&&b);

    if (a>b) {
        printk (KERN_INFO "\n%d is greater than %d", a, b);
    } else if (b>a) {
        printk (KERN_INFO "\n%d is greater than %d", b, a);
    } else {
        printk (KERN_INFO "\n%d is equal to %d", b, a);
    }

    printk (KERN_INFO "\nAddress of a is %p", ptra);
    printk (KERN_INFO "\nValue of ptra is %d", *ptra);

    /*
     * You can have loops as well.
     */

    for (i = b; i <=a; i++) {
        printk (KERN_INFO "\nPrinting i %d", i);
    }

    /*
     * You can have structures as well.
     */

    print_sample_struct(temp);
    
}

static int __init my_init(void)
{
	printk(KERN_INFO "Hello from Hello Module");
    calculator();
	return 0;
}

static void __exit my_exit(void)
{
	printk(KERN_INFO "Bye from Hello Module");
}

module_init(my_init);
module_exit(my_exit);

MODULE_DESCRIPTION("Sample Hello World Module");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");

4.7.2.2. FILE: Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MODULE_FILENAME=mycalc

obj-m +=  $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko

export KROOT=/lib/modules/$(shell uname -r)/build

compile:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules

modules_install:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules_install

clean: 
	@$(MAKE) -C $(KROOT) M=$(PWD) clean
	rm -rf   Module.symvers modules.order

insert: compile
	sudo insmod $(KO_FILE)

remove:
	sudo rmmod $(MODULE_FILENAME)

printlog:
	sudo dmesg -c 
	sudo insmod $(KO_FILE)
	dmesg

4.8. Module with Parameters

4.8.1. Introduction

In the above calculator if we want to change the variables, or we want to do calculation of different integers we will have to change the code and hardcode the value.

We do not want that, we want that the values should be taken from the user. For this we can pass the paramters to the kernel module to do it.

First we will see a small module which does it and then we will see it in the calculator program.

4.8.2. Code

4.8.2.1. FILE: mymodule_with_parameters.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
 * <PD> Program to add the passed parameters to a kernel module </PD>
 */
#include <linux/module.h>
#include <linux/init.h>

#define DEFAULT_PARAM1 100
#define DEFAULT_PARAM2 200

/*
 * Variable for integer parameter
 */
int param1 = DEFAULT_PARAM1;
int param2 = DEFAULT_PARAM2;

/* 
 * Get the parameters.
 */
module_param(param2, int, 0);
module_param(param1, int, 0);

static int __init my_init(void)
{
   printk(KERN_INFO "\nHello !! from Paramter Passing Demo Module\n");

   /*
    * Print the parameters passed
    */
   
   printk(KERN_INFO "\nThe sum of the parameters are :%d:", param1 + param2);
   
   printk(KERN_INFO "\nPassed Parameters\n");
   
   if (param1 == DEFAULT_PARAM1) {
      printk(KERN_INFO "\nNothing Passed OR Default Value :%d: for param1 is Passed\n", DEFAULT_PARAM1);
   } else {
      printk(KERN_INFO "\nparam1 passed is :%d:", param1);
   }

   if (param1 == DEFAULT_PARAM2) {
      printk(KERN_INFO "\nNothing Passed OR Default Value :%d: for param1 Passed\n", DEFAULT_PARAM2);
   } else {
      printk(KERN_INFO "\nparam2 passed is :%d:", param2);
   }

   return 0;
}

static void __exit my_exit(void)
{
   printk(KERN_INFO "\nBye from Parameter Passing Demo Module");
}

module_init(my_init);
module_exit(my_exit);

MODULE_DESCRIPTION("Module To Demonstrate Module Parameters");
MODULE_AUTHOR("rishi.b.agrawal@gmail.com");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");

4.8.2.2. FILE: Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
MODULE_FILENAME=mymodule

obj-m +=  $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko

export KROOT=/lib/modules/$(shell uname -r)/build

compile: clean
	@$(MAKE) -C $(KROOT) M=$(PWD) modules

modules_install:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules_install

clean: 
	@$(MAKE) -C $(KROOT) M=$(PWD) clean
	rm -rf   Module.symvers modules.order

insert: compile
	sudo insmod $(KO_FILE) param1=10 param2=20

remove:
	sudo rmmod $(MODULE_FILENAME)

printlog:
	sudo dmesg -c 
	sudo insmod $(KO_FILE)
	dmesg

output:
	sudo rmmod $(MODULE_FILENAME)
	sudo dmesg -c
	sudo insmod $(KO_FILE) param1=10 param2=20
	sudo dmesg -c

testsanity: clean compile insert remove

4.9. Module with Character Array and Arrays as parameters

4.9.1. Introduction

Passing paramters is not limited to integers only, we can pass characters and arrays as well. See the following example to understand how to do it.

4.9.2. Questions

How to pass floats?

4.9.3. Code

4.9.3.1. FILE: mymodule_with_parameters.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/*
 * <PD> Program to demonstrate arrays and strings for module paramters. </PD>
 */
#include <linux/module.h>
#include <linux/init.h>

#define DEFAULT_PARAM1 100
#define ARRAY_LEN 5
#define STRING_LEN 10

/*
 * Variable for integer parameter
 */
int param1 = DEFAULT_PARAM1;
module_param(param1, int, S_IRUGO | S_IWUSR);

/*
 * Variable for named parameters
 */
static int for_myself = 42;
module_param_named(for_world, for_myself, int, 0444);
MODULE_PARM_DESC(for_world, "For the world");

/*
 * Variable for integer array
 */
static int int_array[ARRAY_LEN];
int array_len;
module_param_array(int_array, int, &array_len, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(int_array, "Integer array for doing nothing");

/*
 * Variable for strings
 */
char test[STRING_LEN];
module_param_string(test_string, test, STRING_LEN, S_IRUGO | S_IWUSR);

static int __init my_init(void)
{
   int i = 0;
   printk(KERN_INFO "\nHello from Hello Module\n");
   printk(KERN_INFO "\nPassed Parameters\n");

   /*
    * Print the parameters passed
    */
   if (param1 == DEFAULT_PARAM1) {
      printk(KERN_INFO
             "\nNothing Passed or Default Value %d for param1 \
                              passed\n",
             DEFAULT_PARAM1);
   } else {
      printk(KERN_INFO "\nParam1 passed is %d", param1);
   }

   /*
    * Module Parameter named - see the file 
    * /sys/module/-module-name-/parameters
    */
   printk(KERN_INFO "\nValue of for_myself is %d", for_myself);

   /*
    * Integer array as a parameter
    */
   for (i = 0; i < array_len; i++) {
      printk(KERN_INFO "Interger Array element %d is %d", i,
             int_array[i]);
   }

   /*
      Print the Character array
    */
   printk(KERN_INFO "\nThe character array passed %s", test);
   return 0;
}

static void __exit my_exit(void)
{
   printk(KERN_INFO "Bye from Hello Module");
}

module_init(my_init);
module_exit(my_exit);

MODULE_DESCRIPTION("module to demonstrate module parameters");
MODULE_AUTHOR("abr");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");

4.9.3.2. FILE: Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MODULE_FILENAME=mymodule_with_parameters

obj-m +=  $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko

export KROOT=/lib/modules/$(shell uname -r)/build

compile:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules

modules_install:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules_install

clean:
	@$(MAKE) -C $(KROOT) M=$(PWD) clean
	rm -rf   Module.symvers modules.order

insert: compile
	sudo insmod $(KO_FILE)

remove:
	sudo rmmod $(MODULE_FILENAME)

printlog:
	sudo dmesg -c 
	sudo insmod $(KO_FILE)
	dmesg

4.10. Calculator with parameters

4.10.1. Introduction

In this module, we have mostly changed the calculator’s code to suite the parameter passing.

4.10.2. Code

4.10.2.1. FILE: mycalc_with_parameters.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
/*
 * <PD> Calculator  with parameters </PD>
 */
#include <linux/module.h>
#include <linux/init.h>

struct sample {
    int x, y;
};

void print_sample_struct(struct sample temp) {
    printk (KERN_INFO "\nx is %d y is %d", temp.x, temp.y);
}


/*
 * Variable for integer parameter
 */
int param1;
module_param(param1, int, 0);

int param2;
module_param(param2, int, 0);

void calculator(int a, int b) {
    int *ptra = &a;
    int i;

    struct sample temp;
    temp.x = 10;
    temp.y = 20;

    printk (KERN_INFO "\nSum is           %d", a+b);
    printk (KERN_INFO "\nDifference is    %d", a-b);
    printk (KERN_INFO "\nProduct is       %d", a*b);
    printk (KERN_INFO "\nDivison is       %d", a/b);
    printk (KERN_INFO "\nMod is           %d", a%b);
    printk (KERN_INFO "\nBitwise NOT of %d Is   %d", a, ~a);
    printk (KERN_INFO "\nBitwise OR is    %d", a|b);
    printk (KERN_INFO "\nBitwise AND is   %d", a&b);
    printk (KERN_INFO "\nBitwise XOR Is   %d", a^b);
    printk (KERN_INFO "\nLogical OR Is    %d", a||b);
    printk (KERN_INFO "\nLogical AND Is   %d", a&&b);


    if (a>b) {
        printk (KERN_INFO "\n%d is greater than %d", a, b);
    } else if (b>a) {
        printk (KERN_INFO "\n%d is greater than %d", b, a);
    } else {
        printk (KERN_INFO "\n%d is equal to %d", b, a);
    }


    printk (KERN_INFO "\nAddress of a is %p", ptra);
    printk (KERN_INFO "\nValue of ptra is %d", *ptra);

    /*
     * You can have loops as well.
     */

    for (i = b; i <=a; i++) {
        printk (KERN_INFO "\nPrinting i %d", i);
    }

    /*
     * You can have structures as well.
     */

    print_sample_struct(temp);
    
}

/*
 * This is the starting point of the kernel module's code execution.
 * Right now we will just print a hello and return from here.
 */

static int __init my_init(void)
{
    /*
     * Variable for integer parameter
     */
	printk(KERN_INFO "Hello from Hello Module");
    
    calculator(param1, param2);
	return 0;
}

/*
 * This is the exit point of the kernel module. When the module is removed 
 * this function is called to do any sort of cleanup activities and other such
 *  stuff.
 * 
 * For example you have made a tree where you keep someinformation - you would 
 * like to place the code for removing the nodes of the tree here.
 */

static void __exit my_exit(void)
{
	printk(KERN_INFO "Bye from Hello Module");
}

module_init(my_init);
module_exit(my_exit);

MODULE_DESCRIPTION("Calculator with parameters.");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");

4.10.2.2. FILE: Makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MODULE_FILENAME=mycalc_with_parameters

obj-m +=  $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko

export KROOT=/lib/modules/$(shell uname -r)/build

modules:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules

modules_install:
	@$(MAKE) -C $(KROOT) M=$(PWD) modules_install

clean: 
	@$(MAKE) -C $(KROOT) M=$(PWD) clean
	rm -rf   Module.symvers modules.order

insert:
	sudo insmod $(KO_FILE)

remove:
	sudo rmmod $(MODULE_FILENAME)

printlog:
	sudo dmesg -c 
	sudo insmod $(KO_FILE)
	dmesg

4.11. Conclusion

In this chapter we mostly learnt about the very basics of kernel module programming. We have a lot of ground to cover. Let’s get into other concepts of modules.

4.12. References