作者:答疑助手lizuobin

原文:

https://blog.csdn.net/lizuobin2/article/details/51779064

在前面学习的过程中,看代码时遇到 arch_initcall(xxx) 等函数总是愣的,对于最基础的module_init(xxx) 也只是拿来用用,不知道幕后的原理,知道 MACHINE_START 是创建了一个machine_desc ,却不知道machine_desc->map_io 等函数何时被调用。

这篇文章,就来搞定它们,再遇到它们时,拒绝懵比!

友情提示:全文9100字,涉及代码较多,请建立source insight工程跟着linux源码阅读。建议收藏哦。

首先,来看链接脚本的缩略版:

SECTIONS
{
.init : { /* Init code and data */
INIT_TEXT
_einittext = .;
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
. = ALIGN(16);
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(.early_param.init)
__early_end = .;
__initcall_start = .;
INITCALLS
__initcall_end = .;
}

内核的文件就是这样组织的,但是具体每个段放的什么东西,怎么放进去,何时取出来我们不知道,下面一个一个分析。

1、*(.proc.info.init) 段

内核中,定义了若干个proc_info_list 结构,它的结构原形在include/asm-arm/procinfo.h 中,表示它所支持的CPU。

struct proc_info_list {
unsigned int cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char *arch_name;
const char *elf_name;
unsigned int elf_hwcap;
const char *cpu_name;
struct processor *proc;
struct cpu_tlb_fns *tlb;
struct cpu_user_fns *user;
struct cpu_cache_fns *cache;
};

对于ARM架构的CPU,这些结构体的源码在arch/arm/mm/目录下,比如 proc-arm920.S , proc_info_list 结构被定义在 ".proc.info.init" 段,在连接内核时,这些结构体被组织在一起,开始地址 __proc_info_begin ,结束地址 _proc_info_end 。

.section ".proc.info.init", #alloc, #execinstr
.type __arm920_proc_info,#object
__arm920_proc_info:
.long 0x41009200
.long 0xff00fff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm920_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
.long cpu_arm920_name
.long arm920_processor_functions
.long v4wbi_tlb_fns
.long v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
.long arm920_cache_fns
#else
.long v4wt_cache_fns
#endif
.size __arm920_proc_info, . - __arm920_proc_info

在内核启动时,首先读取出芯片ID,然后就在__proc_info_begin 和 _proc_info_end 取出 proc_info_list ,看内核是否支持这个CPU。

2、*(.arch.info.init) 段

*(.arch.info.init) 段,存放的是内核所支持的单板信息如机器ID、其实IO物理地址等,它由 MACHINE_START、MACHINE_END 定义。

#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 \
};

举个例子:

MACHINE_START(HALIBUT,"Halibut Board (QCT SURF7200A)")
.boot_params = 0x10000100,
.map_io = halibut_map_io,
.init_irq = halibut_init_irq,
.init_machine = halibut_init,
.timer = &msm_timer,
MACHINE_END

将宏展开:

struct machine_desc __mach_desc_HALIBUT{
__used
__attribute__((__section__(".arch.info.init")))= {
.nr = MACH_TYPE_HALIBUT,
.name = "HalibutBoard (QCT SURF7200A)",
.boot_params = 0x10000100,
.map_io = halibut_map_io,
.init_irq = halibut_init_irq,
.init_machine = halibut_init,
.timer = &msm_timer,
};

内核连接时,所有的 machine_desc 结构都会位于 ".arch.info.init" 段,不同的 machine_desc 结构体用于不同的单板,u-boot 调用内核时,会在 r1 寄存器中给出开发板的标记(机器ID),在__lookup_machine_type 函数中,将取出 ".arch.info.init" 段中的每一个 machine_desc 结构,比较 r1 与 machine_desc->nr 判断内核是否支持该单板。

顺便看一下 map_io 等函数的调用时机:

start_kernel
setup_arch(&command_line);
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
paging_init(mdesc)
devicemaps_init(mdesc);
mdesc->map_io()
init_IRQ()
init_arch_irq();
time_init()
system_timer->init();
rest_init();
kernel_init
do_basic_setup()
do_initcalls()
init_machine()
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine);

先后顺序:

start_kernel -》setup_arch -》 map_io -》 init_irq -》 timer -》 init_machine

map_io 函数中 会对内核进行分区还有时钟、串口的初始化,移植内核时需注意!(传入的机器ID不同,调用的初始化函数自然不同)

3、*(.taglist.init)

*(.taglist.init) 段存放的是 uboot 传递到内核的 tag 的处理函数。在 uboot 中,定义了一个 tag 结构体,里面存放要传递给内核的信息,Uboot 将 tag 依次排放在和内核约定的地点,如s3c2440是 0x30000100 处,排放顺序是有要求的,必须以 ATAG_CORE 标记的 tag 开头,以 ATAG_NONE 为标记的 tag 结尾。

struct tag {

  struct tag_header {
u32 size; /* 表示tag数据结构的联合u实质存放的数据的大小*/
u32 tag; /* 表示标记的类型 */
}hdr; union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};

setup_start_tag (bd); /*设置ATAG_CORE标志*/
setup_memory_tags (bd); /*设置内存标记*/
setup_commandline_tag (bd, commandline); /*设置命令行标记*/
...
setup_end_tag (bd); /*设置ATAG_NONE标志 */

在内核中,使用 __tagtable 来将处理 tag 的函数放到 *(.taglist.init) 段

arch\arm\kernel\setup.c

__tagtable(ATAG_CORE, parse_tag_core);
__tagtable(ATAG_MEM, parse_tag_mem32);
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
__tagtable(ATAG_REVISION, parse_tag_revision);

宏定义在 setup.h (include\asm-arm)

struct tagtable {
__u32 tag;
int (*parse)(const struct tag *);
}; #define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }

以 __tagtable(ATAG_CORE, parse_tag_core)为例


static struct tagtable __tagtable_parse_tag_core __used __attribute__((__section__(".taglist.init"))) = {
ATAG_CORE,
parse_tag_core
}

在内核启动过程中,会使用 parse_tags 来处理 tag ,它最终会调用到 parse_tag ,取出 __tagtable_begin 和 __tagtable_end 之间的每一个 tagtable ,比较它们的类型,如果相同则调用 tagtable 里的处理函数来处理这个tag 。

if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
parse_tags(tags); static void __init parse_tags(const struct tag *t)
{
for (; t->hdr.size; t = tag_next(t))
if (!parse_tag(t))
printk(KERN_WARNING
"Ignoring unrecognised tag 0x%08x\n",
t->hdr.tag);
}
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t; for (t = &__tagtable_begin; t < &__tagtable_end; t++)
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
} return t < &__tagtable_end;
}

4、*(.init.setup)


#define __setup(str, fn) \
__setup_param(str, fn, fn, 0) #define early_param(str, fn) /
__setup_param(str, fn, fn, 1) struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};

#define __initdata __attribute__ ((__section__ (".init.data")))
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata = str; \
static struct obs_kernel_param __setup_##unique_id \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }

举个例子:

__setup("init=", init_setup);
__setup_param("init=", init_setup, init_setup, 0) static char __setup_str_init_setup[] __attribute__ ((__section__ (".init.data"))) = "init=";
static struct obs_kernel_param __setup_init_setup
__attribute_used__
__attribute__((__section__(".init.setup")))
__attribute__((aligned((sizeof(long)))))
= { __setup_str_init_setup, init_setup, 0 }

parse_early_param 处理 early_param 定义的参数,parse_args 处理 __setup 定义的参数

5、*(.early_param.init)

struct early_params {
const char *arg;
void (*fn)(char **p);
}; #define __early_param(name,fn) \
static struct early_params __early_##fn __used \
__attribute__((__section__(".early_param.init"))) = { name, fn }

例如:

__early_param("initrd=", early_initrd);
static struct early_params __early_early_initrd __used __attribute__((__section__(".early_param.init"))) =
{
"initrd=",
early_initrd
}

parse_cmdline 处理 __early_param 定义的参数

6、INITCALLS

#define INITCALLS
*(.initcallearly.init) \
VMLINUX_SYMBOL(__early_initcall_end) = .; \
*(.initcall0.init) \
*(.initcall0s.init) \
*(.initcall1.init) \
*(.initcall1s.init) \
*(.initcall2.init) \
*(.initcall2s.init) \
*(.initcall3.init) \
*(.initcall3s.init) \
*(.initcall4.init) \
*(.initcall4s.init) \
*(.initcall5.init) \
*(.initcall5s.init) \
*(.initcallrootfs.init) \
*(.initcall6.init) \
*(.initcall6s.init) \
*(.initcall7.init) \
*(.initcall7s.init) \
typedef int (*initcall_t)(void);
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn
#define pure_initcall(fn)              __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)

以 device_initcall(mac_hid_init) 为例:

__define_initcall("6",fn,6)
static initcall_t __initcall_mac_hid_init6 __attribute_used__ __attribute__((__section__(".initcall" 6 ".init")))
= mac_hid_init

再看我们熟悉的 module_init(xxx_init)

#define module_init(x)     __initcall(x);
#define __initcall(fn) device_initcall(fn)

看来 module_init(xxx_init) 在 “.initcall6.init” 段 创建函数指针 指向 xxx_init

那么 INITCALLS 里的函数在哪里调用?

start_kernel

    rest_init()

        kernel_init

            do_basic_setup()

                do_initcalls()
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();
}

也就是说 INITCALLS 段里的东西会在内核启动时按顺序逐个调用,以后遇到 xxx_initcall 就不会懵B了。

                                           --

最新文章

  1. 双向数据绑定(angular,vue)
  2. Python 数据类型笔记
  3. C#多线程技术总结(同步)
  4. 【poj3764】 The xor-longest Path
  5. LABjs(类似于LazyLoad,但它更加方便管理依赖关系)
  6. SQL基础语法等
  7. Object-c : block需要注意的几点问题
  8. Mysql设置字符编码及varchar宽度问题
  9. ubuntu12.04和deepin12.06使用root账户登录
  10. Maven远程仓库
  11. 【腾讯Bugly干货分享】总结一个技术总监的教训和经验
  12. 【转】Android JNI编程—JNI基础
  13. 5 Java学习之 泛型
  14. Word字体与像素的对应关系(转)
  15. GIS制图课程目录
  16. Sql server DATEADD日期函数的使用
  17. Ajax简单总结
  18. 前端框架Bootstrap - 快速搭建网站
  19. php代码审计——DVWA
  20. Eclipse控制台输出log日志中文乱码

热门文章

  1. ng build --aot 与 ng build --prod
  2. docker命令小结
  3. Spring Cloud(1):概览
  4. 在SSH里面远程启动ubuntu上的GUI程序
  5. pandas中对日期型数据进行处理
  6. vue-cli@webpack@4打包分析命令
  7. 查找担保圈-step7-提取未被包含过组的成员,得出结论
  8. 查看Oracle索引是否被使用或者有效
  9. ASP.NET Core中使用Dapper
  10. C#与C++的区别