新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Arm linux 內核移植及系統初始化過程分析

        Arm linux 內核移植及系統初始化過程分析

        作者: 時間:2016-11-09 來源:網絡 收藏
        本文主要介紹內核移植過程中涉及文件的分布及其用途,以及簡單介紹系統的初始化過程。整個arm linux內核的啟動可分為三個階段:第一階段主要是進行cpu和體系結構的檢查、cpu本身的初始化以及頁表的建立等;第二階段主要是對系統中的一些基礎設施進行初始化;最后則是更高層次的初始化,如根設備和外部設備的初始化。了解系統的初始化過程,有益于更好地移植內核。

        1. 內核移植2. 涉及文件分布介紹
        2.1. 內核移植2.2. 涉及的頭文件
        /linux-2.6.18.8/include
        [root@localhost include]# tree -L 1
        .
        |-- Kbuild
        |-- acpi
        |-- asm -> asm-arm
        |-- asm-alpha
        |-- asm-arm ------------------------------->(1)
        |-- asm-sparc
        |-- asm-sparc64
        |-- config
        |-- keys
        |-- linux ------------------------------->(2)
        |-- math-emu
        |-- media
        |-- mtd
        |-- net
        |-- pcmcia
        |-- rdma
        |-- rxrpc
        |-- scsi
        |-- sound
        `-- video

        本文引用地址:http://www.104case.com/article/201611/317956.htm

        內核移植過程中涉及到的頭文件包括處理器相關的頭文件(1)和處理器無關的頭文件(2)。

        2.3. 內核移植2.4. 涉及的源文件
        /linux-2.6.18.8/arch/arm
        [root@localhost arm]# tree -L 1
        .
        |-- Kconfig
        |-- Kconfig-nommu
        |-- Kconfig.debug
        |-- Makefile
        |-- boot ------------------------------->(2)
        |-- common
        |-- configs
        |-- kernel ------------------------------->(3)
        |-- lib
        |-- mach-at91rm9200
        ……
        |-- mach-omap1
        |-- mach-omap2
        |-- mach-realview
        |-- mach-rpc
        |-- mach-s3c2410 ------------------------------->(4)
        |-- mach-sa1100
        |-- mach-versatile
        |-- mm ------------------------------->(5)
        |-- nwfpe
        |-- oprofile
        |-- plat-omap
        |-- tools ------------------------------->(1)
        `-- vfp

        (1)
        /linux-2.6.18.8/arch/arm/tools
        [root@localhost tools]# tree -L 1
        .
        |-- Makefile
        |-- gen-mach-types
        `-- mach-types

        Mach-types 文件定義了不同系統平臺的系統平臺號。移植linux內核到新的平臺上需要對新的平臺登記系統平臺號。

        Mach-types文件格式如下:
        # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
        s3c2410 ARCH_S3C2410 S3C2410 182
        smdk2410 ARCH_SMDK2410 SMDK2410 193

        之所以需要這些信息,是因為腳本文件linux/arch/arm/tools/gen-mach-types需要linux/arch/tools/mach-types來產生linux/include/asm-arm/mach-types.h文件,該文件中設置了一些宏定義,需要這些宏定義來為目標系統選擇合適的代碼。

        (2)
        linux-2.6.18.8/arch/arm/boot/compressed
        [root@localhost compressed]# tree -L 1
        .
        |-- Makefile
        |-- Makefile.debug
        |-- big-endian.S
        |-- head-at91rm9200.S
        |-- head.S
        |-- ll_char_wr.S
        |-- misc.c
        |-- ofw-shark.c
        |-- piggy.S
        `-- vmlinux.lds.in

        Head.s 是內核映像的入口代碼,是自引導程序。自引導程序包含一些初始化程序,這些程序都是體系結構相關的。在對系統作完初始化設置工作后,調用misc.c文件中的decompress_kernel()函數解壓縮內核映像到指定的位置,然后跳轉到kernel的入口地址。

        Vmlinux.lds.in用來生成內核映像的內存配置文件。

        (3)
        linux-2.6.18.8/arch/arm/kernel
        [root@localhost kernel]# tree -L 1
        .
        |-- Makefile
        |-- apm.c
        |-- armksyms.c
        |-- arthur.c
        |-- asm-offsets.c
        |-- bios32.c
        |-- calls.S
        |-- dma.c
        |-- ecard.c
        |-- entry-armv.S
        |-- entry-common.S
        |-- entry-header.S
        |-- fiq.c
        |-- head-common.S
        |-- head-nommu.S
        |-- head.S
        |-- init_task.c
        |-- io.c
        |-- irq.c
        |-- isa.c
        |-- module.c
        |-- process.c
        |-- ptrace.c
        |-- ptrace.h
        |-- semaphore.c
        |-- setup.c
        |-- smp.c
        |-- sys_arm.c
        |-- time.c
        |-- traps.c
        `-- vmlinux.lds.S

        內核入口處也是由一段匯編語言實現的,由head.s和head-common.s兩個文件組成。
        Head.s 是內核的入口文件, 在head.s的末尾處 #include "head-common.S"。 經過一系列的初始化后,跳轉到linux-2.6.18.8/init/main.c中的start_kernel()函數中,開始內核的基本初始化過程。


        /linux-2.6.18.8/init
        [root@localhost init]# tree
        .
        |-- Kconfig
        |-- Makefile
        |-- calibrate.c
        |-- do_mounts.c
        |-- do_mounts.h
        |-- do_mounts_initrd.c
        |-- do_mounts_md.c
        |-- do_mounts_rd.c
        |-- initramfs.c
        |-- main.c
        `-- version.c

        (4)
        /linux-2.6.18.8/arch/arm/mach-s3c2410
        [root@localhost mach-s3c2410]# tree -L 1
        .
        |-- Kconfig
        |-- Makefile
        |-- Makefile.boot
        |-- bast-irq.c
        |-- bast.h
        |-- clock.c
        |-- clock.h
        |-- common-smdk.c
        |-- common-smdk.h
        |-- cpu.c
        |-- cpu.h
        |-- devs.c
        |-- devs.h
        |-- dma.c
        |-- gpio.c
        |-- irq.c
        |-- irq.h
        |-- mach-anubis.c
        |-- mach-smdk2410.c
        |-- pm-simtec.c
        |-- pm.c
        |-- pm.h
        |-- s3c2400-gpio.c
        |-- s3c2400.h
        |-- s3c2410-clock.c
        |-- s3c2410-gpio.c
        |-- s3c2410.c
        |-- s3c2410.h
        |-- sleep.S
        |-- time.c
        |-- usb-simtec.c
        `-- usb-simtec.h

        這個目錄中的文件都是板級相關的,其中比較重要是如下幾個:
        linux/arch/arm/mach-s3c2410/cpu.c
        linux/arch/arm/mach-s3c2410/common-smdk.c
        linux/arch/arm/mach-s3c2410/devs.c
        linux/arch/arm/mach-s3c2410/mach-smdk2410.c
        linux/arch/arm/mach-s3c2410/Makefile.boot
        linux/arch/arm/mach-s3c2410/s3c2410.c

        3. 處理器和設備4.
        這里主要介紹處理器和設備的描述和操作過程。設備描述在linux/arch/arm/mach-s3c2410/devs.c和linux/arch/arm/mach-s3c2410/common-smdk.c中實現。最后以nand flash為例具體介紹。
        4.1. 處理器、設備4.2. 描述
        設備描述主要兩個結構體完成:struct resource和struct platform_device。
        先來看看著兩個結構體的定義:
        struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
        };

        Resource結構體主要是描述了設備在系統中的起止地址、名稱、標志以及為了鏈式描述方便指向本結構體類型的指針。Resource定義的實例將被添加到platform_device結構體對象中去。

        struct platform_device {
        const char * name;
        u32 id;
        struct device dev;
        u32 num_resources;
        struct resource * resource;
        };

        Platform_device結構體包括結構體的名稱、ID號、平臺相關的信息、設備的數目以及上面定義的resource信息。Platform_device結構對象將被直接通過設備操作函數注冊導系統中去。具體注冊和注銷過程在下一節介紹。

        4.3. 處理器、設備4.4. 操作
        (1) int platform_device_register(struct platform_device * pdev); 注冊設備
        (2) void platform_device_unregister(struct platform_device * pdev); 注銷設備
        (3) int platform_add_devices(struct platform_device devs, int num);添加設備,通過調用上面兩個函數實現。
        4.5. 添加Nand flash設備4.6.
        下面以nand flash 設備的描述為例,具體介紹下設備的描述和注冊過程。

        // resource結構體實例s3c_nand_resource 對nand flash 控制器描述,包括控制器的起止地址和標志。
        static struct resource s3c_nand_resource[] = {
        [0] = {
        .start = S3C2410_PA_NAND,
        .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
        .flags = IORESOURCE_MEM,
        }
        };

        //platform_device結構體實例s3c_device_nand定義了設備的名稱、ID號并把resource對象作為其成員之一。
        struct platform_device s3c_device_nand = {
        .name = "s3c2410-nand",
        .id = -1,
        .num_resources = ARRAY_SIZE(s3c_nand_resource),
        .resource = s3c_nand_resource,
        };

        // nand flash 的分區情況,由mtd_partition結構體定義。
        static struct mtd_partition smdk_default_nand_part[] = {
        [0] = {
        .name = "Boot Agent",
        .size = SZ_16K,
        .offset = 0,
        },
        [1] = {
        .name = "S3C2410 flash partition 1",
        .offset = 0,
        .size = SZ_2M,
        },
        [2] = {
        .name = "S3C2410 flash partition 2",
        .offset = SZ_4M,
        .size = SZ_4M,
        },
        [3] = {
        .name = "S3C2410 flash partition 3",
        .offset = SZ_8M,
        .size = SZ_2M,
        },
        [4] = {
        .name = "S3C2410 flash partition 4",
        .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() functions
        // for example init_machine pointer is initialized with smdk_machine_init() function , and //init_machine() function is called by customize_machine(), and the function is processed by //arch_initcall(fn). Therefore smdk_machine_init() is issured. by edwin
        setup_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 functioning 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. Were enabling the console before
        * weve 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_INITRD
        if (initrd_start && !initrd_below_start_ok &&
        initrd_start < min_low_pfn << PAGE_SHIFT) {
        printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
        "disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT);
        initrd_start = 0;
        }
        #endif
        vfs_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_X86
        if (efi_enabled)
        efi_enter_virtual_mode();
        #endif
        fork_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_FS
        proc_root_init();
        #endif
        cpuset_init();
        taskstats_init_early();
        delayacct_init();

        check_bugs();

        acpi_early_init(); /* before LAPIC and SMP init */

        /* Do the rest non-__inited, were 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.c
        do_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 edwin
        machine_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);
        saved_command_line[COMMAND_LINE_SIZE-1] = /0;
        parse_cmdline(cmdline_p, from);
        paging_init(&meminfo, mdesc);
        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
        }
        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 were going to be the grim
        * reaper of innocent orphaned children.
        *
        * We dont 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進程
        */

        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
        * were 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) < 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.c
        void __init populate_rootfs(void)
        {
        char *err = unpack_to_rootfs(__initramfs_start,
        __initramfs_end - __initramfs_start, 0);
        if (err)
        panic(err);
        #ifdef CONFIG_BLK_DEV_INITRD
        if (initrd_start) {
        #ifdef CONFIG_BLK_DEV_RAM
        int 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 isnt (%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();
        }
        #else
        printk(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.c
        static void __init do_basic_setup(void)
        {
        /* drivers will send hotplug events */
        init_workqueues();
        usermodehelper_init();
        driver_init(); /* 初始化驅動程序模型。調用驅動初始化函數初始化子系統。 */

        #ifdef CONFIG_SYSCTL
        sysctl_init();
        #endif

        do_initcalls();
        }


        linux/init/main.c
        extern initcall_t __initcall_start[], __initcall_end[];

        static void __init do_initcalls(void)
        {
        initcall_t *call;
        int count = preempt_count();

        for (call = __initcall_start; call < __initcall_end; call++) {
        char *msg = NULL;
        char msgbuf[40];
        int result;

        if (initcall_debug) {
        printk("Calling initcall 0x%p", *call);
        print_fn_descriptor_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__ /
        __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.c
        static int __init v6_userpage_init(void) linux/arch/arm/mm/copypage-v6.c
        static int __init init_dma(void) linux/arch/arm/kernel/dma.c
        static int __init s3c2410_core_init(void) linux/arch/arm/mach-s3c2410/s3c2410.c

        postcore_initcall(fn)
        static int ecard_bus_init(void) linux/arch/arm/kernel/ecard.c

        arch_initcall(fn)
        static __init int bast_irq_init(void) linux/arch/arm/mach-s3c2410/bast-irq.c
        static int __init s3c_arch_init(void) linux/arch/arm/mach-s3c2410/cpu.c
        static __init int pm_simtec_init(void) linux/arch/arm/mach-s3c2410/pm-simtec.c
        static int __init customize_machine(void) linux/arch/arm/kernel/setup.c

        subsys_initcall(fn)
        static int __init ecard_init(void) linux/arch/arm/kernel/ecard.c
        int __init scoop_init(void) linux/arch/arm/common/scoop.c
        static int __init topology_init(void) linux/arch/arm/kernel/setup.c

        fs_initcall(fn)
        static int __init alignment_init(void) linux/arch/arm/mm/alignment.c

        device_initcall(fn)
        static int __init leds_init(void) linux/arch/arm/kernel/time.c
        static int __init timer_init_sysfs(void) linux/arch/arm/kernel/time.c

        late_initcall(fn)
        static int __init crunch_init(void) arch/arm/kernel/crunch.c
        static int __init arm_mrc_hook_init(void) linux/arch/arm/kernel/traps.c

        5.3.4. 啟動用戶空間的程序



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 五家渠市| 阜城县| 舞阳县| 沾化县| 景洪市| 昔阳县| 崇礼县| 惠来县| 行唐县| 随州市| 吴旗县| 新源县| 视频| 清水县| 平遥县| 库车县| 太白县| 龙州县| 双牌县| 福建省| 通辽市| 梅河口市| 东港市| 古浪县| 方正县| 宕昌县| 寻乌县| 台湾省| 泽库县| 奉新县| 海淀区| 汝阳县| 南部县| 福泉市| 勐海县| 双江| 克山县| 分宜县| 洛阳市| 虞城县| 光山县|