setup_arch()函数是start_kernel阶段最重要的一个函数,每个体系都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个体系的setup_arch()函数,由顶层Makefile中的ARCH变量决定:
它首先通过检测出来的处理器类型进行处理器内核的初始化,然后通过 bootmem_init()函数根据系统定义的 meminfo 结构进行内存结构的初始化,最后调用paging_init()开启 MMU,创建内核页表,映射所有的物理内存和 IO空间。
void __init setup_arch(char **cmdline_p) { /*内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。 machine_desc结构体通过MACHINE_START宏来初始化,在代码中, 通过在start_kernel->setup_arch中调用setup_machine_fdt来获取。*/ struct machine_desc *mdesc; /*首先从arm寄存器里面取得cpu ID,然后调用lookup_processor_type来取得proc_info_list这个结构体。 取得proc_info_list以后,将里面的内容一个个赋值给相应的全局变量, 然后将CPU的信息打印出来。然后它会从arm寄存器里面获得cache的信息, 并将cache的信息打印出来(CPU: ARMv7 Processor [410fc051] revision 1 (ARMv7), cr=10c53c7d CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache)。*/ setup_processor(); /*machine_desc结构体通过MACHINE_START宏来初始化,在代码中,调用setup_machine_fdt来获取。 对于samad5,它通过在arch/arm/mach-at91/board_dt.c中的DT_MACHINE_START宏初始化 输出CPU型号Machine: Atmel SAMA5 (Device Tree), model: Atmel SAMA5D34-EK*/ mdesc = setup_machine_fdt(__atags_pointer); if (!mdesc) mdesc = setup_machine_tags(machine_arch_type); machine_desc = mdesc; machine_name = mdesc->name; setup_dma_zone(mdesc); //通过struct machine_desc 中的soft_reboot数据来设置重启类型: if (mdesc->restart_mode) reboot_setup(&mdesc->restart_mode); /*这里通过连接脚本中得到的Linux代码位置数据来初始化一个mm_struct结构体init_mm中的部分数据 ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核自身的mm_struct */ 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; /* 同时填充cmd_line以备后用, 保护boot_command_line数据 populate cmd_line too for later use, preserving boot_command_line */ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = cmd_line; /* 处理在 struct obs_kernel_param 中定义为early的启动参数(主要是内存配置部分的参数) 其中就分析了mem=size@start参数初始化了struct meminfo meminfo; 同时如果有vmalloc=size参数也会初始化 vmalloc_min 参考:《Linux内核高-低端内存设置代码跟踪(ARM构架)》 这里需要注意的是内核的cmdline中的参数按照其被需要的先后,分为early和非early的。 include/linux/init.h: struct obs_kernel_param { const char *str; //在cmdline中相应参数名。 int (*setup_func)(char *); //对于此参数的专用处理函数 int early; //是否为早期需要处理的参数 }; 两种不同的参数在内核中用了不同的宏来定义: early: #define early_param(str, fn) \ __setup_param(str, fn, fn, 1) 非early: #define __setup(str, fn) \ __setup_param(str, fn, fn, 0) 使用这两个宏定义的参数和构架相关,一些构架或者板子可以定义自己特定的参数和处理函数。对于比较重要的“men”参数就是early参数。 */ parse_early_param(); sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL); sanity_check_meminfo();//在此处设置struct meminfo meminfo中每个bank中的highmem变量2.通过vmalloc_min确定每个bank中的内存是否属于高端内存 arm_memblock_init(&meminfo, mdesc);//按地址数据从小到大排序meminfo中的数据,并初始化全局的memblock数据。 /* 设置内核的参考页表。 此页表不仅用于物理内存映射,还用于管理vmalloc区。 此函数中非常重要的一点就是初始化了bootmem分配器! */ paging_init(mdesc); //通过获取设备描述结构体(struct machine_desc)中的数据和编译时产生的地址数据,初始化内存相关的全局结构体变量。 request_standard_resources(mdesc); if (mdesc->restart) arm_pm_restart = mdesc->restart; //通过启动参数中的“非平坦设备树”信息(如果有),获取内存相关信息 unflatten_device_tree(); /*针对SMP处理器,初始化可能存在的CPU映射 - 这描述了可能存在的CPU*/ #ifdef CONFIG_SMP if (is_smp()) smp_init_cpus(); #endif /*1.用于内核崩溃时的保留内核此功能通过内核command line参数中的"crashkernel="保留下内存用于主内核崩溃时获取内核信息的导出*/ reserve_crashkernel(); /* 初始化ARM内部的TCM(紧耦合内存)。 参考资料:《对ARM紧致内存TCM的理解》 */ tcm_init(); #ifdef CONFIG_MULTI_IRQ_HANDLER handle_arch_irq = mdesc->handle_irq; #endif #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) conswitchp = &vga_con; #elif defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; #endif #endif /* 1.如果设备描述结构体定义了init_early函数(应该是早期初始化之意),则在这里调用。 */ if (mdesc->init_early) mdesc->init_early(); }
主要完成了4个方面的工作,一个就是取得MACHINE和PROCESSOR的信息然或将他们赋值给kernel相应的全局变量,然后呢是对boot_command_line和tags接行解析,再然后呢就是memory、cach的初始化,最后是为kernel的后续运行请求资源。主要完成了4个方面的工作,
1.一个就是取得MACHINE和PROCESSOR的信息然或将他们赋值给kernel相应的全局变量.
2.对boot_command_line和tags接行解析.
3.memory、cach的初始化.
4.为kernel的后续运行请求资源.