Inspirational Quotes and Speeches

Monday, February 28, 2011

LINUX KERNEL MODULE PROGRAMMING

As we saw in the integration design, fundamentally linux kernel is a monolithic kernel with the capability to load / unload services in runtime. It means that all the essential services plus core kernel is build and loaded as a single static kernel image. Once this static linux kernel image is loaded into memory and system is up and running , you can add the extra piece of compiled code to this running linux kernel. This is one way you can extend the functionality of a running kernel. The code is added to the kernel at runtime is called as kernel module. So, at anytime when system is running , you can add some functionality or service to the kernel. Once that service is no longer needed , you can remove ( unload ) that from kernel in run-time. So kernel module acts like a plug-in which can be loaded / unloaded on-demand to the running kernel.

So, you can add some code to the existing kernel code in two ways. One is by adding the code in the core kernel services and building it along with the static image. Once this code is build with static kernel image, there is no way to remove it ,it will remain the part of static image. So, the code that is essential to build with image falls in this category. E.g. if you want to change some kernel data structures or changing some core kernel code then use this way of changing code. Another way of adding code is through kernel modules. This code can be added / removed from the kernel code at runtime. Modules are mainly used,

  • To add new service or functionality.
  • Modify the existing service or functionality that does not effect rest of the kernel.
  • extend the functionality of the existing kernel.
  • Adding debug information to kernel

During the course , we will see module implementation for all above. Most of the times you will see kernel modules in device drivers and file systems.

1.1: Writing First Module – Hello World!!

Time to get started with programming and respecting the tradition, let’s start learning kernel module programming by writing the Hello World kernel module. Once you mastered writing, building and running the hello world kernel module, you have crossed the first big obstacle.

Majority of the kernel code is written in ‘C’ while a very little part of kernel code is in assembly. Likewise most of the times you will write module code in ‘C’ , though some occasions you may have to use assembly language too.

One point worth mentioning is that the kernel space ‘C’ library is different than the user space ‘C’ library. So, as a rule , while writing code in kernel space , you will avoid using any user space library call in module code. Therefore you will not use any user space API in your code.

Let’s create a file with name Hello.c that contains the source code of our first module. You can create this source file anywhere. Just create a directory with name Modules under your home directory, like /home/Modules. We decide all our module code to reside under this directory.

Below is the code given for our Hello world module. Open a new file Hello.c and type this code in this file.

# vi /home/Modules/Hello.c

// Your first kernel module HelloWorld.c

#include

#include

// hello_init the init function, called when the module is loaded.

// Returns zero if successfully loaded, nonzero otherwise.

static int hello_init(void)

{

printk((KERN_ALERT "Hello World !!\n");

return 0;

}

// hello_exit the exit function, called when the module is removed.

static void hello_exit(void)

{

printk((KERN_ALERT "Good Bye World !!\n");

}

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("LEARNING LINUX KERNEL");

MODULE_DESCROPTION(“ HELLO WORLD MODULE”);

Fig. 1.1 Hello World Module Code

Now let’s understand the module code line by line.

1.1.1 Headers:

After macro definitions, next comes the header file section. Every module includes various header files. Some header files depend upon to which particular subsystem we are going to add the module, e.g. memory, networking, block device driver etc. There are some header files which are independent to which subsystem we are adding code. No matter for whatever purpose we are writing the module , those header files have to be included always. Those header files are, module.h , version.h, kernel.h and init.h . So, always after defining macros , we have to write below 4 include statements.

# include

# include

Fig. 1.2 – Mandatory header files for a module.

Let’s see the purpose of these header files one by one.

  • module.h : Module Subsystem is responsible for loading and unloading of kernel modules. module.h header file is the interface of the module subsystem. It contains the signature of all the functions of module subsystem.
  • init .h: In kernel module code , we write one init routine and one cleanup routine. They act like a resource allocation routine and resource cleanup routine. While writing a kernel module code, it is mandatory to write both these routines. The signature of these two functions is given in this header file. And you should take care that the signature of init and exit routines in your kernel module exactly matches with them.

1.1.2: Entry and Exit Routines:

Next comes hello_init and hello_exit functions. These functions are initialization and cleanup routines of a module. Conventionally they are referred as entry and exit routines. At runtime , when module gets loaded , the first function executed is entry routine. So, for a linux kernel module , entry routine is analogous to main() function in user space ‘C’ program. Therefore it is mandatory to provide the entry routine to every kernel module. The moment linux kernel module is inserted , entry routine starts executing.

Similarly, when the module gets unloaded , exit routine gets executed. All the acquired resources by module are released by the exit routine.

We are free to choose any names for entry and exit routines but the signature of these two routines are fixed . So, whatever name we give to entry and exit routines , the signature of those routines must match with the given signatures.

static int hello_init(void)

{

printk((KERN_ALERT "Hello World !!\n");

return 0;

}

static void hello_exit(void)

{

printk((KERN_ALERT "Good Bye World !!\n");

}

Fig. 1.3 – entry and exit functions.

Now, we have defined the hello_init and hello_exit functions. For working them with the linux kernel, kernel also should have information about the entry and exit routine of a module. These are special routines of a module and we inform kernel about these routine by registering them with the linux kernel. Registering those functions to the kernel is easy and achieved by macros , module_init() and module_exit() .

1.1.4: printk():

Finally few word about printk() . printk() prints a kernel message. Its syntax is same as of printf(). It is available to entire kernel space. But, unlike printf() , by default printk() does not print on console. Infact printk() is more a kernel logging mechanism that logs the messages into different log files. We can view the output of printk() in /var/log/messages or also we can use 'dmesg' command to see the output of printk(). For now, only this much detail for printk() . We have lot more to say on printk() in later chapters.

1.1.5: Comments and License:

The last 3 lines of our hello world kernel module comprises ‘comments and license’ section.

MODULE_DESCRIPTION (“HELLO WORLD MODULE”);

MODULE_AUTHOR ("LEARNING LINUX KERNEL");

MODULE_DESCRIPTION (“HELLO WORLD MODULE”);

Fig. 1.4 – Module Comments.

These are module comments and add additional information to the module. Unlike use space code comments these module comments go with the module code and become the part of compiled kernel module. MODULE_AUTHOR comment adds important information to the kernel module like module author name and his contact information. In case module is not functioning well , author can be contacted and intimated about the problem with the help of this information.

Linux kernel code is open source and released under GPL license. In case if you add a module to the linux kernel , you must decide the licensing of your module separately. Your module can be licensed under GPL or it can remain propriety too. But if it is propriety then your module will not be able to use linux kernel functions and data structures as entire linux kernel code falls under GPL. As per GPL terms, if you want to use any GPL licensed code , you have to license your code also under GPL. Because of this reason usually all kernel modules falls under GPL licensing scheme. Also, if your module is not GPL then you may not receive support of the open source community too.

You can have a look into module.h header file to know different available licensing schemes.

# cd linux 2.6.33/include/linux

# vi module.h

“GPL”

“GPLvl”

“GPL and additional rights”

“Dual BSD/GPL”

“Dual MPL/GPL”

“Proprietary”

Fig. 1.5 – Module Licensing

1.2: Building Linux Kernel Module:

We completed writing our first ‘Hello World‘ linux kernel module. Now next job is building this module. Unlike the normal user space program , it is not possible to build a kernel module directly by issuing ‘gcc’ command. For building a module, we have to make use of entire framework that builds the linux kernel. This framework is called the ‘kbuild’ subsystem. ‘kbuild’ subsystem contains various makefiles and scripts. We also have to write one Makefile to build our HelloWorld module. This Makefile will be an extension to the ‘kbuild’. Create a Makefile at location where you have saved your Hello.c file.

# cd /home/Modules

# vi Makefile

Now, type below given code in this Makefile. It will be the Makefile to build our Hello World module.

obj-m +=Hello.o

KDIR= /usr/src/linux-2.6.33.1

all:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:

rm -rf *.o *.ko *.mod.* .c* .t*

Fig. 1.6 – Makefile for Hello World Module

Look at the first line of this Makefile, obj-m +=Hello.o . This line is of interest to us. By adding this line in the Makefile , we are telling ‘kbuild’ subsystem to build Hello.ko from Hello.c. Hello.ko is a kernel module , .ko is the extension for linux kernel modules. So, for building your module, just add this line obj-m += on top of the Makefile. You don’t have to care about rest of the lines in this Makefile.

We will take this Makefile as a template Makefile for future.

Now on command line just issue the make command to build your first module, Hello.ko.

# make

Just use ls –ltr command , and look for Hello.ko at the current location.

# ls –ltr

1.3 Loading / Unloading Modules:

Remember , very important, Linux kernel modules are very much kernel version specific. So, the module built on one kernel version cant be loaded on any other kernel version. They are very strictly kernel specific.So, if you try to load the module on a different kernel version, you will get an error saying “invalid module format”. Modules can be inserted using any one of the utilities, ‘insmod’ and ‘modeprobe’ . insmod utility allocates memory to hold the module and copies the module in that memory region. It then resolves all the kernel references by looking into the ‘kernel symbol table’. Kernel symbol table is a place where all kernel symbols( variables and functions) are kept along with their respective base addresses. We will discuss more on kernel symbol in our next chapter.

To load a module using insmod , just use insmod on command prompt.

# insmod Hello

For unloading / removing a module from running kernel , use rmmod

# rmmod Hello

The another utility ‘modeprobe’ is somewhat intelligent than ‘insmod’ and is preferred one also. In case the module is using any symbol , that is not found in kernel symbol table, the ‘insmod’ will return and error. While in this case ‘modeprobe’ searches for that symbol in all the other modules that are in the current module’s search path. So, in addition to consulting kernel symbol table, ‘modeprobe’ resolves other module dependency also.

To use ‘modeprobe’ for loading a module, just use modeprobe on command prompt.

# modeprobe Hello

For unloading / removing a module from running kernel , use modeprobe r .

# modeprobe r Hello

Needless to say, all the above commands for loading / unloading modules can be given only as a ‘root’ user.

So,here ends basics of writing,compiling and loading a kernel module.

1.3 Kernel Symbol Table and exporting symbol:

The names of functions and variables are called symbols. In a compiled program , all the symbols used in progam and their addresses are kept in a Symbol table. Same is with the kernel image also. Every kernel image that you build has a symbol table with it . The kernel symbol table contains names and addresses of all the kernel symbols.

So, kernel symbol table contains all the symbols of linux kernel image. These symbols can be accessed from anywhere in the kernel space. Your module also can use those symbols. They are global symbols in nature. E.g. prinkt is one of the global kernel symbols and can be called from anywhere in the kernel space. But, on the contrary , by default , when you insert your module to the linux kernel, the symbols defined in your module will not be exposed to the rest of the kernel. They are local to your module only. This is the default behavior. However, if you wish, you can make your module’s symbols also global. For doing that you have to export those symbols. You can do that by using EXPORT_SYMBOL and EXPORT_SYMBOL_GPL macros. Once you export those symbols, they will be added to the kernel symbol table and can be accessible from anywhere in the linux kernel.

There is a slight difference between EXPORT_SYMBOL and EXPORT_SYMBOL_GPL. If you have exported symbol using EXPORT_SYMBOL , then that symbol can be used anywhere from the kernel . It will be a truly global symbol. But if you have used EXPORT_SYMBOL_GPL then only the GPL licensed kernel code can access the exported symbol. The kernel code that is not licensed under GPL, cant access this symbol. Most of the driver functions are exported using EXPORT_SYMBOL_GPL option. So, whenever you write a driver, it should be GPL licensed. That will be a great facility in case your module wants to use any other module’s functions that are already exported using GPL.

Just open a new file and write below sample code to get a feel of how module symbol is exported.

# vi export_symbol.c

#include

#include

void my_func(void);

EXPORT_SYMBOL_GPL(my_func);

int test_val=300;

void my_func()

{

printk("This is my_func()\n ");

printk("test_val = %d",test_val);

printk("End of my_func()\n ");

}

int my_init(void)

{

printk("module loaded\n ");

return 0;

}

void my_exit(void)

{

printk("module unloaded\n ");

}

module_init(my_init);

module_exit(my_exit);

MODULE_AUTHOR("LEARNING LINUX KERNEL");

MODULE_DESCRIPTION("EXPORTING SYMBOLS");

MODULE_LICENSE("GPL");

Fig. 1.7 – Exporting module symbol.

Just modify the Makefile we have seen earlier with this module name.

# vi Makefile

obj-m += export_symbol.o

KDIR= /usr/src/linux-2.6.33.1

all:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:

rm -rf *.o *.ko *.mod.* .c* .t*

Fig. 1.8 – Makefile for above example.

Just issue the make command to build export_symbol.ko kernel module.

# make

Insert this module into kernel using insmod.

#insmod module.ko

Module got loaded into the kernel. You may wish to make sure if the symbol that you exported , i.e. my_func , has become the part of kernel’s symbol table or not.

So, how to see the entries in the kernel symbol table? There are two places where you can look for the kernel symbol table.

1. /proc/ksyms

2. /boot/System.map

This System.map file is generated everytime you build the kernel. It contains all the symbol information of the build kernel.

While /proc/ksyms is a “proc file” (pseudo file) that gives the symbol infomration of the running kernel. This is a pseuo file because it’s actuallly not a file on disk, it’s just an interface to get the data from running kernel.

Now,check for entry of our exported symbol in kernel symbol table.

# cat /proc/ksyms | grep my_func

1.4 Module stacking and writing dependent modules:

We have learnt to export the kernel symbol. Once we export the symbol , it will become the part of the kernel symbol table and can be accessible anywhere within the kernel. So, we can write more modules that can directly use this exported symbol. This way you can use the services of another module or you can build a module on top of another module. This is a very useful technique and called the module stacking. Module stacking is used in writing useful drivers. For example FAT32 driver can use the services of the file system modules. FAT32 driver is build on top of the file system. Here FAT32 driver depends upon the services of the file system. So FAT32 module is a dependent module.

Usually a device driver’s functions can be splitted into various modules. Dependent module can call other module’s functions. Other modules have to export those functions that are being used by the dependent module.

In our previous we have exported my_func symbol. Let’s see an small illustration how we use my_fun exported symbol.

# vi depmod.c

#include

#include

extern void my_func(void);

static int init_module(void)

{

printk(" Calling Kernel symbol \n ");

my_func ();

return 0;

}

static void cleanup_module(void)

{

printk(" cleanup invoked \n");

}

module_init(init_module);

module_exit(cleanup_module);

/*Kernel module Comments*/

MODULE_AUTHOR("LLK");

MODULE_DESCRIPTION("MODULE STACKING");

MODULE_LICENSE("GPL");

Fig. 1.9 – Calling other module's function.

Note that we have declared this module license as GPL otherwise call to function my_func will fail.

Now, check for this module.

# lsmod

my_func 1 depend

you can see 1 module is depending on the services of this module. Now try to remove my_func module.

#rmmod my_func

ERROR: my_func is use of depmod

You cant reomove this module unless its dependency count or module usage counter is 0.

# rmmod depmod

# rmmod my_func

Now, try one thing. Remove the license in depmod , build and insert the module. Now check what happens, note down the error too.

1.5 Module Parameters:

In your user space program you might have passed different arguments to main function many times, using argc and argv. Similarly, at the time of inserting your module into the kernel, you can pass arguments to your module too.

How to do it?

1.Include a header file moduleparam.h.

#include

2.Declare the variable that you want to pass as parameter.

static int val = 0 ;

3.Use module_param(name , type, permission)

name - name of the parameter variable

type – Data type of that parameter.

permission -permission of variable (R only, R/W)

module_param(val,int,S_IRUGO);

Let’s have a look into 2nd and 3rd parameters of module_param(), i.e. type and permission arguments.

Type:

byte - 8bit

short - 16 bit signed

ushort - 16 bit unsigned

int - 32 bit signed

uint - 32 bit unsigned

long - 32 bit signed

ulong - 32 bit unsigned

charp - character pointer, used as character based string

bool - boolean type (default false)

invbool - boolean , value is inverted from what user specifies( default true )

Fig. 1.10 – Module Parameter Data Types.

Permission:

Permission argument tells about who can have access to this argument variable. It can be specified in standard octal format. For example , permission 0622 denoted that user have read/write access to this variable while group and others have read-only access. Permission can also be specified using permission macros and ORing them. The format of permission macro is, S_Ifooo, where ‘f’ will be replaced by the permission, ‘ooo‘ will be replaced by user, group or others. For example,

S_IRUGO – Read permission for user, group and others.

S_IRUGO | S_IWUSR – Read permission for user, group, others and write permission for user only.

So, basically using above three steps, you can define a parameter whose value can be passed to module in the command line, at the time of insertion or loading.

Now, let’s see a small module that defines a module parameter, and also how to pass the value to this parameter.

#vi mod_param.c

#include

#include

#include

static int param = 0;

//write for user and read only for user, group and others

module_param(param, int, S_IRUGO | S_IWUSR);

MODULE_PARM_DESC(param, "MODULE PARAMETER");

MODULE_SUPPORTED_DEVICE("NULL");

void foo(void);

void foo1(void);

void foo()

{

printk("I am in function foo \n ");

printk(" param = %d",param);

}

void foo1()

{

printk(" I am in function foo1 \n");

}

int module_init(void)

{

printk("module got inserted\n ");

foo();

return 0;

}

void module_exit(void)

{

printk("module got removed\n ");

}

module_init(module_init);

module_exit(module_exit);

MODULE_AUTHOR("LLK");

MODULE_DESCRIPTION("DEMONSTRATING MODULE PARAMETERS");

MODULE_LICENSE("GPL");

Fig. 1.11 – Parameterized module.

Configure your Makefile to accommodate this module and issue make command.

#make

Now insert this module by giving parameter value.

#insmod mod_param.ko 100

Module got inserted into the kernel . Now check the value of param variable inside the module. It should be 100 instead of the default 0.

#dmesg

So, you have seen that the module parameter is initialized at the module insertion time. In 2.6 kernel , you can change the value of this parameter later on also. This option is not available in linux kernel 2.4. Post insertion you cant change the value of a module parameter. In linux kernel 2.4 , once you have initialized a module parameter at the insertion time, you cant change its value later on.

1.6 Sysfs:

In linux kernel 2.6.x onwards,one file system called sys file system is introduced. This is similar to /proc file system but it the device information. sysfs gives only hardware information. For example, what all devices are present, what all controllers are there, what buses are there, which bus is now free / busy, which port is now free/busy etc.

It also shows information about the drivers. Because all the drivers all written in the form of modules so you can get the module information too from sysfs.

#cd /sys

#ls

You will find many folders here. Each folder shows some details of hardware. For example, firmware details, bus level details, device-driver details , power management details etc. These all hard ware details you can browse through.

Here if you look into /sys directory,you will find another directory with a name modules.

#cd module/

Inside this module you will find details of all the modules / drivers that are loaded in the system.

Remember /proc/modules will show only the list of loaded modules. It does not show any information about those modules. But sysfs interface shows all the information loaded modules.

We inserted a module with name dep_mod in previous example. So, try to find information about dep_mod. Directly inside the module directory,you will find another directory with name dep_mod.

#cd dep_mod

Inside this dep_mod , you will find another directory with name parameters.

#cd parameters

This parameters directory contains all the parameters define for the module dep_mod.

# ls

param

Remember, we defined param as one parameter in dep_mod module. This is stored as a file here. We can specify the paramter value as read-only or read-write. You can see the permissions on this accordigly using 'ls -l'.

If you have defined this parameter as read-write , then you will have read-write permissions on this file (parameter) . You can change the value of parameter from here and it will be reflected in your module.

In case if the module paameter is read-only , then you can just see the value of the parameter here but cant modify its value.

Just try to change the value of param here and check what it is in your module now. So, from 2.6.x onwards you can modify module parameter post insertion also, using sysfs. This is not available in linux kernel 2.4.x or prior to that.

That's enough on module programming. Next we shall start with basic character drivers.....