Linux-Android启动之Machine-Init函数

Linux/Android启动之Machine-Init函数

 

前言

前面写过两篇Linux/Android的启动过程分析,这篇接着前两篇的知识点进行分析。

Linux/Android的启动过程包括了很多内容,其中有些需要了解,有些则需要在系统移植的时候进行修改。本篇文章主要来讲述Machine-Init函数在系统启动过程中如何被调用的以及在何时被调用。

Linux中的Machine-Init在功能和调用位置上类似于Win CE/ Windows Mobile中的OAL初始化函数OEMInit

哈哈。如果有不正确或者不完善的地方,欢迎前来拍砖留言或者发邮件到guopeixin@126.com进行讨论,先行谢过。

 

基础知识

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_endcall < __initcall_endcall++)

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_debuginitcall_debugbool, 0644);

 

int do_one_initcall(initcall_t fn)

{

int count = preempt_count();

ktime_t calltimedeltarettime;

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"fncall.caller);

calltime = ktime_get();

trace_boot_call(&callfn);

enable_boot_trace();

}

 

ret.result = fn();

 

if (initcall_debug) {

disable_boot_trace();

rettime = ktime_get();

delta = ktime_sub(rettimecalltime);

ret.duration = (unsigned long longktime_to_ns(delta) >> 10;

trace_boot_ret(&retfn);

printk("initcall %pF returned %d after %Ld usecs/n"fn,

ret.resultret.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"fnmsgbuf);

}

 

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 int nr; /* architecture number,记录体系结构 */

unsigned int phys_io; /* start of physical io */

unsigned int io_pg_offst; /* byte offset for io 

* page tabe entry */

 

const char *name; /* architecture name,体系结构名字 */

unsigned long boot_params; /* tagged list */

 

unsigned int video_start; /* start of video RAM */

unsigned int video_end; /* end of video RAM */

 

unsigned int reserve_lp0 :1; /* never has lp0 */

unsigned int reserve_lp1 :1; /* never has lp1 */

unsigned int reserve_lp2 :1; /* never has lp2 */

unsigned int soft_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 S3C6410machine_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中通过注册表的方式来定义驱动的优先级,只有通过导出函数的LevelMakefile中模块驱动书写的先后顺序来定义。

关于导出函数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

 

                                                                    (完)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/243129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

边沿检测—以脉冲形式给出信号

边沿检测—以脉冲形式给出信号 题目描述&#xff1a; 有一个缓慢变化的1bit信号a&#xff0c;编写一个程序检测a信号的上升沿给出指示信号rise&#xff0c;当a信号出现下降沿时给出指示信号down。 注&#xff1a;rise,down应为单脉冲信号&#xff0c;在相应边沿出现时的下一个…

贷款秒拒?你可能进了“灰名单”!

灰名单/GRAY LIST今天有客户向我反映&#xff0c;他的征信良好没有半点问题&#xff0c;只有一张信用卡备用&#xff0c;没有一次逾期&#xff0c;但是最近想申请贷款都是被秒拒。他觉得很郁闷&#xff0c;是不是办了假的贷款&#xff1f;他完全不知道被拒的理由&#xff0c;只…

Linux--根文件系统的挂载过程分析

前言&#xff1a; 本篇文章以S3C6410公版的Linux BSP和U-Boot来进行分析&#xff0c;文中所有提及的名词和数据都是以该环境为例&#xff0c;所有的代码流程也是以该环境为例来进行分析。哈哈。如果有不正确或者不完善的地方&#xff0c;欢迎前来拍砖留言或者发邮件到guopeixi…

Troubleshooting OpenStack Bug- 每天5分钟玩转 OpenStack(162)

这是 OpenStack 实施经验分享系列的第 12 篇。 问题描述 客户报告了一个问题&#xff1a;对 instance 执行 migrate 操作&#xff0c;几个小时了一直无法完成&#xff0c;不太正常。 问题分析 遇到这种情况&#xff0c;第一个要检查的就是 instance 所在计算节点的 nova-comput…

ROM简单实现

ROM简单实现 题目描述 实现一个深度为8&#xff0c;位宽为4bit的ROM&#xff0c;数据初始化为0&#xff0c;2&#xff0c;4&#xff0c;6&#xff0c;8&#xff0c;10&#xff0c;12&#xff0c;14。可以通过输入地址addr&#xff0c;输出相应的数据data 接口信号图如下&…

Linux--Sys_Read系统调用过程分析

注&#xff1a; 本片文章以Read函数的调用为例来讲述一下系统对块驱动层的一些处理, 哈哈。如果有不正确或者不完善的地方&#xff0c;欢迎前来拍砖留言或者发邮件到guopeixin126.com进行讨论&#xff0c;先行谢过。 一&#xff0e;Read函数经由的层次模型 首先来了解一下Re…

负债的阶梯,你在第几层?

现在的年轻人成为了消费市场上的中流砥柱&#xff0c;他们为我国的GDP贡献了70%的数据。如今年轻人更愿意刷信用卡、网贷平台借钱消费。尽管在外打拼不容易&#xff0c;但是工作稳定&#xff0c;收入稳定&#xff0c;为了犒劳自己及时享乐&#xff0c;他们选择了背负一点债务。…

Android应用程序开发环境的建立

注&#xff1a; 本片文章简单的描述一下Android应用程序开发环境的建立。如果有不正确或者不完善的地方&#xff0c;欢迎前来拍砖留言或者发邮件到guopeixin126.com进行讨论&#xff0c;先行谢过。 一&#xff0e;准备工作... 2 二&#xff0e;安装步骤... 2 1. 安装JDK5.0.…

你真的会 snapshot 吗? - 每天5分钟玩转 OpenStack(163)

​这是 OpenStack 实施经验分享系列的第 13 篇。 instance snapshot 操作可用于备份或者将 instance 保存为新的 image。如果在生产系统中执行 snapshot 操作&#xff0c;必须确保此操作快速且安全。这里有两个关键点&#xff1a; 快速。 为保证数据的一致性&#xff0c;snaps…

输入序列连续的序列检测

输入序列连续的序列检测 题目描述 请编写一个序列检测模块&#xff0c;检测输入信号a是否满足01110001序列&#xff0c;当信号满足该序列&#xff0c;给出指示信号match。 模块的接口信号图如下&#xff1a; 模块的时序图如下&#xff1a; timescale 1ns/1ns module seque…

哪些信用卡取现0手续费?

经常使用信用卡的朋友都知道&#xff0c;信用卡取现一般是要手续费的&#xff0c;而且取现之后不能享受免息期&#xff0c;这点让人很不爽。有时候我们经常会急用钱&#xff0c;这时候用信用卡取现确实很方便&#xff0c;可以解决燃眉之急&#xff0c;能帮上不少忙&#xff0c;…

如何申请到利息低的贷款?

经常有朋友问我&#xff0c;如何才能申请到利息低的贷款&#xff1f;针对这个问题&#xff0c;很多时候我不能给予一个明确的回答。很多朋友之前可能都没有接触过贷款&#xff0c;所以对贷款本身并不是很了解&#xff0c;更有的朋友以为贷款利息可以像商品一样讨价还价&#xf…

含有无关项的序列检测

含有无关项的序列检测 题目描述 请编写一个序列检测模块&#xff0c;检测输入信号a是否满足011XXX110序列&#xff08;长度为9位数据&#xff0c;前三位是011&#xff0c;后三位是110&#xff0c;中间三位不做要求&#xff09;&#xff0c;当信号满足该序列&#xff0c;给出指…

Metadata Service 最高频的应用 - 每天5分钟玩转 OpenStack(164)

实现 instance 定制化&#xff0c;cloud-init&#xff08;或 cloudbase-init&#xff09;只是故事的一半&#xff0c;metadata service 则是故事的的另一半。两者的分工是&#xff1a;metadata service 为 cloud-init 提供自定义配置数据&#xff0c;cloud-init 完成配置工作。…

如何获取并分析Bluetooth HCI层Command Packet和Event Packet包

首先&#xff0c;如何来确定什么样的包为Command/Event Packet呢&#xff1f; 我们知道&#xff0c;通过HCI Packet包括四种&#xff0c;即Command&#xff0c;Event&#xff0c;ACL和SCO/eSCO&#xff0c;对应到MS-Stack中的定义&#xff0c;即为COMMAND_PACKET &#xff0c;E…

Metadata Service 架构详解 - 每天5分钟玩转 OpenStack(165)

下面是 Metadata Service 的架构图&#xff0c;本节我们详细讨论各个组件以及它们之间的关系。 nova-api-metadata nova-api-metadata 是 nova-api 的一个子服务&#xff0c;它是 metadata 的提供者&#xff0c;instance 可以通过 nova-api-metadata 的 REST API 来获取 metada…

hi3531交叉编译环境arm-hisiv100nptl-linux搭建过程

安装SDK 1、Hi3531 SDK包位置 在"Hi3531_V100R001***/01.software/board"目录下&#xff0c;您可以看到一个 Hi3531_SDK_Vx.x.x.x.tgz 的文件&#xff0c; 该文件就是Hi3531的软件开发包。 2、解压缩SDK包 在linux服务器上&#xff08;或者一台装有linux的PC上&…

不重叠序列检测

不重叠序列检测 题目描述 请编写一个序列检测模块&#xff0c;检测输入信号&#xff08;a&#xff09;是否满足011100序列&#xff0c; 要求以每六个输入为一组&#xff0c;不检测重复序列&#xff0c;例如第一位数据不符合&#xff0c;则不考虑后五位。一直到第七位数据即下一…

首付贷换了马甲,又重现江湖了

据《每日经济新闻》报道&#xff0c;广州某小贷公司通过无抵押信用贷款&#xff0c;一个月入万元左右的普通市民可以获得30万元贷款&#xff0c;高者可获得50万元无抵押信用贷款用于垫付首付款。首付贷以“消费贷”的名义&#xff0c;换了马甲又重现江湖了。根据《每日经济新闻…

获取 metadata 的完整例子 - 每天5分钟玩转 OpenStack(166)

我们将通过实验详细分析 instance 从 nova-api-metadata 获取信息的完整过程。 环境介绍 1. 一个 all-in-one 环境&#xff08;多节点类似&#xff09;。 2. 已创建 neutron 网络 test_net&#xff0c;DHCP 已启动。在这个 metadata 实验中&#xff0c; test_net 的 type 不重…