这篇文章,就来搞定他们,再遇到它们时,拒绝懵比!
首先,来看下链接脚本的缩略版:
- 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,
- };
顺便看一下 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_machinemap_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标志 *
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);
- 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
- }
- 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;
- }
- #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 }
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
- }
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)
- __define_initcall("6",fn,6)
- static initcall_t __initcall_mac_hid_init6 __attribute_used__ __attribute__((__section__(".initcall" 6 ".init")))
- <span style="white-space:pre"> </span>= mac_hid_init
- #define module_init(x) __initcall(x);
- #define __initcall(fn) device_initcall(fn)
那么 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();
- }