[原创]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 (_banks != 0)   squash_mem_tags(tags);  //继续把atag的参数传递结束,在这里将把uboot传递进来的commandline覆盖  parse_tags(tags); }//下面几个参数是由vmlinux.lds文件决定的 init_mm.start_code = (unsigned long) &_text; d_code   = (unsigned long) &_etext; d_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指定的)建立相应的一二级页表/*************************************/       p
aging_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"           :           : "r" (stk),//堆栈空间起始地址             "I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),//切换到irq模式,并禁止irq fiq             "I" (offsetof(struct stack, irq[0])),//irq[0]的偏移,一下同理             "I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE),             "I" (offsetof(struct stack, abt[0])),             "I" (PSR_F_BIT | PSR_I_BIT | UND_MODE),             "I" (offsetof(struct stack, und[0])),             "I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE)           : "r14");//注意切换svc时,告诉系统,r14改变了}       /*        * Set up various architecture-specific pointers        */ //继续将020.c中的mache_start何mache_end之间的内容注册到系统中,这里是把系统初始化函数,中断,定时器注册(这几
printf输出格式linux
部分也就是在为一个新的处理器移植内核的时候非常重要的地方)       init_arch_irq = mdesc->init_irq;       system_timer = mdesc->timer;       init_machine = mdesc->init_machine; #ifdef CONFIG_VT#if defined(CONFIG_VGA_CONSOLE)       conswitchp = &vga_con;#elif defined(CONFIG_DUMMY_CONSOLE)       conswitchp = &dummy_con;#endif#endif}至此整个setup_arch函数解释完毕,由于setup_arch中还有一个重要函数paging_init函数也蛮庞大的,因此在第二讲中详细说明这个函数。