start kernel flow

来源:互联网 发布:软件系统评分表 编辑:程序博客网 时间:2024/06/05 18:57
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];


/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();


/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();


cgroup_init_early();


local_irq_disable();
early_boot_irqs_disabled = true;


/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu();/* arch-specific boot-cpu hooks */


build_all_zonelists(NULL, NULL);
page_alloc_init();


pr_notice("Kernel command line: %s\n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
  __stop___param - __start___param,
  -1, -1, &unknown_bootoption);


jump_label_init();


/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
setup_log_buf(0);
pidhash_init();
vfs_caches_init_early();
sort_main_extable();
trap_init();
mm_init();


/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
idr_init_cache();
perf_event_init();
rcu_init();
tick_nohz_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
tick_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
call_function_init();
WARN(!irqs_disabled(), "Interrupts were enabled early\n");
early_boot_irqs_disabled = false;
local_irq_enable();


kmem_cache_init_late();


/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();
if (panic_later)
panic(panic_later, panic_param);


lockdep_info();


/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
locking_selftest();


#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
   page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
   page_to_pfn(virt_to_page((void *)initrd_start)),
   min_low_pfn);
initrd_start = 0;
}
#endif
page_cgroup_init();
debug_objects_mem_init();
kmemleak_init();
setup_per_cpu_pageset();
numa_policy_init();
if (late_time_init)
late_time_init();
sched_clock_init();
calibrate_delay();
pidmap_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
proc_caches_init();
buffer_init();
key_init();
security_init();
dbg_late_init();
vfs_caches_init(totalram_pages);
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cgroup_init();
cpuset_init();
taskstats_init_early();
delayacct_init();


check_bugs();


acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();


if (efi_enabled(EFI_RUNTIME_SERVICES)) {
efi_late_init();
efi_free_boot_services();
}


ftrace_init();


/* Do the rest non-__init'ed, we're now alive */
rest_init();
}

MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->TESTPROJECT_stingray_init()
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。


arcadia MACH_ARCADIAARCADIA 4423
arcadia_l MACH_ARCADIA_LARCADIA_L 4424
msm8930dt MACH_MSM8930DTMSM8930DT 4425
ktam3874 MACH_KTAM3874KTAM3874 4426
cec4 MACH_CEC4CEC4 4427
ape6evm MACH_APE6EVMAPE6EVM 4428
tx6 MACH_TX6 TX6 4429
cfa10037 MACH_CFA10037CFA10037 4431
ezp1000 MACH_EZP1000EZP1000 4433
wgr826v MACH_WGR826VWGR826V 4434
exuma MACH_EXUMAEXUMA 4435
fregate MACH_FREGATEFREGATE 4436
osirisimx508 MACH_OSIRISIMX508OSIRISIMX508 4437
st_exigo MACH_ST_EXIGOST_EXIGO 4438
pismo MACH_PISMOPISMO 4439
atc7 MACH_ATC7ATC7 4440
nspireclp MACH_NSPIRECLPNSPIRECLP 4441
nspiretp MACH_NSPIRETPNSPIRETP 4442
nspirecx MACH_NSPIRECXNSPIRECX 4443
maya MACH_MAYAMAYA 4444
wecct MACH_WECCTWECCT 4445
m2s MACH_M2S M2S 4446




MAYA这个宏在/arch/arm/tools/mach-types文件里定义:

MACHINE_START的定义在arch/arm/include/asm/mach/arch.h
#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                             \
};


其实就是定义了一个struct machine_desc类型结构体变量,这个结构体还定义了其他一些成员,接下来着重关注.init_machine这个成员,
它是一个函数指针,值为 XXXXX_machine_init,
具体内容先不分析。现在最关心的是这个结构体变量在哪里被调用,从而调用它里面的成员和成员函数呢?先来看/arch/arm/kernel/setup.c 里面的setup_arch()函数:


_text, _etext, _edata, _end 
这四个参数来自于文件:[ arch/arm/kernel/vmlinux.lds.S ]


arch/arm/include/asm/mach-types.h 内容
include/generated/mach-types.h


#define MACH_TYPE_XXXXX            2520


也就是说参数machine_arch_type的值为2520。在setup_machine()函数里主要调用了lookup_machine_type()函数来查找对应的type,应该是出于效率的原因,
这个函数是通过汇编实现的,在此就不给出具体代码了。到这里,知道了在/init/main.c的start_kernel()函数里调用了setup_arch(),在setup_arch()里找到了具体的struct machine_desc类型的变量,但是在哪里通过这个变量调用里面的成员或成员函数的呢?继续找。还是在setup.c里,看到了这样一个函数:
复制代码


1 static int __init customize_machine(void)
2 {
3         /* customizes platform devices, or adds new ones */
4         if (machine_desc->init_machine)
5                 machine_desc->init_machine();
6         return 0;
7 }
8 arch_initcall(customize_machine);


终于看到了,成员函数init_machine就是在这里被调用的。但是它没有被显式调用,而是放在了arch_initcall这个宏里,去看看它怎么定义的:


#define arch_initcall(fn)               __define_initcall("3",fn,3)


再看__define_initcall宏:


1 #define __define_initcall(level,fn,id) \
2         static initcall_t __initcall_##fn##id __used \
3         __attribute__((__section__(".initcall" level ".init"))) = fn


嗯,它被链接到了.initcall段里,现在简单看看/arch/arm/kernel/vmlinux.lds这个链接脚本里关于initcall的定义:
复制代码


 1 __initcall_start = .; 
 2 *(.initcallearly.init) __early_initcall_end = .; 
 3 *(.initcall0.init) *(.initcall0s.init) 
 4 *(.initcall1.init) *(.initcall1s.init) 
 5 *(.initcall2.init) *(.initcall2s.init) 
 6 *(.initcall3.init) *(.initcall3s.init) 
 7 *(.initcall4.init) *(.initcall4s.init) 
 8 *(.initcall5.init) *(.initcall5s.init) 
 9 *(.initcallrootfs.init) 
10 *(.initcall6.init) *(.initcall6s.init) 
11 *(.initcall7.init) *(.initcall7s.init) 
12 __initcall_end = .;




可以看到customize_machine()被放到了.initcall3.init里。说了那么多定义,究竟它在哪里被调用啊?好吧,它是在/init/main.c里一个叫do_initcalls()的函数里被调用,去看看呗:
复制代码


1 extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];

3 static void __init do_initcalls(void)
4 {
5         initcall_t *fn;

7         for (fn = __early_initcall_end; fn < __initcall_end; fn++)
8                 do_one_initcall(*fn);
9 }






看到第1行,很熟悉吧。在for循环里依次调用了从__early_initcall_end开始到__initcall_end结束的所有函数。customize_machine()也是在其间被调用。




start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->mini6410_machine_init()




让我们看一下parameter 的parser


parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
  __stop___param - __start___param,
  -1, -1, &unknown_bootoption);


early_param和__setup宏




1.编译相关
    在/include/asm-generic/Vmlinux.lds.h文件中定义了__setup_start.....__setup_end段
    #define INIT_SETUP(initsetup_align)         \  
            . = ALIGN(initsetup_align);     \  
            VMLINUX_SYMBOL(__setup_start) = .;  \  
            *(.init.setup)          \  
            VMLINUX_SYMBOL(__setup_end) = .;  
    标记了.init.setup的函数会被编译进该段
2.内核启动的相关调用关系
    在start_kernel中调用parse_early_param()


    void __init parse_early_param(void)  
    {  
        static __initdata int done = 0;  
        static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];  
        if (done)  
            return;  
        strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); //复制启动命令行数据  
        parse_early_options(tmp_cmdline);   //调用parse_early_options函数  
        done = 1;  
    }  
    parse_early_options函数
    void __init parse_early_options(char *cmdline)  
    {  
        parse_args("early options", cmdline, NULL, 0, do_early_param);  
    }  
    接着调用parse_args函数
    int parse_args(const char *name,char *args,const struct kernel_param *params,unsigned num,int (*unknown)(char *param, char *val))  
    {  
        char *param, *val;  
        DEBUGP("Parsing ARGS: %s\n", args);  
      
        args = skip_spaces(args);  
      
        while (*args) { //遍历启动命令行  
            int ret;  
            int irq_was_disabled;  
      
            args = next_arg(args, &param, &val);    //获取下一个参数,填充param和val参数(例如:param--console;val--tty2,115200n8)  
            irq_was_disabled = irqs_disabled();  
            ret = parse_one(param, val, params, num, unknown);  //解析一个命令行参数  
            if (irq_was_disabled && !irqs_disabled()) {  
                printk(KERN_WARNING "parse_args(): option '%s' enabled ""irq's!\n", param);  
            }  
            switch (ret) {  
            case -ENOENT:  
                printk(KERN_ERR "%s: Unknown parameter `%s'\n",name, param);  
                return ret;  
            case -ENOSPC:  
                printk(KERN_ERR "%s: `%s' too large for parameter `%s'\n",name, val ?: "", param);  
                return ret;  
            case 0:  
                break;  
            default:  
                printk(KERN_ERR"%s: `%s' invalid for parameter `%s'\n",name, val ?: "", param);  
                return ret;  
            }  
        }  
      
        /* All parsed OK. */  
        return 0;  
    }  
    命令行参数的解析parse_one 
    static int parse_one(char *param,char *val,const struct kernel_param *params,unsigned num_params,int (*handle_unknown)(char *param, char *val))  
    {  
        unsigned int i;  
        int err;  
      
        /* Find parameter */  
        for (i = 0; i < num_params; i++) {   //num_params=0  
            if (parameq(param, params[i].name)) {  
                if (!val && params[i].ops->set != param_set_bool)  
                    return -EINVAL;  
                DEBUGP("They are equal!  Calling %p\n",params[i].ops->set);  
                mutex_lock(&param_lock);  
                err = params[i].ops->set(val, &params[i]);  
                mutex_unlock(&param_lock);  
                return err;  
            }  
        }  
      
        if (handle_unknown) {   //若handle_unknown函数存在  
            DEBUGP("Unknown argument: calling %p\n", handle_unknown);  
            return handle_unknown(param, val);  //则调用handle_unknown函数,参数为param,val  
        }  
      
        DEBUGP("Unknown argument `%s'\n", param);  
        return -ENOENT;  
    }  
    回溯回去handle_unknow函数就是do_early_param
    [cpp]  
    static int __init do_early_param(char *param, char *val)  
    {  
        const struct obs_kernel_param *p;  
      
        for (p = __setup_start; p < __setup_end; p++) {  
            if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0)) {  
                if (p->setup_func(val) != 0)  
                    printk(KERN_WARNING"Malformed early option '%s'\n", param);  
            }  
        }  
        /* We accept everything at this stage. */  
        return 0;  
    }  
    do_early_param函数从__setup_start遍历到__setup_end段,
    判断参数,进入if函数体里面
    if (p->setup_func(val) != 0)这句调用了对应setup_func或early_param成员的函数,并将val作为其参数,val其实便是__setup(str, fn)或__early_param中的str
    其实就是调用了fn(str)
    这里的第一条if会刷选掉__setup定义的情况(除了console和earlycon参数的),因为__setup定义的obs_kernel_param结构体p->early=0
    __setup定义的fn会在start_kernel->parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);
    unknown_bootoption->obsolete_checksetup函数给调用
    看start_kernel中调用顺序
    parse_early_param();  
    parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param,&unknown_bootoption);  
    可见先调用__early_param定义的解析参数函数及__setup定义的(console及earlycon)的参数解析函数
    接着再调用__setup定义的其他解析参数函数
0 0
原创粉丝点击