天天看點

基于FL2440的3.6.6核心移植出現Uncompressing Linux... done, booting the kernel.1.具體問題:2. 參考解決方案:3.解決思路4.深入解決

  • 具體問題
  • 參考解決方案
  • 解決思路
  • 深入解決

1.具體問題:

在移植3.6.6的核心後,下載下傳啟動卡死,具體是序列槽列印資訊停留在“Uncompressing Linux… done, booting the kernel.”

确認了移植是正常的,肯定沒有錯誤。

2. 參考解決方案:

依據網上的說法要確定如下情況:

  • 2.1 核心的時鐘頻率正确
  • 2.2 boot和kerel 配置一緻的MACH_TYPE,即闆子MACHINE ID
  • 2.3 序列槽驅動配置正常

    在核心配置device drivers->character devices->serial 中

    <*> Samsung SoC serial support

    [*] Support for console on Samsung SoC serial port

  • 2.4. 啟動參數設定正常

    console 的ttySAC0 ,波特率115200對應正确

3.解決思路

檢查了前面三項都是OK的,剛開始以為是核心無法啟動,後來将顯示驅動移植成功後(其它沒變)下載下傳到開發闆,發現啟動資訊同樣也無法通過序列槽列印到minicom或其它dnw視窗中,但在開發闆的LCD上卻列印出了核心的啟動資訊。由此我知道了核心移植是成功的,隻是沒有通過序列槽列印出來,那就是序列槽設定不對了,可檢查發現bootloader的console這些設定都是對的,突然想起來核心裡也可以設定啟動參數,試了下果然成功了(在Boot options —> Default kernel command string 中,填上console=ttySAC0,115200等啟動參數就行了),但按理說也可以通過bootloader傳遞呀?為何這裡卻不行?

4.深入解決

由于以前移植2.6.35版本的核心時,在改變啟動參數時都是從bootloader裡設定的而忽略了核心也可以設定啟動參數,當時弄2.6.35以前的版本都不需要設定這裡,而且我在更改NFS啟動時核心設定的參數也不起作用,就以為此項沒用,是以就沒考慮到這,實際原因是2.6.35核心配置裡要選中always use default…這個才會使用核心配置的啟動參數,不然的話由于飛淩的bootloader裡預設設了一些啟動參數,也就是說無論你在bootloader裡設沒設,它實質上都設定了,是以核心就用外來設定的啟動參數覆寫了kernel裡預設的啟動參數。但3.6.6的版本在這方面可能是有改進,在核心boot option配置中可以看到如下Kernel command line type:

(X) Use bootloader kernel arguments if available         
        ( ) Extend bootloader kernel arguments             
        ( ) Always use the default kernel command string 
           

而在2.6.35的版本中隻有一個

Always use the default kernel command string

可選項,是以确實有改進

,是以從此處看也有可能是我的bootloader與核心在傳遞啟動參數時有問題(bootloader啟動參數的位址在核心中沒有設定好)導緻無法把bootloader裡設定的參數傳遞給核心,造成核心找不到正确的序列槽進而無法列印核心資訊,是以核心從bootloader中沒有擷取到可用參數就使用核心設定的啟動參數了。經測試,無論我怎麼改變bootloader裡的啟動參數都沒效果,應該是核心中的bootloader傳參位址設定有問題,如果無法從bootloader設定那每次更換都要編譯核心,太麻煩,有沒有解決方法呢?

首先了解下 kernel啟動參數傳遞方式

了解核心啟動參數可以參考

文章1

文章2

或自行百度

據此我明白了,kernel啟動參數有兩種,一種是通過核心設定預設啟動參數中,一種是從某段記憶體中讀取進來取代核心啟動參數(而這個設定是由bootloader來設定并儲存在某段事先與核心協商好的記憶體位址中),在啟動時如果在“Kernel command line type”選中的是第一項,就先去尋找這段記憶體看有沒有參數,沒有就直接使用核心預設啟動參數,有就覆寫掉核心設定的。(在查錯Uncompressing linux…done,boot這種卡死的問題時,如果确認其它都OK還是無法啟動,那就直接選中永遠使用核心參數起動,這樣可以檢查是否為Bootloader的問題)

那麼核心是如何知道bootloader将啟動參數儲存在哪裡的呢?

看了下2.6.35版裡的

/arch/arm/mach-s3c24xx/mach-smdk2440.c

在最後的MACHINE_START中有個

boot_params

,好了猜測就是它了,但在3.6.6中加入會提示錯誤,那應該是改了名之類的,先尋找下MACHINE_START的定義在檔案

arch/arm/include/asm/mach/arch.h中

struct machine_desc {
+---   lines: Note! The first four elements are used--------------------------------------------------------
    unsigned int        nr;     /* architecture number  */
    unsigned int        phys_io;    /* start of physical io */
    unsigned int        io_pg_offst;    /* byte offset for io 
                         * page tabe entry  */

    const char      *name;      /* architecture name    */
    unsigned long       boot_params;    /* tagged list      */

    unsigned int        video_start;    /* start of video RAM   */
    unsigned int        video_end;  /* end of video RAM */

    unsigned int        reserve_lp0 :; /* never has lp0    */
    unsigned int        reserve_lp1 :; /* never has lp1    */
    unsigned int        reserve_lp2 :; /* never has lp2    */
    unsigned int        soft_reboot :; /* soft reboot      */
    void            (*fixup)(struct machine_desc *,
                     struct tag *, char **,
                     struct meminfo *);
    void            (*map_io)(void);/* IO mapping function  */
    void            (*init_irq)(void);
    struct sys_timer    *timer;     /* system tick timer    */
    void            (*init_machine)(void);
};
           

可以看到

boot_params;

那我們到3.6.6版中去看看,果然更換了如下

struct machine_desc {
    unsigned int        nr;     /* architecture number  */
    const char      *name;      /* architecture name    */
    unsigned long       atag_offset;    /* tagged list (relative) */
    const char *const   *dt_compat; /* array of device tree
                         * 'compatible' strings */

    unsigned int        nr_irqs;    /* number of IRQs */

#ifdef CONFIG_ZONE_DMA
    unsigned long       dma_zone_size;  /* size of DMA-able area */
#endif

    unsigned int        video_start;    /* start of video RAM   */
    unsigned int        video_end;  /* end of video RAM */

    unsigned char       reserve_lp0 :; /* never has lp0    */
    unsigned char       reserve_lp1 :; /* never has lp1    */
    unsigned char       reserve_lp2 :; /* never has lp2    */
    char            restart_mode;   /* default restart mode */
    void            (*fixup)(struct tag *, char **,
                     struct meminfo *);
    void            (*reserve)(void);/* reserve mem blocks  */
    void            (*map_io)(void);/* IO mapping function  */
    void            (*init_early)(void);
    void            (*init_irq)(void);
    struct sys_timer    *timer;     /* system tick timer    */
    void            (*init_machine)(void);
    void            (*init_late)(void);
    #ifdef CONFIG_MULTI_IRQ_HANDLER
    void            (*handle_irq)(struct pt_regs *);
#endif
    void            (*restart)(char, const char *);
};
           

,好了,試着修改此參數為S3C2410_SDRAM_PA + 0x100(原值為0x100)編譯下載下傳後居然無法啟動了,卡死在Uncompressing Linux… done, booting the kernel.這一步,修改回原來的值,尋找了下發現在arch/arm/kernel/setup.c的setup_machine_tags函數中有所改變

if (__atags_pointer)
        tags = phys_to_virt(__atags_pointer);
    else if (mdesc->atag_offset)
        tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);//實際是進入這一步,
           

跟2.6.35的不同,

if (__atags_pointer)
     {
         printk("using __atags_pointer to get tags.\n");
         tags = phys_to_virt(__atags_pointer);
     }
     else if (mdesc->boot_params)
     {
         tags = phys_to_virt(mdesc->boot_params);//不同點
         printk("using mdesc->boot_params to get tags.the tags is 0x%08x\n",tags);//anzyelay
         printk("tags->hdr.tag = 0x%08x\n",tags->hdr.tag);
     }
           

是以我直接列印輸出下發現mdesc->boot_params結果居然不是0x30000100(S3C2410_SDRAM_PA + 0x100)而是

mdesc->boot_params=0xc0000100(為什麼??不知道呀不知道 ,我看了下bootloader源碼裡也是0x30000100這個位址呀,懶得去跟蹤了,反正舊版傳過來是這個值。)

而3.6.6版本的從arch/arm/mach-s3c24xx/mach-smdk2440.c中傳過來的0x0100加上PAGE_OFFSET(0xc000000正好是0xc0000100,是以你一改MACHINE_START裡的.

__atag_offset

還會出錯。是以結論是位址是正确設定了的。

現在既然不是核心與bootloader傳參位址錯誤這個原因那怎麼辦?我對比兩個版本分析調試了下setup_machine_tags函數,發現原來是

CONFIG_DEPRECATED_PARAM_STRUCT

這個宏沒有定義的問題,導緻後面核心讀取到的參數位址tags是錯誤的(不在是0xc0000100)。

首先核心設定參數的路徑是在main.c函數中, start_kernel–>setup_arch(&command_line)–>setup_command_line(command_line);

重點就在setup_arch函數中,此函數位于arch/arm/kernel/setup.c ,如下

void __init setup_arch(char **cmdline_p)
{
    struct machine_desc *mdesc;

    setup_processor();
    mdesc = setup_machine_fdt(__atags_pointer);//因為__atags_pointer為空,直接跳出來了,
    //這個參數是在R2寄存器給過來的吧,可以看head-common.s中的__mmap_switched
    if (!mdesc)
        mdesc = setup_machine_tags(machine_arch_type);//然後進入此處,
        //可以對比下2.6.35,3.6.6這裡優化了下。這次出錯的原因也主要是在這個函數裡
    machine_desc = mdesc;
    machine_name = mdesc->name;

    setup_dma_zone(mdesc);

    if (mdesc->restart_mode)
        reboot_setup(&mdesc->restart_mode);

    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;

    /* populate cmd_line too for later use, preserving boot_command_line */
    strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
    *cmdline_p = cmd_line;//擷取到啟動參數

    parse_early_param();

    sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[]), meminfo_cmp, NULL);
    sanity_check_meminfo();
    arm_memblock_init(&meminfo, mdesc);

    paging_init(mdesc);
    request_standard_resources(mdesc);

    if (mdesc->restart)
        arm_pm_restart = mdesc->restart;

    unflatten_device_tree();

#ifdef CONFIG_SMP
    if (is_smp())
        smp_init_cpus();
#endif
    reserve_crashkernel();

    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

    if (mdesc->init_early)
        mdesc->init_early();
}
           

我們來看setup_machine_tags這個函數,在同一檔案中

static struct machine_desc * __init setup_machine_tags(unsigned int nr)
{
    struct tag *tags = (struct tag *)&init_tags;
    struct machine_desc *mdesc = NULL, *p;
    char *from = default_command_line;//編譯核心時配置的Boot Options

    init_tags.mem.start = PHYS_OFFSET;

    /*
     * locate machine in the list of supported machines.
     * 這裡是根據你在MACHINE_START(arg1,arg2)給的arg1=nr才找出
     * 對應的裝置描述結構p,是以,如果你沒有設定正确arg1時,或者你的arg1
     * 所對應的Machine ID(在檔案arch/arm/tools/mach-types中)不對
     * 時,那也會出現卡在Uncompres...這個錯誤。你開啟kernel hacking中的
     * early print時就會看到錯誤原因。(這就是所謂的機器碼導緻的卡死原因。)
     */
    for_each_machine_desc(p)
        if (nr == p->nr) {
            printk("Machine: %s\n", p->name);
            mdesc = p;
            break;
        }

    if (!mdesc) {
        early_print("\nError: unrecognized/unsupported machine ID"
            " (r1 = 0x%08x).\n\n", nr);
        dump_machine_table(); /* does not return */
    }

    if (__atags_pointer)
        tags = phys_to_virt(__atags_pointer);
        /*前面說過這個參數為空,直接路過了,跟參考文章2說的什麼
        * 設定了bootloader參數就直接擷取過來有些不對,可能與我
        * 用的飛淩的bootloader,如果用UBOOT估計從這裡進吧,我沒
        * 有弄uboot,望知道的人告知下*/
    else if (mdesc->atag_offset)
        tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);//我們是進入到這裡,
        //前面說了在配置檔案中的atag_offset就是0x0100,不要改。tags=0xc0000100

    /*2.6.35版的是沒有下面這個宏的,由此直接跳過了convert_to_tag_list這一步
    *導緻後面的tags值改變,目前我的bootloader傳來的參數就是old style,是以這一步
    *不能省。這個在kernel配置裡的kernel feature裡選中Provide old way to 
    * pass kernel parameters 這項就行。*/
#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
    /*
     * If we have the old style parameters, convert them to
     * a tag list.
     */
    if (tags->hdr.tag != ATAG_CORE)
        convert_to_tag_list(tags);
#endif

    if (tags->hdr.tag != ATAG_CORE) {
#if defined(CONFIG_OF)
        /*
         * If CONFIG_OF is set, then assume this is a reasonably
         * modern system that should pass boot parameters
         */
        early_print("Warning: Neither atags nor dtb found\n");
#endif
        tags = (struct tag *)&init_tags;
    }
//到這裡tags=0xc0000100依舊,如果變了那肯定不對了(我就是在跟蹤到此處不對才發現問題在上面這一步的)。
//同時tags->hdr.tag == ATAG_CORE(0x54410001)了,
    if (mdesc->fixup)
        mdesc->fixup(tags, &from, &meminfo);

    if (tags->hdr.tag == ATAG_CORE) {
        if (meminfo.nr_banks != )
            squash_mem_tags(tags);
        save_atags(tags);
        parse_tags(tags);
    }


    /* parse_early_param needs a boot_command_line */
    strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

    return mdesc;
}
           

好了,依據上面的說法,make menuconfig 勾上kernel feature裡的Provide old way to pass kernel parameters ,make zImage後,下載下傳啟動,可以正常通過 bootloader設定啟動參數了。同時我們可以在啟動提示資訊中看到

ATAG_INITRD is deprecated; please update your bootloader.
           

這句,是以bootloader與核心版本不相容時也會導緻參數傳遞錯錯引起輸出停留在Uncompressing Linux… done, booting the kernel.

另附加問題:我用arm-linux-gcc-4.9.4編譯已經成功移植好的2.6.35核心也出現停留在此的現象,換回其它版本的編譯器又能正常啟動(隻換個編譯器,其它未做任何修改)。開始以為是4.9.4的編譯器有問題,今天用來編譯了下3.6.6的核心居然是可以的,是以應該是2.6.35核心移植還有問題不相容4.9.4的gcc,具體不知原因!!!!

繼續閱讀