Linux/Android启动之Machine-Init函数


.基础知识

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

LinuxOS的启动过程中将会去创建线程kernel_init,该线程负责Driver初始化等一系列工作。线程kernel_init将会依次调用do_basic_setup() -->do_initcalls()-->do_one_initcall(),并在do_initcalls()中完成对各个驱动模块Init函数的调用。

这部分代码如下:

函数do_basic_setup()如下:

/*

*Ok,themachineisnowinitialized.Noneofthedevices

*havebeentouchedyet,buttheCPUsubsystemisupand

*running,andmemoryandprocessmanagementworks.

*

*Nowwecanfinallystartdoingsomerealwork..

*/

staticvoid__initdo_basic_setup(void)

{

rcu_init_sched();/*neededbymodule_initstage.*/

init_workqueues();

usermodehelper_init();

driver_init();

init_irq_proc();

do_initcalls();

}

函数do_initcalls()

externinitcall_t__initcall_start[],__initcall_end[],__early_initcall_end[];

staticvoid__initdo_initcalls(void)

{

initcall_t*call;

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

do_one_initcall(*call);

/*Makesurethereisnopendingstufffromtheinitcallsequence*/

flush_scheduled_work();

}

函数do_one_initcall(initcall_tfn)如下:

intinitcall_debug;

core_param(initcall_debug,initcall_debug,bool,0644);

intdo_one_initcall(initcall_tfn)

{

intcount=preempt_count();

ktime_tcalltime,delta,rettime;

charmsgbuf[64];

structboot_trace_callcall;

structboot_trace_retret;

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=(unsignedlonglong)ktime_to_ns(delta)>>10;

trace_boot_ret(&ret,fn);

printk("initcall%pFreturned%dafter%Ldusecs/n",fn,

ret.result,ret.duration);

}

msgbuf[0]=0;

if(ret.result&&ret.result!=-ENODEV&&initcall_debug)

sprintf(msgbuf,"errorcode%d",ret.result);

if(preempt_count()!=count){

strlcat(msgbuf,"preemptionimbalance",sizeof(msgbuf));

preempt_count()=count;

}

if(irqs_disabled()){

strlcat(msgbuf,"disabledinterrupts",sizeof(msgbuf));

local_irq_enable();

}

if(msgbuf[0]){

printk("initcall%pFreturnedwith%s/n",fn,msgbuf);

}

returnret.result;

}

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

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

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

1.重要结构体machine_desc说明

structmachine_desc描述了机器(machine,也就是目标板)对内核初始阶段资源分配至关重要的一些参数。

首先,来看一下结构体machine_des的内容(在arch/arm/include/asm/mach/arch.h):

structmachine_desc{

/*

*Note!Thefirstfourelementsareused

*byassemblercodeinhead.S,head-common.S

*/

unsignedintnr;/*architecturenumber,记录体系结构*/

unsignedintphys_io;/*startofphysicalio*/

unsignedintio_pg_offst;/*byteoffsetforio

*pagetabeentry*/

constchar*name;/*architecturename,体系结构名字*/

unsignedlongboot_params;/*taggedlist*/

unsignedintvideo_start;/*startofvideoRAM*/

unsignedintvideo_end;/*endofvideoRAM*/

unsignedintreserve_lp0:1;/*neverhaslp0*/

unsignedintreserve_lp1:1;/*neverhaslp1*/

unsignedintreserve_lp2:1;/*neverhaslp2*/

unsignedintsoft_reboot:1;/*softreboot*/

void(*fixup)(structmachine_desc*,

structtag*,char**,

structmeminfo*);

void(*map_io)(void);/*IOmappingfunction*/

void(*init_irq)(void);

structsys_timer*timer;/*systemticktimer*/

void(*init_machine)(void);

};

其中,结构体的最后一个函数指针init_machine会被初始化为Machine-Init的地址。系统中针对每一种CPU都会去定义一个该结构体变量,并在系统的启动过程中进行引用。

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

这里以SamsungS3C6410的开发板的BSP为例来进行分析。

SamsungS3C6410machine_des在文件arch/arm/mach-s3c6410/mach-s3c6410.c中定义,定义方式如下:

MACHINE_START(SMDK6410,"SMDK6410")

/*Maintainer:BenDooks<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,

#ifndefCONFIG_HIGH_RES_TIMERS

.timer=&s3c64xx_timer,

#else

.timer=&sec_timer,

#endif/*CONFIG_HIGH_RES_TIMERS*/

MACHINE_END

Linux中针对各个不同的CPU存在很多个类似于上面的定义,宏定义MACHINE_START的定义如下:

/*

*Setofmacrostodefinearchitecturefeatures.Thisisbuiltinto

*atablebythelinker.

*/

#defineMACHINE_START(_type,_name)/

staticconststructmachine_desc__mach_desc_##_type/

__used/

__attribute__((__section__(".arch.info.init")))={/

.nr=MACH_TYPE_##_type,/

.name=_name,

#defineMACHINE_END/

};

其实,宏定义替换后的就变成了如下的定义方式:

staticconststructmachine_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,

#ifndefCONFIG_HIGH_RES_TIMERS

.timer=&s3c64xx_timer,

#else

.timer=&sec_timer,

};

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

函数setup_machine实现对结构体machine_des位置的判别,其代码代码如下:

staticstructmachine_desc*__initsetup_machine(unsignedintnr)

{

structmachine_desc*list;

/*

*locatemachineinthelistofsupportedmachines.可能支持多个cpu哦

*/

list=lookup_machine_type(nr);

if(!list){

printk("Machineconfigurationbotched(nr%d),unable"

"tocontinue./n",nr);

while(1);

}

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

returnlist;

}

上面红色标记的函数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函数

staticvoid(*init_machine)(void)__initdata;

staticint__initcustomize_machine(void)

{

/*customizesplatformdevices,oraddsnewones*/

if(init_machine)

init_machine();

return0;

}

arch_initcall(customize_machine);

可能不太熟悉驱动优先级的人会觉得,这里怎么使用arch_initcall导出,而不是通常的module_init方式进行导出。

Linux中,没有办法像CE/Mobile中通过注册表的方式来定义驱动的优先级,只有通过导出函数的LevelMakefile中模块驱动书写的先后顺序来定义。

关于导出函数Level的代码如下:

#define__define_initcall(level,fn)staticinitcall_t__initcall_##fn__attribute_used____attribute__((__section__(".initcall"level".init")))=fn

#definecore_initcall(fn)__define_initcall("1",fn)

#definepostcore_initcall(fn)__define_initcall("2",fn)

#definearch_initcall(fn)__define_initcall("3",fn)

#definesubsys_initcall(fn)__define_initcall("4",fn)

#definefs_initcall(fn)__define_initcall("5",fn)

#definedevice_initcall(fn)__define_initcall("6",fn)

#definelate_initcall(fn)__define_initcall("7",fn)

#define__initcall(fn)device_initcall(fn)

#define__exitcall(fn)staticexitcall_t__exitcall_##fn__exit_call=fn

#definemodule_init(x)__initcall(x);

#definemodule_exit(x)__exitcall(x);

可以看到,通常驱动中采用的module_init优先级为6,也即最低优先级,而前面采用的arch_initcall优先级为最高优先级1


更多相关文章

  1. Android调整TimePicker和DatePicker大小
  2. android_定义多个Activity及跳转
  3. android dataBinding详解
  4. Android(安卓)自定义打印log
  5. Java层Binder使用(ServiceManager)
  6. 【转】高通平台android 环境配置编译及开发经验总结
  7. Android(安卓)自定义View之自定义评分选择器RatingBar
  8. Android开发工具——ADB(Android(安卓)Debug Bridge) HOST端
  9. Android(安卓)Intent原理分析

随机推荐

  1. Android性能优化(一)App启动原理分析及启动
  2. Android(1)--Application和Activity
  3. Android——简介
  4. [android] 百度地图开发 (一).申请AK显示
  5. Android(安卓)VNDK简介
  6. 【小超_Android】2015最流行的android组
  7. [置顶] SQLITE在ANDROID上的一个BUG
  8. How C/C++ Debugging Works on Android
  9. 【Android动态布局】之【使用addView方法
  10. Android FragmentManage FragmentTransac