浅谈分析Arm linux 内核移植及系统初始化的过程(二)3浅谈分析Arm linux 内核移植及系统初始化的过程。咨询QQ:313807838 .offset = SZ_1M * 10,.size = SZ_4M,},[5] = {.name = "S3C2410 flash partition 5",.offset = SZ_1M * 14,.size = SZ_1M * 10,},[6] = {.name = "S3C2410 flash partition 6",.offset = SZ_1M * 24,.size = SZ_1M * 24,},[7] = {.name = "S3C2410 flash partition 7",.offset = SZ_1M * 48,.size = SZ_16M,}};static struct s3c2410_nand_set smdk_nand_sets[] = {[0] = {.name = "NAND",.nr_chips = 1,.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),.partitions = smdk_default_nand_part,},};/* choose a set of timings which should suit most 512Mbit * chips and beyond.*/static struct s3c2410_platform_nand smdk_nand_info = {.tacls = 20,.twrph0 = 60,.twrph1 = 20,.nr_sets = ARRAY_SIZE(smdk_nand_sets),.sets = smdk_nand_sets,};/* devices we initialise */// 最后将nand flash 设备加入到系统即将注册的设备集合中。 static struct platform_device __initdata *smdk_devs[] = {&s3c_device_nand,&smdk_led4,&smdk_led5,&smdk_led6,&smdk_led7,};然后通过smdk_machine_init()函数,调用设备添加函数platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)) 完成设备的注册。具体过程参见系统初始化的相关部分。5. 系统初始化5.1. 系统初始化的主干线Start_kernel() èsetup_arch() èreset_init() è kernel_thread(init …) è init() è do_basic_setup() èdriver_init() è do_initcall()Start_kernel()函数负责初始化内核各个子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。Start_kernel()函数在init/main.c中实现。asmlinkage void __init start_kernel(void){char * command_line;extern struct kernel_param __start___param[], __stop___param[];smp_setup_processor_id();/* * Need to run as early as possible, to initialize the * lockdep hash: */lockdep_init();local_irq_disable();early_boot_irqs_off();early_init_irq_lock_class();/* * Interrupts are still disabled. Do necessary setups, then * enable them */lock_kernel();boot_cpu_init();page_address_init();printk(KERN_NOTICE);printk(linux_banner);setup_arch(&command_line); //setup processor and machine and destinate some pointers for do_initcalls() s5、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838 //for example init_machine pointer is initialized withsmdk_machine_init() , and //init_machine() is called bycustomize_machine(), and the is processed by //arch_initcall(fn).Therefore smdk_machine_init() is issured. by edwinsetup_per_cpu_areas();smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks *//* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() * time - but meanwhile we still have a ing scheduler. */sched_init();/* * Disable preemption - early bootup scheduling is extremely * fragile until we cpu_idle() for the first time. */preempt_disable();build_all_zonelists();page_alloc_init();printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line);parse_early_param();parse_args("Booting kernel", command_line, __start___param, __stop___param - __start___param, &unknown_bootoption);sort_main_extable();unwind_init();trap_init();rcu_init();init_IRQ();pidhash_init();init_timers();hrtimers_init();softirq_init();timekeeping_init();time_init();profile_init();if (!irqs_disabled())printk("start_kernel(): bug: interrupts were enabled early\n");early_boot_irqs_on();local_irq_enable();/* * HACK ALERT! This is early. We're enabling the console before * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */console_init();if (panic_later)panic(panic_later, panic_param);lockdep_info();/* * Need to run this when irqs are enabled, because it wants * to self-test [hard/soft]-irqs on/off lock inversion bugs * too: */locking_selftest();#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start && !initrd_below_start_ok &&initrd_start printk(KERN_CRIT "initrd overwritten (0x%08lx 6、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838 "disabling it.\n",initrd_start,min_low_pfn initrd_start = 0;}#endifvfs_caches_init_early();cpuset_init_early();mem_init();kmem_cache_init();setup_per_cpu_pageset();numa_policy_init();if (late_time_init)late_time_init();calibrate_delay();pidmap_init();pgtable_cache_init();prio_tree_init();anon_vma_init();#ifdef CONFIG_X86if (efi_enabled)efi_enter_virtual_mode();#endiffork_init(num_physpages);proc_caches_init();buffer_init();unnamed_dev_init();key_init();security_init();vfs_caches_init(num_physpages);radix_tree_init();signals_init();/* rootfs populating might need page-writeback */page_writeback_init();#ifdef CONFIG_PROC_FSproc_root_init();#endifcpuset_init();taskstats_init_early();delayacct_init();check_bugs();acpi_early_init(); /* before LAPIC and SMP init *//* Do the rest non-__init'ed, we're now alive */rest_init();}分析start_kernel()源码, 其中setup_arch() 和 reset_init()是两个比较关键的函数。下面将具体分析这两个函数。5.2. setup_arch()函数分析首先我们来分析下setup_arch()函数。Setup_arch()函数主要工作是安装cpu和machine,并为start_kernel()后面的初始化函数指针指定值。其中setup_processor()函数调用linux/arch/arm/kernel/head_common.S 中的lookup_processor_type函数查询处理器的型号并安装。Setup_machine()函数调用inux/arch/arm/kernel/head_common.S中的lookup_machine_type(__machine_arch_type)函数根据体系结构号__machine_arch_type,在__arch_info_begin和__arch_info_end段空间查询体系结构。问题是__machine_arch_type是在什么时候赋的初值?__arch_info_begin和__arch_info_end段空间到底放的是什么内容?__machine_arch_type是一个全局变量,在linux/boot/decompress/misc.c的解压缩函数中得以赋值。decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, int arch_id){__machine_arch_type = arch_id;}__arch_info_begin和__arch_info_end段空间到底放的内容由链接器决定,存放是.arch.info.init段的内容。这个段是通过段属性__attribute__指定的。Grep一下.arch.info.init得到./include/asm/mach/arch.h:53:__attribute__((__section__(".arch.info.init"))) = { \在linux/include/asm-arm/mach/arch.h 中发现MACHINE_START宏定义。#define MACHINE_START(_type,_name) \static const struct machine_desc __mach_desc_##_type \ __attribute_used__ \ __attribute__((__section__(".arch.info.init"))) = { \.nr = MACH_TYPE_##_type, \.name = _name,#define MACHINE_END \};inux/arch/arm/mach-s3c2410/mach-smdk2410.c中对.arch.info.init段的初始化如下。 MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch * to SMDK2410 *//* Maintainer: Jonas Dietsche */.phys_io = S3C2410_PA_UART,.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,.boot_params = S3C2410_SDRAM_PA + 0x100,.map_io = smdk2410_map_io,.init_irq = s3c24xx_init_irq,.init_machine = smdk_machine_init,.timer = &s3c24xx_timer,MACHINE_END由此可见在.arch.info.init段内存放了__desc_mach_desc_SMDK2410结构体。初始化了相应的初始化函数指针。问题又来了, 这些初始化指针函数是什么时候被调用的呢?分析发现,不一而同。如s3c24xx_init_irq()函数是通过start_kernel()里的init_IRQ()函数调用init_arch_irq()实现的。因为在MACHINE_START结构体中 .init_irq =s3c24xx_init_irq,而在setup_arch()函数中init_arch_irq = mdesc->init_irq,所以调用init_arch_irq()就相当于调用了s3c24xx_init_irq()。又如smdk_machine_init()函数的初始化。在MACHINE_START结构体中,函数指针赋值,.init_machine =smdk_machine_init。而init_machine()函数被linux/arch/arm/kernel/setup.c文件中的customize_machine()函数调用并被arch_initcall(Fn)宏处理,arch_initcall(customize_machine)。被arch_initcall(Fn)宏处理过函数将linux/init/main.cdo_initcalls()函数调用。 具体参看下边的部分。void __init setup_arch(char **cmdline_p){struct tag *tags = (struct tag *)&init_tags;struct machine_desc *mdesc;char *from = default_command_line;setup_processor();mdesc = setup_machine(machine_arch_type);//machine_arch_type =SMDK2410 by edwinmachine_name = mdesc->name;if (mdesc->soft_reboot)reboot_setup("s");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);if (tags->hdr.tag == ATAG_CORE) {if (meminfo.nr_banks != 0)squash_mem_tags(tags);parse_tags(tags);}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;memcpy(saved_command_line, from, COMMAND_LINE_SIZE); 8、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838 saved_command_line[COMMAND_LINE_SIZE-1] = '\0';parse_cmdline(cmdline_p, from);paging_init(&meminfo, mdesc);request_standard_resources(&meminfo, mdesc);#ifdef CONFIG_SMPsmp_init_cpus();#endifcpu_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}5.3. rest_init()函数分析下面我们来分析下rest_init()函数。Start_kernel()函数负责初始化内核各子系统,最后调用reset_init(),启动一个叫做init的内核线程,继续初始化。在init内核线程中,将执行下列init()函数的程序。Init()函数负责完成根文件系统的挂接、初始化设备驱动程序和启动用户空间的init进程等重要工作。static void noinline rest_init(void)__releases(kernel_lock){kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);numa_default_policy();unlock_kernel();/* * The boot idle thread must execute schedule() * at least one to get things moving: */preempt_enable_no_resched();schedule();preempt_disable();/* Call into cpu_idle with preempt disabled */cpu_idle();}static int init(void * unused){lock_kernel();/* * init can run on any cpu. */set_cpus_allowed(current, CPU_MASK_ALL);/* * Tell the world that we're going to be the grim * reaper of innocent orphaned children. * * We don't want people to have to make incorrect * assumptions about where in the task array this * can be found. */child_reaper = current;smp_prepare_cpus(max_cpus);do_pre_smp_initcalls();smp_init();sched_init_smp();cpuset_init_smp();/* * Do this before initcalls, because some drivers want to access * firmware files. */populate_rootfs(); //挂接根文件系统do_basic_setup(); //初始化设备驱动程序/* * check if there is an early userspace init. If yes, let it do all * the work //启动用户空间的init进程 9、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838 */if (!ramdisk_execute_command)ramdisk_execute_command = "/init";if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {ramdisk_execute_command = NULL;prepare_namespace();}/* * Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. */free_initmem();unlock_kernel();mark_rodata_ro();system_state = SYSTEM_RUNNING;numa_default_policy();if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) printk(KERN_WARNING "Warning: unable to open an initial console.\n");(void) sys_dup(0);(void) sys_dup(0);if (ramdisk_execute_command) {run_init_process(ramdisk_execute_command);printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);}/* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */if (execute_command) {run_init_process(execute_command);printk(KERN_WARNING "Failed to execute %s. Attempting ""defaults...\n", execute_command);}run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");panic("No init found. Try passing init= option to kernel.");}5.3.1. 挂接根文件系统Linux/init/ramfs.cvoid __init populate_rootfs(void){char *err = unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start, 0);if (err)panic(err);#ifdef CONFIG_BLK_DEV_INITRDif (initrd_start) {#ifdef CONFIG_BLK_DEV_RAMint fd;printk(KERN_INFO "checking if image is initramfs...");err = unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 1);if (!err) {printk(" it is\n");unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 0);free_initrd();return;}printk("it isn't (%s); looks like an initrd\n", err); fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);if (fd >= 0) {sys_write(fd, (char *)initrd_start,initrd_end - initrd_start);sys_close(fd);free_initrd();}#elseprintk(KERN_INFO "Unpacking initramfs...");err = unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 0);if (err)panic(err);printk(" done\n");free_initrd();#endif}#endif}5.3.2. 初始化设备5.3.3. 驱动程序linux/init/main.cstatic void __init do_basic_setup(void){/* drivers will send hotplug events */init_workqueues();usermodehelper_init();driver_init(); /* 初始化驱动程序模型。调用驱动初始化函数初始化子系统。 */#ifdef CONFIG_SYSCTLsysctl_init();#endifdo_initcalls();}linux/init/main.cextern initcall_t __initcall_start[], __initcall_end[];static void __init do_initcalls(void){initcall_t *call;int count = preempt_count();for (call = __initcall_start; call char *msg = NULL;char msgbuf[40];int result;if (initcall_debug) {printk("Calling initcall 0x%p", *call);print_fn_deor_symbol(": %s()",(unsigned long) *call);printk("\n");}result = (*call)();………………}/* Make sure there is no pending stuff from the initcall sequence */flush_scheduled_work();}分析上面一段代码可以看出,设备的初始化是通过do_basic_setup()函数调用do_initcalls()函数,实现__initcall_start,__initcall_end段之间的指针函数执行的。而到底是那些驱动函数怎么会被集中到这个段内的呢?我们知道系统内存空间的分配是由链接器ld读取链接脚本文件决定。链接器将同样属性的文件组织到相同的段里面去,如所有的.text段都被放在一起。在链接脚本里面可以获得某块内存空间的具体地址。我们来看下linux-2.6.18.8\arch\arm\kernel\vmlinux.lds.S文件。由于文件过长,只贴出和__initcall_start, __initcall_end相关的部分。__initcall_start = .;*(.initcall1.init)*(.initcall2.init)*(.initcall3.init)*(.initcall4.init)*(.initcall5.init)*(.initcall6.init)*(.initcall7.init)__initcall_end = .;从脚本文件中我们可以看出, 在__initcall_start,__initcall_end之间放置的是属行为(.initcall*.init)的函数数据。在linux/include/linux/init.h文件中可以知道,(.initcall*.init)属性是由__define_initcall(level, fn)宏设定的。#define __define_initcall(level,fn) \static initcall_t __initcall_##fn __attribute_used__ \ 11、浅谈分析Arm linux 内核移植及系统初始化的过程 咨询QQ:313807838 __attribute__((__section__(".initcall" level ".init"))) = fn#define core_initcall(fn) __define_initcall("1",fn)#define postcore_initcall(fn) __define_initcall("2",fn)#define arch_initcall(fn) __define_initcall("3",fn)#define subsys_initcall(fn) __define_initcall("4",fn)#define fs_initcall(fn) __define_initcall("5",fn)#define device_initcall(fn) __define_initcall("6",fn)#define late_initcall(fn) __define_initcall("7",fn)#define __initcall(fn) device_initcall(fn)由此可以判断,所有的设备驱动函数都必然通过*_initcall(fn)宏的处理。以此为入口,可以查询所有的设备驱动。core_initcall(fn)static int __init consistent_init(void) linux/arch/arm/mm/consistent.cstatic int __init v6_userpage_init(void) linux/arch/arm/mm/copypage-v6.cstatic int __init init_dma(void) linux/arch/arm/kernel/dma.cstatic int __init s3c2410_core_init(void) linux/arch/arm/mach-s3c2410/s3c2410.cpostcore_initcall(fn)static int ecard_bus_init(void) linux/arch/arm/kernel/ecard.carch_initcall(fn)static __init int bast_irq_init(void) linux/arch/arm/mach-s3c2410/bast-irq.cstatic int __init s3c_arch_init(void) linux/arch/arm/mach-s3c2410/cpu.cstatic __init int pm_simtec_init(void) linux/arch/arm/mach-s3c2410/pm-simtec.cstatic int __init customize_machine(void) linux/arch/arm/kernel/setup.csubsys_initcall(fn)static int __init ecard_init(void) linux/arch/arm/kernel/ecard.cint __init scoop_init(void) linux/arch/arm/common/scoop.cstatic int __init topology_init(void) linux/arch/arm/kernel/setup.cfs_initcall(fn)static int __init alignment_init(void) linux/arch/arm/mm/alignment.cdevice_initcall(fn)static int __init leds_init(void) linux/arch/arm/kernel/time.cstatic int __init timer_init_sysfs(void) linux/arch/arm/kernel/time.clate_initcall(fn)static int __init crunch_init(void) arch/arm/kernel/crunch.cstatic int __init arm_mrc_hook_init(void) linux/arch/arm/kernel/traps.c