Wednesday, September 16, 2009

Kernel Module writing with examples

Kernel Module writing with examples
introduction :

Hello all , as we all know linux is a BIG do it your self program
it allow you to do EVERY THING on your system
all what you have to do is to learn how to use it's capabilities

WE are going to deal with kernel by modules right now , but what is module ?
it's like a small kernel application (not user application) that can be loaded into kernel at run time whenever you need it by insmod , modeprobe
that is a good advantage actually , you don't need to compile kernel statically every time you need a module and that will cause you to reboot your system , imagine how could this be a west of time


but why that ([color="#0000FF"]kernel application[/color]) what is the difference between it and user application


[color="#FF0000"]user application:[/color][/i]
as we all know user application (ordinary programs) start execution from [color="#0000FF"]main()[/color] function
continue execution of this function until it satisfy it's job then exit
you can use standard libraries and user libraries


[color="#0000FF"]kernel applications[/color] : modules
piece of code that will be compiled against kernel objects producing a kernel module
that can be linked dynamically and removed dynamically at run time without a need to reboot

kernel modules doesn't start execution like ordinary programs from main()
However , it starts execution from function we call (initialization function) which you will code
module end when you request it to be removed but before kernel unload it . it will call a function we call (cleaning up function) and clean up all it's job.
calling this functions will depend on you because you will specify which function is the init and which function is the cleanup (later);
when a module is loaded kernel get the address of the init function and run it , then it hangs on the kernel until some application requires a symbol that this module exported ;
NOTE : if kernel is suspicious about module job (still hanging around with a file descriptor or what ever) it will not unload it until it finishes it's job .
unless you specified `MODULE_FORCE_UNLOAD` in your kernel configuration

modules doesn't hang in memory like ordinary applications might does . kernel handles it memory and kernel frees it's memory;

any fault at modules can cause a BIG something if you are not careful.....
SIGSEGV in ordinary app will cause a core dumb and exit :D
but in kernel it might be a disaster...

you can't use library function in modules (NO linking to libc or any other library)
modules are objects files . they only able to deal with internal kernel functions ("/proc/kallsyms")
which we can call SYSTEM CALLS .
system calls are defined in kernel which load the modules and resolve it's referencing
at insmod"ing time ;
in a kernel space you can do anything you want :) you are dealing with kernel;

you MUST have a kernel source tree to be able to compile your module
you will NOT be able to do anything without a kernel source
a kernel module runs on kernel 2.6 will definitely not able to run on 2.4
a compiled module runs on 2.6.X might not run in 2.6.D



++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++
[color="#0000FF"]your First kernel module :[/color]

get a shell and pick and editor and start typing :
Code: [Select]
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>

static char *name = "Kernel Hacking first module\n";


module_param(name , charp , S_IRUGO | S_IWUSR);

static int __init  module_start(void)
{
    printk(KERN_INFO"Hello This IS a kernel module testing\n");
      printk(KERN_INFO"Information : %s\n", name);
        printk(KERN_INFO"Information Delevered\n");
          return 0;
}

static void __exit  module_end(void)
{
    printk(KERN_INFO"\nEnd And\n\n");
      printk(KERN_INFO"Every Thing Has a beginning has an end");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("St0rM");
MODULE_DESCRIPTION("Something Nice we do in here");

module_init(module_start);
module_exit(module_end);

after saving It

Quote
make -C /lib/modules/`uname -r`/build M=`pwd`


let's start explaining what is this
Code: [Select]
#include <linux/init.h>
#include <linux/module.h>

essential headers to make module able to load
Quote
#include <linux/moduleparam.h> --> allow module to receive parameters while loading
#include <linux/stat.h> --> contain definition of permission flags like S_IRDWR by it you can specify which user can read/write/read&write on
parameters

+++++++++++++++++++++++
initialization :
+++++++++++++++++++++++
static int __init module_start(void);
this function will be passed to module_init why ? because module_init specify that this is the function should be called when module is loaded
which will point in module(object file) that this is the function should be called whenever this module is loaded


++++++++++++++++++++++
exiting: cleaning up
++++++++++++++++++++++
static void module_end(void);

will be passed to module_exit(); same as module_init() but it does the opposite it points to cleanup = exit function;

passing parameters :
Code: [Select]
module_param(name , charp , S_IRUGO | S_IWUSR);

this function allow you to pass parameters to the module at loading time by
insmod module.kp name="Bleh"

specify variablename=value
Quote
module_param(variable name . variable type , permissions);
charp = character pointer ;

you can't pass a float point variable;

types:
Quote
char
charp = character pointer
int
long
short
ushort = unsigned shor
ulong = unsigned long

also you can pass arrays
like
Quote
insmode module.ko array=x,v,d,g

each value separated by (,)

but you will have to change it like

Code: [Select]
int x[5];
module_param_array(x , int , 5 , permissions)

++++++++++++++
[color="#0000FF"]printk: your first kernel hacking[/color]
++++++++++++++
what the hell is printk ? where is f ? did the grinch stole it :@ what a  bastard :@
no he didn't , I told you before that you can't use library function
printk is the kernel version of printf function which is a system call allow you to print format messages .it prints by priority by the way , and that is [color="#0000FF"]KERN_INFO[/color] is it's a priority every string is printed by this
Code: [Select]
MODULE_LICENSE("GPL");
MODULE_AUTHOR("St0rM");
MODULE_DESCRIPTION("Something Nice we do in here");

information about module as you can see :D


no what is that make command ?
as I told you before modules compiled against several objects files in the kernel source tree
as an example : magicversion.o this object file contain information about the module
date , version , which kernel it was compiled on , etc

make -C |link source tree| enter that folder and uses it's object files
M=`pwd` this argument says that files in here should be compiled to objects
then against Linux objects the modul

moudles : tell the make command that output will be a module not an ordinary object file

how to load it then ?

Quote
insmod module.ko |arguments if any|

or better :
modprobe module.ko
it's better because it solves the references by this module and catches any undefined symbol in kernel . if any it checks for it's standard moudles directory and load any depends required by insmod them

how to unload it then ?

Quote
rmmod module.ko

how list running modules ?

Quote
lsmod

files ?
Quote
/proc/modules

cd /sys/module -> directory contain directories about the running modules


Examples :
Code: [Select]
root@St0rM-Man:~/Desktop/linux.programming/kernel/pipe# insmod pipe_char.ko major=100 minor=0
root@St0rM-Man:~/Desktop/linux.programming/kernel/pipe# mknode /dev/pipe_char c 100 0
-bash: mknode: command not found
root@St0rM-Man:~/Desktop/linux.programming/kernel/pipe# mknod /dev/pipe_char c 100 0
root@St0rM-Man:~/Desktop/linux.programming/kernel/pipe# echo "It's only after we have lost every thing that we are free to do anything" > /dev/pipe_char
root@St0rM-Man:~/Desktop/linux.programming/kernel/pipe# cat /dev/pipe_char
It's only after we have lost every thing that we are free to do anything

Code: [Select]
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

#include <linux/completion.h>

static int major;
static int minor;
static dev_t device_node;

static char buffer[265];

module_param(major , int , S_IWUSR | S_IRUSR);
module_param(minor , int , S_IWUSR | S_IRUSR);

struct cdev device;
struct completion complete_task;

static int pipe_open(struct inode *node , struct file *filp)
{
  /* No defice specification Will be Done */

  return 0;
}
static int pipe_read(struct file *filp , char __user *buf , size_t size , loff_t *pos)
{

  wait_for_completion(&complete_task);

  if(copy_to_user(buf , buffer , strlen(buffer)) > 0)
    return -1;
  else
    return strlen(buffer);
}
static int pipe_write(struct file *filp ,
    const char __user *buf ,
    size_t size ,
    loff_t *pos)
{
  int count = size;

  if(count > 264)
    count = 264;

  if(copy_from_user(buffer , buf , count) > 0)
    return -1;
  complete(&complete_task);
  return count;
}

static int pipe_release(struct inode *node , struct file *filp)
{
  return 0;
}

struct file_operations fops =
{
  .open = pipe_open ,
  .read = pipe_read ,
  .write = pipe_write ,
  .release = pipe_release,
};

static int __init pipe_init(void)
{
  int result;

  if(major)
  {
    device_node = MKDEV(major , minor);

    result = register_chrdev_region(device_node , 1 , "pipe");
  }else
  {
    result = alloc_chrdev_region(&device_node , 1 , 1 , "pipe");
  }

  if(result == -1)
  {
    printk(KERN_INFO"Failed To Register Device Number\n\n");
    return result;
  }

  printk(KERN_INFO
      "Registred with major:minor %d:%d\n",
      MAJOR(device_node) , MINOR(device_node));

  cdev_init(&device , &fops);
  device.owner = THIS_MODULE;

  result = cdev_add(&device , device_node , 1);
  if(result == -1)
  {
    printk(KERN_INFO"Failed To register Char device\n\n");
    return result;
  }

  printk(KERN_INFO"Device Is live\n\n");

  /* initialize Complete Structure */
  init_completion(&complete_task);
  return 0;

}


static void __exit pipe_exit(void)
{
  unregister_chrdev_region(device_node , 1);
  cdev_del(&device);

  printk(KERN_INFO"Device Is Removed\n\n");

}
module_init(pipe_init);
module_exit(pipe_exit);

Use it in a kernel application if you want , using seq_file

Code: [Select]
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/errno.h>

/* itmes will be sequenced and sended to user interface using the lovely seq_file
 * interface; items will be initialized ar initializing modules time
 * any fauilr will result in whole module failur and unload immediatly 
 */

/* items hold data */
static struct items
{
 struct items *next;
 char *buffer;
}*head , *cur;


/* open seqeunce proto type function */
static int   open_seq(struct inode *inode , struct file *filp);
/*iterator prototypes */
static void *start_seq(struct seq_file *s , loff_t *pos);
static void *next_seq(struct seq_file *s , void *data , loff_t *pos);
static void stop_seq(struct seq_file *s , void *data );
static int show_seq(struct seq_file *s , void *data);

/* Mark eof */
static int eof = 0;
/* erasing list function */

static void erase(struct items *head);

/* Seq operations */
static struct seq_operations seq_ops = {
 .start = start_seq,
 .next = next_seq,
 .stop = stop_seq,
 .show = show_seq
};

/* File operations */

struct file_operations fops = 
{
 .owner = THIS_MODULE,
 .open = open_seq ,
 .read = seq_read,
 .release = seq_release,
 .llseek = seq_lseek
};


/* Module initializiation function definition */

static int __init proc_seq_init(void)
{
 /* We need to allocate 5 items for items structure and fill buffers in them */
 int i;
 struct proc_dir_entry *proc_entry;

 head = NULL;
 cur = NULL;

 for(i=0; i <= 5; i++)
 {
  if(head == NULL) /*first */
  {
   head = kmalloc(sizeof(struct items) , GFP_KERNEL);
   head->buffer = kmalloc(50 *sizeof(char), GFP_KERNEL);
   
   if( !head ||!head->buffer)
   {
    if(head)
    {
     erase(head);
     printk(KERN_INFO"allocating memory error\n");
    }
    return -ENOMEM;
   }
   cur = head;
   cur->next = NULL;
  }else if(!cur->next) /* other */
  {
   cur->next = kmalloc(sizeof(struct items)  , GFP_KERNEL);
   cur = cur->next;
   if(cur)
   {
    cur->buffer = kmalloc(50 *sizeof(char), GFP_KERNEL);
    if(!cur->buffer)
    {
     erase(head); /* the only reason i did this cause
       memory is not enough in this case
       and i don't want somebody's system
       to hang 
       */
     printk(KERN_INFO"allocating memory error\n");
     return -ENOMEM;
    }
    cur->next = NULL;
   }else
   {
    erase(head);
    return -ENOMEM;
   }
     
  }
   /* fill the buffer with information */
   sprintf(cur->buffer , "Ok this is the %d time\n",i);

 }
 /* initializing items is done at this case we should now register our proc_file */
 /*using low level creat_proc_enrty cause we don't want to have a read only proc 
  * we are not implementing ordinary proc file we are using seq_file proc technique  */

 proc_entry = create_proc_entry("linked_info" , S_IRUSR | S_IROTH  , NULL /* parent  */); 
 if(proc_entry)
  proc_entry->proc_fops = &fops;
 else
  return -1;

 printk(KERN_INFO"All seems to be fine\n\n");
 return 0;
}

    
static void __exit proc_seq_clean(void)
{
 /* erase list */
 erase(head);
 /* remove file */
 remove_proc_entry("linked_info" , NULL);
}


module_init(proc_seq_init);
module_exit(proc_seq_clean);
/* open_seq definition */

static int open_seq(struct inode *inode , struct file *filp)
{
 /* all we want to do is register sequnece operation with file */
 eof = 0;
 return seq_open(filp , &seq_ops);
}

/* iterator functions definitions */
static void *start_seq(struct  seq_file *s , loff_t *pos)
{
 /* actually it will do nothing accept updating position  */
 *pos += sizeof(struct items) +( 50 *sizeof(char));
 if(head->next)
  return head->next;
 else 
  return NULL;
};

static void *next_seq(struct seq_file *s , void *data , loff_t *pos)
{
 struct items *item;
 /* update postion and move on sequence list */
 
 item = (struct items*) data;

 if(item->next)
 {
  return item->next;
 }
 else 
  return NULL;
}

static void stop_seq(struct seq_file *s , void *data )
{
;
}

static int  show_seq(struct seq_file *s , void *data)
{
 struct items *item;
 int result;
 
 item = (struct items *) data;
 if(eof == 1)
 {
  return 0;
 }
 result = seq_printf( s , item->buffer );
 if(item->next == NULL)
  eof = 1;

 return result;
}

/* erasing function definition */

static void erase(struct items *item)
{
 struct items *temp;
 
 if(!item)
  return;

 while(item)
 {
  temp=item->next;
  kfree(item);
  item = temp;
 }
}
MODULE_LICENSE("Gpl");
MODULE_AUTHOR("Ahmed Mosatfa");
MODULE_DESCRIPTION("/proc file system module");
MODULE_VERSION("0.0.1");

No comments:

Post a Comment