环境:
(1) 硬件
MPU采用freescale imx25 (arm926ejs)、SDRAM 64M、j-link调试环境、其他略。
(2) 软件
Uboot+官方uboot补丁、Linux2.6.28 内核+官方内核补丁。
现象:当UBOOT启动,解压内核之后,在串口上打印出信息booting the kernel然后就死住了。
硬件特殊的地方:由于板子在加上第一片内存时工作不正常,只有一片64M的内存,即第二片内存,起始地址是0×9000000。
解决思路:
1. Uboot向linux参数传递问题。
2. 内存配置问题。
3. 控制台或者串口等驱动问题。
调试方法:
1. 由于采用内核启动之前print_ascii方法时,必须修改内核配置的相关选项。即,在make menuconfig 中,在kernel hacking选项中,勾选Debug Kernel之后的Kernel low-level debug一项。但是,这样配置之后,编译内核出错。所以,我们采用了自己的方法:在内核启动的起始阶段,即汇编代码段,没进入C代码之前,通过插入汇编语句,加上 b. 死循环,修改相应寄存器的内容,在JTAG commander下查看写入的寄存器是否有问题。在进入C代码之后采用内联汇编调试 mov + while(1),修改相应寄存器的内容,再通过JTAG commander查看写入的寄存器是否有问题,来查明内核启动的具体位置。
2. 在串口可以输出数据之后,采用printk打印信息,确认代码执行到的具体位置。
一开始想到这个问题,主要是考虑U-boot向内核传的参数传对了没有,如何将传递的参数打印出来。于是在init/main.c文件中的start_kernel函数中试图打印参数,才发现根本没有执行到到该函数。这是什么原因呢,通过在代码中,内联汇编,查看PC值,发现跑飞了,跑到了不知道什么地方去了。
接着,我们考虑是不是解压缩或者其他过程中的地址都不正确呢?这个问题很棘手,因为linux在启动时大概用到了5、6个地址。比如说zrealaddr这个是真正解压缩后的物理地址,PHYS_OFFSET这个是内存的基地址,LOAD_ADDR是zImage中解压缩代码在RAM中的偏移地址,TEXT_START是内核ram启动的偏移地址,虚拟地址等等。考虑在mkimage由zImage生成uImage时,传递的参数代表的就是解压缩后的地址。它只是在zImage前面加上长度为40的头,提供信息而已。在uboot上还有三、四个地址,比如说参数所在的地址应该放在0×90000100处(这个地址详细介绍参见后面文档),还有将Uboot自己加载到内存空间的地址等。
经过一点点的查看PC都是正常,解压缩过程都是正常的,就继续跟踪启动过程,页表的建立也是正常的,问题出现在开启MMU之后程序跑飞了。这是为什么呢?一开始不解,因为我们感觉到该配置的比如内存基地址都已经配置了,因为开启MMU之后,虚拟地址开始正式使用,PC的值是要加上3G的,但是也不至于跑飞到E********开头的地方去啊。还应该是地址问题,因为freescale官方为内核打上支持芯片的补丁后,搞的linux内核里面对于freescale的支持非常混乱。在arch/arm下面有两个目录,一个是我们的MPU mx_25的文件夹还有一个是plat_mx的文件夹。刚开始不好猜测这两者的关系,经过删除生成的.o文件和查看编译时的信息,得知很可能是plat_mx的mx25.h在起作用。于是我们跟踪进去,果然在其中发现一个配置内存首地址的选项,将其更改为第一块bank,(默认为0块)。即修改SDRAM0为SDRAM1。编译之后运行,哎,还是死在解压缩后。但是在这个时候,跟踪PC后,发现没有跑飞,所以第一个问题解决了。但是为什么通过串口还是没有能打印出信息来呢?
还是考虑参数传递问题,反复修改由uboot传递给linux的参数。如console=mxcuart,0×43f90000,115200n8,或者console= ttymxc0 都不能正常打印程序。于是,开始考虑是频率问题,对于MPU的频率在uboot打印出来是398M,但是内核配置的是399M,激动的很啊,以为真正的问题找到了。但是进去一看代码之后,就觉得是这个问题的概率很小了。因为频率打印的时候用到的是整除运算,所以得到398M是很可能的。经过一些列查看配置文件证明不太可能是MPU频率的问题,又卡死在这儿了。没办法硬着头皮继续跟踪start_kernel,对于嵌入式打印操作,必须好好了解其中的printk的原理。原来,参见文档,经过仔细跟踪调试,发现在调用early_console(一个很简单的专门用来打印调试信息的函数)时,在写函数中出现了死循环情况,看来是读状态寄存器读不到东西才造成信息输出的问题。因为imx25的uart跟23的不是一样的(23的支持一个debug uart,但是这个支持在每一个串口中支持UTS测试用),猜测是reset时没有正确reset状态寄存器。结果跟踪函数才发现这是很简单的配置,于是猜测还是先使用UTS打印信息,经过修改后发现打印一部分信息后又死住。
真是一波三折啊,这次又是死到什么地方了?不过现在调试好多了,printk可以使用了。开始使用printk测试,发现在start_kernel函数中的rest_init函数中死住,将initcall_debug打开(在U-boot传递参数中指定),发现串口输出打印到epio的初始化时便死住了。哎,考虑到暂时还没有用到这个模块,于是将其注销掉。再重新编译内核,结果启动内核后打印终于正常了。
写着简单,做着太难。
注意:
有好几次问题出现在自己身上。因为用while(1)调试不同于断点调试,如果用这个一定要在用完之后根据错误情况选择是否注释掉,有好多次都是死到自己定制的while(1)循环上,要及时撤销。