Wednesday, April 2, 2014

Simple Kernel Module (or) Simple Device Driver

What is kernel module?

Kernel module is a simple C program, unlike user space program. This program or module can be loaded or unloaded from the kernel based on demand. The linux kernel has many modules, we call this module as a driver.


Why we need kernel module?
         
          The purpose of the kernel module is to access the hardware, for example we have temp sensor in our embedded system, if you want to read the temperature, we can write our own module to read it.

Kernel module vs User space program

          The system memory is divided into two

·        Kernel space
·        User space

Most of the embedded system (32 bit), the memory size is 4GB (3GB is user space and 1 GB to kernel space).

          We should handle the kernel memory very carefully, just a null pointer in kernel module will lead the system to panic (reboot).  

             The user space program runs in userspace and kernel module runs in kernel space.


Simple user space program
test.c

#include <stdio.h>
int main(void)
{
printf("\n Hello world \n");
return 0;
}

How do I compile this program?
gcc –o test test.c
How do I execute this program?
./test
Output:
Hello world

                Fine, I have few questions on above program.

·        why will we get always a.out on gcc test.c? Why don’t we get b.out or c.out?
              
               a.out is the default file format given by GCC, a.out stands for assembler output according Dennis Ritchie. So if you want to give a fancy name then –o can be used.


·        Do we have any other compiler to compile other than gcc?
              
               Yes. We have cc. both gcc and cc are having same cksum, basically cc is the softlink to gcc.

·        What is cross compile?

               We have many embedded system architecture, examples x86 aka intel, ppc aka powerpc, arm, xtensa, etc. our PC or laptop is x86 architecture, the gcc, compiles the code for x86 and it will run only in our PC. You can’t run this on any other arch. (./a.out will not work in any embedded board)

              if you have embedded board with ppc arch. Then you need to compile the program with ppc-gcc, and you can run it in the ppc embedded board. This is called cross compile. Ok. How to do it.? let’s see.

·        Can I know, for what arch my a.out compiled?

              Yes, you can.

     Example:
     gcc test.c    ---- this compiles the test.c and gives a.out
    
file a.out           --- file command will tell you the arch. I compiled for x86.

a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

powerpc-linux-gcc test.c     ---- this compiles the test.c and gives a.out, for ppc arch.

file a.out

a.out: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.4, not stripped

·        Where did you get powerpc-linux-gcc?
    

              Well, that’s another story, to get a cross compile tool chain in our case ppc, we need to build it from the source code. I will explain this in later post.


Simple Kernel module
       Let’s move on to writing a simple “hello_word” module. The kernel module consists of two default functions, like main() in user space application. To load the kernel module, the linux running in our system must have below config. In our PC, it always set.
Loadable module support  --->
  [*] Enable loadable module support
  [*]   Module unloading
  [ ]   Module versioning support (EXPERIMENTAL)
  [*]   Automatic kernel module loading   

Here is code:
/* 
 *  hello-1.c - The simplest kernel module.
 */
#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 1.\n");

        /*
         * A non 0 return means init_module failed; module can't be loaded.
         */
        return 0;
}

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

Can I have any one function init or exit?
       No, you can’t, the kernel module must have both.

When will be these two function invoked?
       As we all know, in user space program the main() will be called at first, when you execute it.
       In the kernel module, the init_module will be called first during the insertion, and the cleanup_module will be called during the removal.

What you mean by insert and remove?
       Let’s see this bit later.

Can I have my own name for init and exit?
       Yes, you can, from kernel 2.3.31, version we can have our own name for these functions, let’s see this later.

How do I compile this kernel module gcc file.c?
       No, you can’t compile the kernel module, just like gcc file.c. there is a way to compile it. We need to compile it using Makefile.
What is Makefile?
       Makefile is the file, that contains set of unix commands, by giving make, the file will be excuted.
Simple Makefile.
obj-m := test_module.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

What is printk?
       Printk is the similar function like printf in user space program. But printk does not only printing the data in the console, also it logs into buffer based on the log priority.
They are:
#define KERN_EMERG      "<0>"   /* system is unusable                   */
#define KERN_ALERT      "<1>"   /* action must be taken immediately     */
#define KERN_CRIT       "<2>"   /* critical conditions                  */
#define KERN_ERR        "<3>"   /* error conditions                     */
#define KERN_WARNING    "<4>"   /* warning conditions                   */
#define KERN_NOTICE     "<5>"   /* normal but significant condition     */
#define KERN_INFO       "<6>"   /* informational                        */
#define KERN_DEBUG      "<7>"   /* debug-level messages                 */

Example:
       Printk(KERN_ERR, “hello”);
       This will be printed in console as well as stored in dmesg buffer. So when you need later, just giving by demsg you will able to see it.
       Ok, back to qus how do it compile it.

Keep the makefile and the test_module.c in a same dierctroy.
$mkdir test
$cd test
$cp /Makefile .
$cp /test_module.c .
$make
$ls
You would see test_module.ko
$insmod test_module.ko
Hello world 1.
$rmmod test_module.ko
Goodbye world 1.
       Insmod and rmmod is the utility available in the linux to insert and remove the kernel module.
I hope I answered for insert remove questions

Is this the only way to load the module?
       No, there are two ways static loading and dynamic loading, the above one is the static load, means that we load the module after the kernel booted.

What is dynamic loading?
       In dynamic loading, we build the module with kernel image,

How to do it?
       Go to /linux/driver/char path.
Make a entry in kconfig and makefile, just refer any other module. Then set the defconfig in .config


 Insmod is the only way to insert?
       No, we have other way to insert, that is called modprobe.

Insmod vs modprobe?
       Insmode basically load the particular module, you have given, whereas the modprobe loads the dependent module also. 

How does insmod work?
                Insmod install the loadable module in the running kernel, insmod tries to link a module into running kernel by resolving all symbols from the kernel's exported. If the module file name is given without directories or extension, insmod will search for the module in some common default directories.  “/etc/modules.conf”

How does modprobe work?
                Modprobe  does similar work like insmod. But the modprobe loads the dependent module also.

Ex:
a.ko depended on b.ko and c.ko in the same directory, and c.ko depended on b.ko as well, then modprobe a.ko, loads all the module.
modprobe find the dependented module from the below file

"modules.dep"


No comments: