linux内核启动之setup_arch

来源:互联网 发布:三生网络直销怎么样 编辑:程序博客网 时间:2024/04/29 18:10

start_kernel ()

         --> setup_arch ()

                   --> paging_init ()

                            --> bootmem_init ()

                            --> alloc_bootmem_low_pages ()

 

       调用 setup_arch()函数进行与体系结构相关的第一个初始化工作; 
对不同的体系结构来说该函数有不同的定义。对于 ARM 平台而言,该函数定义在arch/arm/kernel/Setup.c。它首先通过检测出来的处理器类型进行处理器内核的初始化,然后通过 bootmem_init()函数根据系统定义的 meminfo 结构进行内存结构的初始化,最后调用paging_init()开启 MMU,创建内核页表,映射所有的物理内存和 IO空间。

 

函数实现在 arch/arm/kernel/Setup.c 对内核参数的解析

void __init setup_arch(char **cmdline_p)
{
 struct tag *tags = (struct tag *)&init_tags; //定义了一个默认的内核参数列表
 struct machine_desc *mdesc;
 char *from = default_command_line;

 unwind_init();   //本架构中没有用

 setup_processor();  //汇编的CPU初始化部分
 mdesc = setup_machine(machine_arch_type);
 machine_name = mdesc->name;

 if (mdesc->soft_reboot)
  reboot_setup("s");

 if (__atags_pointer)
  tags = phys_to_virt(__atags_pointer);
 else if (mdesc->boot_params)
  tags = phys_to_virt(mdesc->boot_params);
//由于MMU单元已打开,此处需要而boot_params是物理地址,需要转换成虚拟地址才能访问,因为此时CPU访问的都是虚拟地址
 /*
  * If we have the old style parameters, convert them to
  * a tag list.
  */
//内核参数列表第一项必须是ATAG_CORE类型
 if (tags->hdr.tag != ATAG_CORE)  //如果不是,则需要转换成新的内核参数类型,新的内核参数类型用下面struct tag结构表示
  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);   //用内核参数列表填充meminfo

 if (tags->hdr.tag == ATAG_CORE) {
  if (meminfo.nr_banks != 0)
   squash_mem_tags(tags);
  save_atags(tags);
  parse_tags(tags); //解析内核参数列表,然后调用内核参数列表的处理函数对这些参数进行处理。比如,如果列表为命令行,则最终会用parse_tag_cmdlin函数进行解析,这个函数用_tagtable编译连接到了内核里
 }
//下面是记录内核代码的起始,结束虚拟地址
 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;

//下面是对命令行的处理,刚才在参数列表处理parse_tag_cmdline函数已把命令行拷贝到了from空间
 memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
 boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
 parse_cmdline(cmdline_p, from);  //解析出命令行,命令行解析出以后,同样会调用相关处理函数进行处理。系统用__early_param宏在编译阶段把处理函数编译进内核。
 paging_init(mdesc);
//这个函数完成页表初始化,具体的方法为建立线性地址划分后每个地址空间的标志;清除在boot阶段建立的内核映射空间,也即把页表项全部清零;调用bootmem_init,禁止无效的内存节点,由于我们的物理内存都是连续的空间,因此,内存节点为1个。接下来判断INITRD映像是否存在,若存在则检查其所在的地址是否在一个有效的地址内,然后返回此内存节点号。
//先看两个数据结构。
//struct meminfo表示内存的划分情况。Linux的内存划分为bank。每个bank用
//struct membank表示,start表示起始地址,这里是物理地址,size表示大小,node表示此bank所在的节点号,对于只有一个节点的内存,所有bank节点都相等
//struct membank {
//       unsigned long start;
//       unsigned long size;
//       int           node;
//};
//struct meminfo {
//       int nr_banks;
//       struct membank bank[NR_BANKS];
//};


//在完成内存页映射后即进入request_standard_resources,这个函数比较简单,主要完成从iomem_resource空间申请所需的内存资源,比如内核代码和视频所需的资源等
 request_standard_resources(&meminfo, mdesc);


#ifdef CONFIG_SMP
 smp_init_cpus();
#endif

 cpu_init();  //此函数为空

 /*
  * Set up various architecture-specific pointers
  */
 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
 early_trap_init();  //重定位中断向量,将中断向量代码拷贝到中断向量页,并把信号处理代码指令拷贝到向量页中
}

原创粉丝点击