[ 注:内核版本Linux-2.6.39.4 开发板:MINI2440 ]
setup_arch执行是由start_kernel来调用的:
start_kernel [ init/main.c ] --> setup_arch(&command_line) [arch/arm/kernel/setup.c ]
799 void __init setup_arch(char **cmdline_p)
800 {
801 struct tag *tags = (struct tag *)&init_tags;
802 struct machine_desc *mdesc;
803 char *from = default_command_line;
804
805 init_tags.mem.start = PHYS_OFFSET;
806
807 unwind_init();
808
809 setup_processor();
810 mdesc = setup_machine(machine_arch_type);
811 machine_desc = mdesc;
812 machine_name = mdesc->name;
813
814 if (mdesc->soft_reboot)
815 reboot_setup("s");
816
817 if (__atags_pointer)
818 tags = phys_to_virt(__atags_pointer);
819 else if (mdesc->boot_params) {
820 #ifdef CONFIG_MMU
821 /*
822 * We still are executing with a minimal MMU mapping created
823 * with the presumption that the machine default for this
824 * is located in the first MB of RAM. Anything else will
825 * fault and silently hang the kernel at this point.
826 */
827 if (mdesc->boot_params < PHYS_OFFSET ||
828 mdesc->boot_params >= PHYS_OFFSET + SZ_1M) {
829 printk(KERN_WARNING
830 "Default boot params at physical 0x%08lx out of reach\n",
831 mdesc->boot_params);
832 } else
833 #endif
834 {
835 tags = phys_to_virt(mdesc->boot_params);
836 }
837 }
838
839 #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
840 /*
841 * If we have the old style parameters, convert them to
842 * a tag list.
843 */
844 if (tags->hdr.tag != ATAG_CORE)
845 convert_to_tag_list(tags);
846 #endif
847 if (tags->hdr.tag != ATAG_CORE)
848 tags = (struct tag *)&init_tags;
849
850 if (mdesc->fixup)
851 mdesc->fixup(mdesc, tags, &from, &meminfo);
852
853 if (tags->hdr.tag == ATAG_CORE) {
854 if (meminfo.nr_banks != 0)
855 squash_mem_tags(tags);
856 save_atags(tags);
857 parse_tags(tags);
858 }
859
860 init_mm.start_code = (unsigned long) _text;
861 init_mm.end_code = (unsigned long) _etext;
862 init_mm.end_data = (unsigned long) _edata;
863 init_mm.brk = (unsigned long) _end;
864
865 /* parse_early_param needs a boot_command_line */
866 strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
867
868 /* populate cmd_line too for later use, preserving boot_command_line */
869 strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
870 *cmdline_p = cmd_line;
871
872 parse_early_param();
873
874 arm_memblock_init(&meminfo, mdesc);
875
876 paging_init(mdesc);
877 request_standard_resources(mdesc);
878
879 #ifdef CONFIG_SMP
880 if (is_smp())
881 smp_init_cpus();
882 #endif
883 reserve_crashkernel();
884
885 cpu_init();
886 tcm_init();
887
888 #ifdef CONFIG_MULTI_IRQ_HANDLER
889 handle_arch_irq = mdesc->handle_irq;
890 #endif
891
892 #ifdef CONFIG_VT
893 #if defined(CONFIG_VGA_CONSOLE)
894 conswitchp = &vga_con;
895 #elif defined(CONFIG_DUMMY_CONSOLE)
896 conswitchp = &dummy_con;
897 #endif
898 #endif
899 early_trap_init();
900
901 if (mdesc->init_early)
902 mdesc->init_early();
903 }
以下进行逐行分析:
809行:检测处理器类型,并初始化处理器相关的底层变量。内核启动时的处理器信息(包括cache)就是通过这个函数打印的,例如:
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
810行: mdese 为设备描述结构体,对于任何板子都定义了这样的一个结构体,在内核自解压完成以后内核会首先会进入 bl __lookup_machine_type函数(在arch/arm/kernel/head.S中),检查machine_type是否匹配,如果不匹配会跳入__error_a函数(在arch/arm/kernel/head-common.S中),导致启动失败,失败时串口会停在Uncompressing Linux... done, booting the kernel.对于mini2440
此结构体定义在arch/arm/mach-s3c2440/mach-mini2440.c
677 MACHINE_START(MINI2440, "MINI2440")
678 /* Maintainer: Michel Pollet <buserror@gmail.com> */
679 .boot_params = S3C2410_SDRAM_PA + 0x100,
680 .map_io = mini2440_map_io,
681 .init_machine = mini2440_init,
682 .init_irq = s3c24xx_init_irq,
683 .timer = &s3c24xx_timer,
684 MACHINE_END
用GDB调试,其值为:
(gdb) p mdesc
$1 = (struct machine_desc *) 0xc0022518
(gdb) p *mdesc
$2 = {nr = 1999, name = 0xc038616a "MINI2440", boot_params = 805306624, nr_irqs = 0, video_start = 0,
video_end = 0, reserve_lp0 = 0, reserve_lp1 = 0, reserve_lp2 = 0, soft_reboot = 0, fixup = 0, reserve = 0,
map_io = 0xc000dbfc <mini2440_map_io>, init_early = 0, init_irq = 0xc000dc84 <s3c24xx_init_irq>,
timer = 0xc0401d1c, init_machine = 0xc000d8d8 <mini2440_init>}
835行,tags定义在801行,定义时让它指向init_tags,phys_to_virt()是一个宏,它让tags指向mdesc->boot_params(0xc0000100),也就是BootLoader传递过来的参数的地址
#define phys_to_virt(a) ((void *)(unsigned long)(a))
860-863行,这四个参数来自arch/arm/kernel/vmlinux.lds.S,
这里通过连接脚本中得到的Linux代码位置数据来初始化一个mm_struct结构体init_mm中的部分数据,每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核自身的mm_struct;
872行,解析cmdline,这里只解析了当中的mem部分,内核参数的解析一共有两处,一处是setup_arch()->parse_cmdline()用于解析内核参数中关于内存的部分,另外一处是start_kernel()->parse_option()用于解析其余部分。
874行,在此处按地址数据从小到大排序meminfo中的数据,并初始化全局的memblock数据;
876行,设置内核的参考页表。此页表不仅用于物理内存映射,还用于管理vmalloc区。此函数中非常重要的一点就是初始化了bootmem分配器!对于bootmem会有专门博文进行分析。
877行,通过获取设备描述结构体(struct machine_desc)中的数据和编译时产生的地址数据,初始化内存相关的全局结构体变量。
885行,初始化一个CPU,并设置一个per-CPU栈;
886行,初始化ARM内部的TCM(紧耦合内存);
888-890行,注册irq函数;
892-899行,对中断向量进行早期初始化;