[原创]Linux arm 启动 c语言部分详解第一讲(from Start kernel)
?? ??
??written by leeming ??
??作为我们实验室的一个学术交流,我顺着fp的linux arm启动汇编部分继续下去。我们可以看到其实linux汇编部分的启动大量的工作是对zimage的解压,重定位等操作,如果是image(也就是zimage解压重定位结束后)来说,其实主要就做了以下这么几件事情:1.建立启动时的一级页表,2.打开mmu,3.保存机器号等参数。 ??
??因此对于整个处理器系统来说还需要做大量的工作,对于移植内核来说,只有真正了解了这部分你才会明白在arch/arm/mach-sep4020这个目录中的文件为什么需要这样写。进入正题: ??
??1.进入start_kernel,详解setup_arch(处理器的移植,页表建立都在这里实现的) ??
??asmlinkage void __init start_kernel(void) ?? ??{ ??
?? char * command_line; ??
?? extern struct kernel_param __start___param[], __stop___param[]; ?? ??/* ??
?? * Interrupts are still disabled. Do necessary setups, then ??
?? * enable them ?? ?? */ ??
?? lock_kernel(); ?? ?? ??
?? //这里是和高端内存相关的操作,arm中不涉及 ??
?? page_address_init(); ??
?? //这个只是printk的等级,但是为什么在控制台不显示,待看 ??
?? //这时候控制台还没有初始化,因此所有的信息都是在
log_buf里 ??
?? printk(KERN_NOTICE); ??
?? printk(linux_banner); ??
?? setup_arch(&command_line); ??
?? …… ??
?? …… ?? ?? ??
??到这里就碰到了我们详解start kernel的第一道坎,setup_arch(&command_line);别看就一句话,其实这个函数本身是非常庞大的,下面我们来具体看完整的setup_arch函数。 ??
??void __init setup_arch(char **cmdline_p) ?? ??{ ??
?? struct tag *tags = (struct tag *)&init_tags; ??
?? struct machine_desc *mdesc; ??
??//in the arch/arm/kernel/setup.c ??
??//static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; ??
??//在我们的配置中#define CONFIG_CMDLINE "root=/dev/ram0 rw console=ttyS0,115200" ??
?? char *from = default_command_line; ??
?? //就是查找你是什么版本的处理器架构,最后就是调用 ??
?? //了lookup_processor_type这个函数,它在汇编部分也提到过 ??
?? setup_processor(); ??
?? //machine_arch_type 就是我们的机器号0xc2 ??
?? mdesc = setup_machine(machine_arch_type);
??
?? machine_name = mdesc->name; ??
?? //这个变量初始值为"h",如果这里设置成softboot,它会将这个初始值变为"s" ??
?? if (mdesc->soft_reboot) ??
?? reboot_setup("s"); ??
?? //boot_params 如果为0则表示bootloader没有传参数 ??
?? //一般默认为0x30000100位置 ??
?? if (mdesc->boot_params) ??
?? tags = phys_to_virt(mdesc->boot_params); ?? ?? /* ??
?? * If we have the old style parameters, convert them to ??
?? * a tag list. ??
?? */ ??
?? if (tags->hdr.tag != ATAG_CORE) ??
?? convert_to_tag_list(tags); ??
?? if (tags->hdr.tag != ATAG_CORE) ??
?? tags = (struct tag *)&init_tags; ??
?? if (mdesc->fixup) ??
?? mdesc->fixup(mdesc, tags, &from, &meminfo); ??
?? //是通过标签0x544100**来辨别的,因此uboot中有相应的标签字 ??
?? if (tags->hdr.tag == ATAG_CORE) { ??
?? //已经被fixup函数修改,则将atag中的mem段置为none ??
?? if (meminfo.nr_banks != 0) ??
?? squash_mem_tags(tags); ??
?? //继续把atag的参数传递结束,在这里将把uboot传递进来的commandline覆盖 ??
?? parse_tags(tags); ?? ?? } ??
??//下面几个参数是由vmlinux.lds文件决定的 ??
?? init_mm.start_code = (unsigned long) &_text; ??
?? init_mm.end_code = (unsigned long) &_etext; ??
?? init_mm.end_data = (unsigned long) &_edata; ??
?? init_mm.brk = (unsigned long) &_end; ??
?? //因此在这里已经是我们uboot的参数了。 ??
?? memcpy(saved_command_line, from, COMMAND_LINE_SIZE); ??
?? saved_command_line[COMMAND_LINE_SIZE-1] = ‘\\0’; ??
??//分析cmdline,这里只分析了当中的mem部分。 ??
??//内核参数的解析一共有两处,一处是setup_arch()->parse_cmdline() ??
??//用于解析内核参数中关于内存的部分,另外一处是start_kernel()->parse_option()用于解析其余部分 ??
?? parse_cmdline(cmdline_p, from); ?? ?? ??
??//非常重要的部分,页表建立,根据meminfo,也就是我们在4020.c中的mache_start何mache_end之间的内容,它其实就是一个类型是machine_desc的结构体,这里就是把这个结构体中的内存信息(fixup函数中指定的),寄存器信息(sep4020_map_io指定的)建立相应的一二级页表 ??
??/*************************************/
??
?? paging_init(&meminfo, mdesc); ??
??/*************************************/ ??
?? request_standard_resources(&meminfo, mdesc); ?? ?? ??
??#ifdef CONFIG_SMP ??
?? smp_init_cpus(); ??
??#endif ?? ?? ??
??//设置不同模式的堆栈,包括3种模式,irq,abort,undefine,每种模式的堆栈是12个字节 ??
?? cpu_init(); ?? ??{ ??
?? //gcc内嵌汇编的格式是__asm(汇编代码:输入:输出:破坏描述部分); ??
?? //下面的限制字符"r" "I"是把变量放进通用寄存器,立即数的意思 ??
?? __asm__ ( ??
?? "msr cpsr_c, %1\\n\\t" ??
?? "add sp, %0, %2\\n\\t" ??
?? "msr cpsr_c, %3\\n\\t" ??
?? "add sp, %0, %4\\n\\t" ??
?? "msr cpsr_c, %5\\n\\t" ??
?? "add sp, %0, %6\\n\\t" ??
?? "msr cpsr_c, %7"