基于FL2440的3.6.6内核移植出现Uncompressing Linux... done, booting the kernel.
2016-07-16 19:17
651 查看
具体问题
参考解决方案
解决思路
深入解决
确认了移植是正常的,肯定没有错误。
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对应正确
而在2.6.35的版本中只有一个
,因此从此处看也有可能是我的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/include/asm/mach/arch.h中
可以看到
,好了,试着修改此参数为S3C2410_SDRAM_PA + 0x100(原值为0x100)编译下载后居然无法启动了,卡死在Uncompressing Linux… done, booting the kernel.这一步,修改回原来的值,寻找了下发现在arch/arm/kernel/setup.c的setup_machine_tags函数中有所改变
跟2.6.35的不同,
所以我直接打印输出下发现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里的.
现在既然不是内核与bootloader传参地址错误这个原因那怎么办?我对比两个版本分析调试了下setup_machine_tags函数,发现原来是
首先内核设置参数的路径是在main.c函数中, start_kernel–>setup_arch(&command_line)–>setup_command_line(command_line);
重点就在setup_arch函数中,此函数位于arch/arm/kernel/setup.c ,如下
我们来看setup_machine_tags这个函数,在同一文件中
好了,依据上面的说法,make menuconfig 勾上kernel feature里的Provide old way to pass kernel parameters ,make zImage后,下载启动,可以正常通过 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,具体不知原因!!!!
参考解决方案
解决思路
深入解决
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,具体不知原因!!!!
相关文章推荐
- Linux 下的 core dump
- Linux学习笔记--打包压缩命令
- CentOS7 下安装 SublimeText3
- Linux安装JDK
- 学习如何搭建Linux运维环境
- CentOS6.7 下安装JDK
- CentOS6.7 下安装JDK
- CentOS6.7 下安装JDK
- linux下文件操作
- linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结
- linux下关闭防火墙
- centos 如何用 rsyslog 搭建本地日志服务
- CentOS安装 netdata 实时监视 Linux 系统性能
- 一些linux下面的生活向软件
- GraphicsMagick在Linux环境的安装 和 im4java的配置
- 深入理解linux内核v4l2框架之videobuf
- 深入理解l内核v4l2框架之video for linux 2
- Linux进程模型1
- Linux--谈父子进程执行过程
- Linux下查看磁盘分区命令详解