天天看點

Linux核心源碼分析--核心啟動之(4)Image核心啟動(setup_arch函數)(Linux-3.0 ARMv7)

在分析start_kernel函數的時候,其中有構架相關的初始化函數setup_arch。此函數根據構架而異,對于ARM構架的詳細分析如下:

void __init setup_arch(char **cmdline_p)

{

    struct machine_desc *mdesc;

點選(此處)折疊或打開

此為裝置描述結構體,對于任何闆子都定義了這樣的一個結構體,我以前的文章有介紹:

    unwind_init();

初始化基於ARM EABI的Backtrace Unwind機制(棧回退),此函數主要用于位址轉換(arch/arm/kernel/unwind.c)

    setup_processor();

再次檢測處理器類型,并初始化處理器相關的底層變量。核心啟動時的處理器資訊(包括cache)就是通過這個函數列印的,例如:

CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c53c7f

CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache

    mdesc = setup_machine_fdt(__atags_pointer);

    if (!mdesc)

        mdesc = setup_machine_tags(machine_arch_type);

<b>在此處通過bootloader傳遞過來的裝置ID來比對一個 struct machine_desc 結構體</b>

<b>(這個結構體就是在arch/arm/mach-*/mach-*.c中定義的結構體:MACHINE_START~MACHINE_END )</b>

如果沒有比對上就死循環。

如果比對上了就列印機器名 ,并處理bootloader傳遞過來的tagged_list,将所有的tag資訊儲存到相應的全局變量或結構體中。

核心啟動時的機器資訊就是這裡列印的,例如:

Machine: ti8168evm

最後傳回結構體指針。

    machine_desc = mdesc;

    machine_name = mdesc-&gt;name;

通過比對的struct machine_desc 結構體資料,初始化一些全局變量

    if (mdesc-&gt;soft_reboot)

        reboot_setup("s");

通過struct machine_desc 中的soft_reboot資料來設定重新開機類型:

如果存在就為“s”:softreset;如果不存在就為“h”:hardreset

    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;

這裡通過連接配接腳本中得到的Linux代碼位置資料來初始化一個mm_struct結構體init_mm中的部分資料

ps:每一個任務都有一個mm_struct結構以管理記憶體空間,init_mm是核心自身的mm_struct

    /* 同時填充cmd_line以備後用, 保護boot_command_line資料 */

    strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);

    *cmdline_p = cmd_line;

将boot_command_line複制到cmd_line中。<b>這裡關鍵是要知道系統啟動的時候的cmdline是如何傳遞的。</b>

    parse_early_param();

處理在 struct obs_kernel_param 中定義為early的啟動參數(主要是記憶體配置部分的參數)

其中就分析了mem=size@start參數初始化了struct meminfo meminfo;

同時如果有vmalloc=size參數也會初始化 vmalloc_min

這裡需要注意的是核心的cmdline中的參數按照其被需要的先後,分為early和非early的。

include/linux/init.h:

struct obs_kernel_param {

const char *str;            //在cmdline中相應參數名。

int (*setup_func)(char *);  //對于此參數的專用處理函數

int early;                 <b> //是否為早期需要處理的參數</b>

};

兩種不同的參數在核心中用了不同的宏來定義:

early: #define early_param(str, fn) \

        __setup_param(str, fn, fn, <b>1</b>)

非early: #define __setup(str, fn) \

         __setup_param(str, fn, fn, <b>0</b>)

使用這兩個宏定義的參數和構架相關,一些構架或者闆子可以定義自己特定的參數和處理函數。對于比較重要的“men”參數就是early參數。

    sanity_check_meminfo();

在此處設定struct meminfo meminfo中每個bank中的highmem變量,

通過vmalloc_min确定每個bank中的記憶體是否屬于高端記憶體

    arm_memblock_init(&amp;meminfo, mdesc);

在此處按位址資料從小到大排序meminfo中的資料,并初始化全局的memblock資料。

<b>    paging_init(mdesc);</b>

設定核心的參考頁表。

此頁表不僅用于實體記憶體映射,還用于管理vmalloc區。

此函數中非常重要的一點就是初始化了bootmem配置設定器!

    request_standard_resources(mdesc);

通過擷取裝置描述結構體(struct machine_desc)中的資料和編譯時産生的位址資料,<b>初始化記憶體相關的全局結構體變量</b>。

    unflatten_device_tree();

通過啟動參數中的“非平坦裝置樹”資訊(如果有),擷取記憶體相關資訊

#ifdef CONFIG_SMP

    if (is_smp())

        smp_init_cpus();

#endif

針對SMP處理器,初始化可能存在的CPU映射 - 這描述了可能存在的CPU

    reserve_crashkernel();

用于核心崩潰時的保留核心

此功能通過核心command line參數中的"crashkernel="保留下記憶體用于主核心崩潰時擷取核心資訊的導出。 

    cpu_init();

初始化一個CPU,并設定一個per-CPU棧

    tcm_init();

初始化ARM内部的TCM(緊耦合記憶體)。

ARM官網也有介紹文檔

#ifdef CONFIG_MULTI_IRQ_HANDLER

    handle_arch_irq = mdesc-&gt;handle_irq;

調用裝置描述結構體中的mdesc-&gt;handle_irq函數,目的未知。

#ifdef CONFIG_VT

#if defined(CONFIG_VGA_CONSOLE)

    conswitchp = &amp;vga_con;

#elif defined(CONFIG_DUMMY_CONSOLE)

    conswitchp = &amp;dummy_con;

    early_trap_init();

對中斷向量表進行早期初始化

<b>    if (mdesc-&gt;init_early)</b>

<b>        mdesc-&gt;init_early();</b>

如果裝置描述結構體定義了init_early函數(應該是早期初始化之意),則在這裡調用。

}

這個函數主要是檢查處理器的類型是否比對,并擷取處理器資訊來設定處理器的相關底層參數。

static void __init setup_processor(void)

    struct proc_info_list *list;

    /*

     * 在支援處理器清單中定位處理器

   * 連接配接器為我們建立這個清單,從                                                                              * <b>arch/arm/mm/proc-*.S</b>中的入口

    */

    list = lookup_processor_type(read_cpuid_id());

    if (!list) {

        printk("CPU configuration botched (ID %08x), unable "

         "to continue.\n", read_cpuid_id());

        while (1);

    }

這裡再次核對處理器類型,雖然這個已經在彙編代碼中執行過一遍了

    cpu_name = list-&gt;cpu_name;

#ifdef MULTI_CPU

    processor = *list-&gt;proc;

#ifdef MULTI_TLB

    cpu_tlb = *list-&gt;tlb;

#ifdef MULTI_USER

    cpu_user = *list-&gt;user;

#ifdef MULTI_CACHE

    cpu_cache = *list-&gt;cache;

通過從struct proc_info_list擷取的資料初始化CPU相關的全局變量

    printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",

     cpu_name, read_cpuid_id(), read_cpuid_id() &amp; 15,

     proc_arch[cpu_architecture()], cr_alignment);

列印核心啟動時的處理器資訊

    sprintf(init_utsname()-&gt;machine, "%s%c", list-&gt;arch_name, ENDIANNESS);

    sprintf(elf_platform, "%s%c", list-&gt;elf_name, ENDIANNESS);

    elf_hwcap = list-&gt;elf_hwcap;

#ifndef CONFIG_ARM_THUMB

    elf_hwcap &amp;= ~HWCAP_THUMB;

    feat_v6_fixup();

針對特定的ARM核軟體屏蔽一些功能

    cacheid_init();

初始化ARM核中的緩存

    cpu_proc_init();

宏:

#define cpu_proc_init __glue(CPU_NAME,_proc_init)

意在調用處理器特定的初始化函數。

繼續閱讀