您的位置:首页 > 运维架构 > Linux

Linux/Android启动 之 (module_init和machine-init函数)

2012-02-17 10:25 302 查看

Linux/Android启动之Machine-Init函数



一. 基础知识

1. Linux启动过程中驱动模块初始化的位置

Linux OS的启动过程中将会去创建线程kernel_init,该线程负责Driver初始化等一系列工作。线程kernel_init将会依次调用do_basic_setup()
-->do_initcalls()-->do_one_initcall(),并在do_initcalls()中完成对各个驱动模块Init函数的调用。
这部分代码如下:
函数do_basic_setup()如下:
/*
* Ok, 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.
*
* Now we can finally start doing some real work..
*/
static void __init do_basic_setup(void)
{
rcu_init_sched(); /* needed by module_init stage. */
init_workqueues();
usermodehelper_init();
driver_init();
init_irq_proc();
do_initcalls();
}
函数do_initcalls():
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
static void __init do_initcalls(void)
{
initcall_t *call;

for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call);

/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
函数do_one_initcall(initcall_t fn)如下:
int initcall_debug;
core_param(initcall_debug, initcall_debug, bool, 0644);

int do_one_initcall(initcall_t fn)
{
int count = preempt_count();
ktime_t calltime, delta, rettime;
char msgbuf[64];
struct boot_trace_call call;
struct boot_trace_ret ret;

if (initcall_debug) {
call.caller = task_pid_nr(current);
printk("calling %pF @ %i/n", fn, call.caller);
calltime = ktime_get();
trace_boot_call(&call, fn);
enable_boot_trace();
}

ret.result = fn();

if (initcall_debug) {
disable_boot_trace();
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
ret.duration = (unsigned long long) ktime_to_ns(delta) >> 10;
trace_boot_ret(&ret, fn);
printk("initcall %pF returned %d after %Ld usecs/n", fn,
ret.result, ret.duration);
}

msgbuf[0] = 0;

if (ret.result && ret.result != -ENODEV && initcall_debug)
sprintf(msgbuf, "error code %d ", ret.result);

if (preempt_count() != count) {
strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));
preempt_count() = count;
}
if (irqs_disabled()) {
strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
local_irq_enable();
}
if (msgbuf[0]) {
printk("initcall %pF returned with %s/n", fn, msgbuf);
}

return ret.result;
}

二.Machine-Init函数被调用的位置

Machine-Init也将在函数do_one_initcall(initcall_t fn)中伴随其它的模块Init函数一起被调用,不同之处在于Machine-Init的优先级最高。

三.Machine-Init函数相关解析过程

1. 重要结构体machine_desc说明

struct machine_desc描述了机器(machine, 也就是目标板)对内核初始阶段资源分配至关重要的一些参数。
首先,来看一下结构体machine_des的内容(在arch/arm/include/asm/mach/arch.h):
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned intnr;/* architecture number,记录体系结构*/
unsigned intphys_io;/* start of physical io*/
unsigned intio_pg_offst;/* byte offset for io
* page tabe entry*/

const char*name;/* architecture name,体系结构名字*/
unsigned longboot_params;/* tagged list*/

unsigned intvideo_start;/* start of video RAM*/
unsigned intvideo_end;/* end of video RAM*/

unsigned intreserve_lp0 :1;/* never has lp0*/
unsigned intreserve_lp1 :1;/* never has lp1*/
unsigned intreserve_lp2 :1;/* never has lp2*/
unsigned intsoft_reboot :1;/* soft reboot*/
void(*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void(*map_io)(void);/* IO mapping function*/
void(*init_irq)(void);
struct sys_timer*timer;/* system tick timer*/
void(*init_machine)(void);
};
其中,结构体的最后一个函数指针init_machine会被初始化为Machine-Init的地址。系统中针对每一种CPU都会去定义一个该结构体变量,并在系统的启动过程中进行引用。

2. 对应当前开发板的结构体machine_des的初始化

这里以Samsung S3C6410的开发板的BSP为例来进行分析。
Samsung S3C6410的machine_des在文件arch/arm/mach-s3c6410/mach-s3c6410.c中定义,定义方式如下:
MACHINE_START(SMDK6410, "SMDK6410")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io= S3C_PA_UART & 0xfff00000,
.io_pg_offst= (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params= S3C64XX_PA_SDRAM + 0x100,
.fixup= smdk6410_fixup,
.init_irq= s3c6410_init_irq,
.map_io= smdk6410_map_io,
.init_machine= smdk6410_machine_init,
#ifndef CONFIG_HIGH_RES_TIMERS
.timer= &s3c64xx_timer,
#else
.timer= &sec_timer,
#endif /* CONFIG_HIGH_RES_TIMERS */

MACHINE_END
Linux中针对各个不同的CPU存在很多个类似于上面的定义,宏定义MACHINE_START的定义如下:
/*
* Set of macros to define architecture features. This is built into
* a table by the linker.
*/
#define MACHINE_START(_type,_name)/
static const struct machine_desc __mach_desc_##_type/
__used/
__attribute__((__section__(".arch.info.init"))) = {/
.nr= MACH_TYPE_##_type,/
.name= _name,

#define MACHINE_END/
};
其实,宏定义替换后的就变成了如下的定义方式:
static const struct machine_desc__SMDK6410
__used
__attribute__((__section__(".arch.info.init"))) = { /*__section__指定了该结构体被链接的位置*/

.nr = MACH_TYPE_SMDK6410,
.name = "SMDK6410",
phys_io= S3C_PA_UART & 0xfff00000,
.io_pg_offst= (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params= S3C64XX_PA_SDRAM + 0x100,
.fixup= smdk6410_fixup,
.init_irq= s3c6410_init_irq,
.map_io= smdk6410_map_io,
.init_machine= smdk6410_machine_init,
#ifndef CONFIG_HIGH_RES_TIMERS
.timer= &s3c64xx_timer,
#else
.timer= &sec_timer,
};

3. 函数setup_machine实现对结构体machine_des的定位

函数setup_machine实现对结构体machine_des位置的判别,其代码代码如下:
static struct machine_desc * __init setup_machine(unsigned int nr)
{
struct machine_desc *list;

/*
* locate machine in the list of supported machines.可能支持多个cpu哦
*/
list = lookup_machine_type(nr);
if (!list) {
printk("Machine configuration botched (nr %d), unable "
"to continue./n", nr);
while (1);
}

printk("Machine: %s/n", list->name);

return list;
}
上面红色标记的函数lookup_machine_type(nr)用汇编实现在文件head.S中,用来查找对应当前设备的machine_desc变量位置,并通过函数lookup_machine_type指向其起始位置,后续就可以直接通过该返回值来对结构体machine_desc变量machine_desc__SMDK6410的值进行读取,如后面读取Machine-Init函数的指针的操作init_machine = mdesc->init_machine。
而函数setup_machine被调用的过程如下(start_kernelàsetup_archàsetup_machine):





在函数setup_machine最后会在全局指针变量init_machine中记录Machine-Init函数的信息。

4. Machine-Init的导出

在文件arch/arm/kernel/setup.c中通过如下的方式将Machine-Init设置为模块的Init函数:
static void (*init_machine)(void) __initdata;

static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine);
可能不太熟悉驱动优先级的人会觉得,这里怎么使用arch_initcall导出,而不是通常的module_init方式进行导出。
在Linux中,没有办法像CE/Mobile中通过注册表的方式来定义驱动的优先级,只有通过导出函数的Level和Makefile中模块驱动书写的先后顺序来定义。
关于导出函数Level的代码如下:
#define __define_initcall(level,fn) static initcall_t __initcall_##fn __attribute_used__ __attribute__((__section__(".initcall" level ".init"))) = fn

#define core_initcall(fn) __define_initcall("1",fn)
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
#define subsys_initcall(fn) __define_initcall("4",fn)
#define fs_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)

#define __initcall(fn) device_initcall(fn)

#define __exitcall(fn) static exitcall_t __exitcall_##fn __exit_call = fn

#define module_init(x) __initcall(x);

#define module_exit(x) __exitcall(x);
可以看到,通常驱动中采用的module_init优先级为6,也即最低优先级,而前面采用的arch_initcall优先级为最高优先级1。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: