U-Boot移植好后就要考虑如何引导内核了(以下介绍的是bootm方式,开发板是友善之臂的mini2440)。所用的U-Boot是移植的U-Boot-2009.08 ,内核是mini2440光盘中的kernel-2.6.29。U-Boot引导内核分三步:内核的编译、启动参数的设置、下载固化。
(一)内核的编译
通常,u-boot为kernel提供一些kernel无法知道的信息,比如ramdisk在RAM中的地址。Kernel也必须为U-boot提供必要的信息,如通过mkimage这个工具(在u-boot代码的tools目录中)可以给zImage添加一个header,也就是使得通常编译的内核zImage添加一个数据头,把添加头后的image通常叫uImage,uImage是可以被U-boot直接引导的内核镜像。那么如何使用mkimage工具而产生uImage的呢?
1. 工具 mkimage
--------------------------------------------------------------------------------
编译U-Boot成功后,在u-boot代码的tools目录中生成一些工具,比如mkimage。将它们复制到/usr/local/bin 目录下,就可以直接使用了。现在我们编译内核需要用到mkimage来生成U-Boot格式的内核映像文件uImage 。复制完成后在终端输入命令" mkimage "并回车,显示关于mkimage的提示信息,表示你现在已经可以使用此命令了:
Usage: mkimage -l image
-l ==> list image header information
mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch' //用于指定CPU类型,比如ARM
-O ==> set operating system to 'os' //用于指定操作系统,比如Linux
-T ==> set image type to 'type' //用于指定image类型,比如Kernel
-C ==> set compression type 'comp' //指定压缩类型
-a ==> set load address to 'addr' (hex) //指定image的载入地址
-e ==> set entry point to 'ep' (hex) //内核的入口地址,一般是:image的载入地址+0x40(信息头的大小)
-n ==> set image name to 'name' //image在头结构中的命名
-d ==> use image data from 'datafile' //无头信息的image文件名
-x ==> set XIP (execute in place) //设置执行位置
mkimage [-D dtc_options] -f fit-image.its fit-image
2. 编译
--------------------------------------------------------------------------------
在内核的根目录下执行命令进行编译。如果之前编译过,最好先 make clean 一下。然后 make zImage 生成 zImage 的内核(在此之前别忘了参看说明书装载缺省配置文件)。最后在目录kernel-2.6.29/arch/arm/boot下执行命令
mkimage -n 'linux-2.6.29' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage.img生成 U-Boot可以引导的内核,生成的文件名为uImage.img (也可以使用默认设置,在内核根目录下执行make uImage,生成默认的文件名为uImage)。生成的内核文件都在目录kernel-2.6.29/arch/arm/boot下。
注:这里我用的mkimage是在U-BOOT-2009.08版本中生成的,在执行mkimage -n 'linux-2.6.29' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage.img时,会出现错误,错误提示代码大概意思是说zImage是不支持的image类型,提示中还会列出支持的image类型。 开始还以为编译出来的zImage文件有问题,结果用友善提光盘中提供的zImage文件执行时也出同样的错误。在网上找了很多别人的做法也都是这么做的,没办法,最后试着将zImage文件改名为kernel,再执行命令:mkimage -n 'linux-2.6.29' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d kernel uImage.img 顺利通过了。
关于mkimage -a -e参数的设置,还有另一种方式,具体请google一下。
(二)启动参数的设置
这里我用的是通过串口下载的方式,故暂不提tftp的设置.
将bootcmd的参数改变成从nand flash引导kernel(每次开机后让u-boot复制到sdram中,再使用env参数中的bootm命令引导)。方法是在u-boot提示符下输入:
set bootcmd 'nand read 0x31000000 0x80000 0x00200000;bootm 0x31000000'再改变bootargs,使得从nand flash启动根文件系统。方法是在u-boot提示符下输入:
set bootargs 'noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0'最后保存设置:
saveenv
注:这里需要特别说明的一下的是set bootcmd 'nand read 0x31000000 0x80000 0x00200000;bootm 0x31000000' ,这个启动命令的意思是:启动时从nand flash的0x80000处(也就是内核存放在NAND FLASH中的起始地址)开始读取大小为0x200000的数据到内存地址0x31000000处,最后调用bootm指令启动内核,bootm指令参数为0x31000000即内核在RAM中的起始地址。
关于内核在NAND FLASH中起始地址的设置,我这里设置的为0x80000.这里主要根据U-BOOT存放区域和UBOOT启动参数存放区域来确定。下面列一个我的NAND区域划分:
0x00000000-0x00060000 //u-boot
0x00060000-0x00080000 //启动参数,即saveenv命令,将设置的参数保存的地址。
这个地址区的确定,可查看UBOOT代码./u-boot-2009.08/include/configs/mini2440.h
#define CONFIG_ENV_OFFSET 0X60000
#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */
从这两个宏,可以得知参数保存的区域为0x00060000-0x00080000。
0x00080000-0x00280000 //内核
内核区域的起始地址尤其关键,如果在用nand write指令固化内核到NAND FLASH中时,写入起始地址在0x00080000之前时,就可以能导致覆盖u-boot和启动参数,导致无法开始或失去上面设置过的启动参数,而无法引导内核。
0x00280000-之后的就是文件系统存放的区域了。
(三)下载固化
1. 引导系统测试
--------------------------------------------------------------------------------
我们先把内核下载到内存中(地址为环境参数bootcmd中的nand read 和bootm 后的地址),然后我们执行bootm命令(同一个地址)看能不能启动系统。在U-Boot下执行:
loadx 0x31000000 //通过超级终端传送文件,将uImage.img下载到0x31000000
在提示下载完成后再执行:
bootm 0x31000000
如果成功则证明我们的设置没有问题(如果有文件系统的话可以正常启动,这个文件系统可以是用Nor Flash 中的 supervivi 烧到 Nand Flash中的)。
很不幸失败了.....
加载内核时跑到:
Starting kernel ...
Uncompressing Linux.......................................................................................... done, booting the kernel.
到这里就不动了。老办法GOOGLE一下,发现这是个经典问题了,经典问题自有经典的办法解决:
1、u-boot中的命令行参数中console设定有问题,对2.6的内核应该使用ttySAC0,而不是ttyS0。更改为"console=ttySAC0"就可以解决问题。
2、u-boot中FCLK与kernel时钟频率不一致。kernel的FCLK为200MHz,但是uboot的默认值是202.8MHz。(vivi默认的也是200MHz,所以vivi不会出现这个问题。)这样修改uboot的时钟频率设定就可以解决问题。
3. u-boot传递的mach_type参数与内核设置的mach_type不对。
通过分析,发现存在第三种问题。
a).通过对U-BOOT-2009.08代码的分析,mach_type参数在board_Init函数中传出来,在u-boot-2009.08/board/mini2440/mini2440.c文件中,
#if defined(CONFIG_S3C2440)
/* arch number of S3C2440 -Board */
gd->bd->bi_arch_number = MACH_TYPE_S3C2440 ;
#endif
由此可见,mach_type的值由宏MACH_TYPE_S3C2440定义决定。
在u-boot-2009.08/include/asm-arm/mach-types.h中可以找到,默认定义为:
#define MACH_TYPE_S3C2440 362
b).通过对友善内核2.6.29代码分析,在./linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c中,找到MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board"),由此发现,由MINI2440这个参数确定。
在./linux-2.6.29/arch/arm/tools/mach-types中,找到
mini2440 MACH_MINI2440 MINI2440 1999
由此可见,内核设定的mach_type的值为1999。
c).将U-BOOT中,#define MACH_TYPE_S3C2440 362
改为#define MACH_TYPE_S3C2440 1999
重新编译uboot,启动nor flash中的 supervivi将重新编译得到的uboot.bin烧到NAND FLASH中,按上面所说的方法测试加载到RAM中的内核。
一长串串的打印英文飘过,OK。。。引导内核成功了。
2.下载固化
----------------------------------------------------------------------------------------
在u-boot下输入loadx 0x31000000,通过超级终端将uImage下载到内存中。
接着执行 nand erase 0x80000 0x200000 删除掉该区域原有的数据。
执行 nand write 0x31000000 0x80000 0x200000,将内存中的kernel烧入nand flash。
重新开机,OK。。。。。引导成功。。。