基于FL2440的3.6.6内核移植出现Uncompressing Linux... done, booting the kernel.

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 {
+---  4 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 :1; /* never has lp0    */
    unsigned int        reserve_lp1 :1; /* never has lp1    */
    unsigned int        reserve_lp2 :1; /* never has lp2    */
    unsigned int        soft_reboot :1; /* 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; /* tagged list */那我们到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 :1; /* never has lp0    */
    unsigned char       reserve_lp1 :1; /* never has lp1    */
    unsigned char       reserve_lp2 :1; /* 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[0]), 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 != 0)
            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,具体不知原因!!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值