Thursday, 6 July 2017

Linux device driver loading procedure in boot up

// driver call flow
start_kernel()
   |
rest_init()
   |
kernel_thread(kernel_init, NULL, CLONE_FS);
   |
kernel_init()
   |
kernel_init_freeable()
   |
do_basic_setup()(the machine is now initialized. None of the devices have been touched yet, but the CPU subsystem is up and running, and memory and process management works.)
   |
   1.driver_init() //drivers/base/init.c (Call the driver model init functions to initialize their subsystems. Called early from init/main.c)
   2.do_initcalls() The purpose of this loop is to execute each of the init functions corresponding to each of the initcall levels (All built-in modules initialized with module_init () are represented by initcall level 6).


It depends on whether the driver is a built-in module or compiled as a loadable module. I'll be talking about a built-in module in this answer (one with y in .config):

module_init expands to  #define module_init(x)  __initcall(x);

which then expands to

    
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)             __define_initcall(fn, 6)

If your driver's initialization routine is called strtdrv then  #define device_initcall(fn) __define_initcall(fn, 6) becomes  #define device_initcall(fn) __define_initcall(strtdrv, 6)

_define_initcall expands to

    
    #define __define_initcall(fn, id) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall" #id ".init"))) = fn



which means we now have

       static initcall_t __initcall_strtdrv6 __used __attribute__((__section__(".initcall6.init"))) = strtdrv;
   
  
A new symbol is created, called __initcall_strtdrv6, which is inserted into the ELF section called .initcall6.init, which points to a routine called strtdrv.

If we take a look at init/main.c, do_basic_setup() has a call to do_initcalls().

static void __init do_initcalls(void)
{
    int level;

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}


The purpose of this loop is to execute each of the init functions corresponding to each of the initcall levels (All built-in modules initialized with module_init () are represented by initcall level 6).

We will now expand do_initcall_level(level) and focus on a particular chunk of code:

static void __init do_initcall_level(int level)
{
    /* some code */
    initcall_t *fn;
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
    do_one_initcall(*fn);
    /* some code */
}

The function pointer *fn is pointed to first function pointer registered within each of the ELF sections and is incremented by the size of fn* ( sizeof(initcall_t *)) until the end of the ELF section is reached and for each step taken, the pointer is called and the init function is executed, so in our case, do_one_initcall() will simply call the driver's initialization routine strtdrv()

    int __init_or_module do_one_initcall(initcall_t fn) {
    /* some code */
    ret = fn(); // which in our case is strtdrv(), so ret = strtdrv();
    /* some code */



and what happens next depends on the routine's code. After the initialization is done, an architecture specific function called free_initmem is called to clean up the memory pages consumed by the init functions and its data.


1 comment:


  1. When you're tired, you want to relax after a stressful working hours, you need to have time to take care of the kids active.
    Please visit our website and play exciting flash games.
    Thanks you for sharing!
    visit our website

    ReplyDelete