加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

嵌入式linux内核不能正常启动 问题跟踪记录

(2014-08-16 21:10:01)
标签:

内核坏块

u-boot

nandread

boot_zimage

天祥电子

分类: 嵌入式

  最近在烧录一批板子的时候出现了一个奇怪的问题:烧录完成之后内核不能正常启动,打印信息如下:

Copy linux kernel from 0x00120000 to 0x30008000, size = 0x00400000 ... Copy Kernel to SDRAM done,NOW, Booting Linux......
data abort
pc : [<3000b0ac>]    lr : [<300080ec>]
sp : 301ff6f4  ip : fe694329  fp : 00000000

r10: 40000000  r9 : 30000000  r8 : 30008000
r7 : 0000270f  r6 : 302b03b8  r5 : 73c590d6  r4 : 30008000
r3 : 0000270f  r2 : 302c1410  r1 : 302b1410  r0 : 302c1410
Flags: nzCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

  总共50多块板子竟然有5块这样的问题,故障率达到了10%。

注意到一个细节:uboot在烧录内核的时候跳过了一个坏块。会不会是copy内核到SDRAM的时候没跳过坏块而导致启动数据错误了呢?

  uboot的移植是参考天祥电子的移植手册,这里表示十分的感谢!手册写的很详细很全面,也是很好的学习资料。

u-boot引导内核用的是自己添加的命令:boot_zImage,实现函数在arch/arm/lib下的boot_zImage.c中,其中拷贝内核的函数为

static inline int copy_kernel_img(ulong dst, const char *src, size_t size)
{
 int ret = 0;
 ret = nand_read_ll((unsigned char *)dst,(unsigned long)src, (int)size);
 return ret;
}

用到了函数nand_read_ll();而此函数定义在nand_read,c中

int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
 int i, j;
 unsigned short nand_id;
 struct boot_nand_t nand;
 
nand_select();
nand_clear_RnB();
 for (i = 0; i < 10; i++)
;
 nand_id = nand_read_id();
 if (0) {
  unsigned short * nid = (unsigned short *)0x31fffff0;
*nid = nand_id;
}
if (nand_id == 0xec76 ||  
nand_id == 0xad76 ) { 
nand.page_size = 512; nand.block_size = 16 * 1024;
nand.bad_block_offset = 5;
// nand.size = 0x4000000;
 } else if (nand_id == 0xecf1 ||
     nand_id == 0xecda || 
     nand_id == 0xecd3 )  {
nand.page_size = 2048;
  nand.block_size = 128 * 1024;
nand.bad_block_offset = nand.page_size;
// nand.size = 0x8000000;
 } else {
return -1; // hang
}
 if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))
  return -1; 
 for (i=start_addr; i < (start_addr + size);) {
#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
  if (i & (nand.block_size-1)== 0) {
if (is_bad_block(&nand, i) ||
   is_bad_block(&nand, i + nand.page_size)) {

i += nand.block_size;
size += nand.block_size;
continue;
}
}
#endif
  j = nand_read_page_ll(&nand, buf, i);
i += j;
buf += j;
}
 
nand_deselect();

return 0;
}

其中跳坏块的部分用到了条件编译

#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
  if (i & (nand.block_size-1)== 0) {
if (is_bad_block(&nand, i) ||
   is_bad_block(&nand, i + nand.page_size)) {

i += nand.block_size;
size += nand.block_size;
continue;
}
}
#endif
  我搜索了下CONFIG_S3C2410_NAND_SKIP_BAD,除了此处用到了,再没有第二个地方用了,很明显这个跳坏块的功能被屏蔽掉了,问题的关键找到了,我之间把条件编译的那部分去掉了,重新编译u-boot下载到开发板上并下载内核结果现象依旧。

  后来注意到if (i & (nand.block_size-1)== 0) 这一句感觉这里应该是写错了,没注意到&和==的优先级,于是改过来之后重新编译并烧录,果然成功了。

 

  虽然现在写总结看起来很简单的过程,但是当时在调试的时候还是费了很大劲的,因为问题的原因并不是那么容易就找到,而且找到问题后解决的方法也是很多的,当时在用nand read命令将内核拷贝到内存并用go命令运行的时候,整个的内核数据是对的,但是机器码没有设置,这样想到的解决办法就是在boot_zImage.c中添加nand read将内核拷贝到RAM中,之后在通过其他的函数设置u-boot要传递给内核的参数。虽然这在理论上是可行的,但最终并没有选择这个方案,而是去查找跳过坏块相关的代码,感觉那里有错误,也许是程序员的直觉吧。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有