U-BOOT全线移植分析系列之四
――U-boot如何引导Linux内核启动?
Sailor_forever sailing_9806@163.com 转载请注明
http://blog.csdn.net/sailor_8318/archive/2008/08/05/2773412.aspx
【摘要】本节介绍了U-boot使用go或bootm启动linux内核的方法。首先介绍了mkimage的参数意义和bootm的详细执行流程。然后分析了如何利用mkimage生成内核映象的方法。对于bootm方式的内核是否压缩、-a、-e、运行地址等16种组合情况,给出了详细的测试过程,提出了6种可用方法种的三种最优解。
【关键字】:U-boot;AT91RM9200;bootm;mkimage;-a;-e;-c
四 U-boot如何引导Linux内核启动?
4.1 GO命令引导未用mkimage生成的内核
1) 运行地址!=链接地址0x20008000,不能启动
Uboot> tftp 21000000 Image;tftp 21100000 ramdisk;go 21000000
。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Error: a 在哪提示的?
2) 运行地址=链接地址0x20008000,不能启动,难道是ramdisk的问题
Uboot> tftp 20008000 Image;tftp 21100000 ramdisk;go 20008000
。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Error: a
1) 运行地址!=链接地址0x20008000,能启动,内核自解压成功,但是解压后的内核运行错误
Uboot> tftp 21000000 zImage;tftp 21100000 ramdisk;go 21000000
。。。。。。。。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Uncompressing Linux............................................................. done, booting the kernel.
€?~??鄜屈
2) 运行地址==链接地址0x20008000,能启动,内核自解压成功,但是解压后的内核运行错误
Uboot> tftp 20008000 zImage;tftp 21100000 ramdisk; go 20008000
## Starting application at 0x20008000 ...
Uncompressing Linux............................................................. done, booting the kernel.
€?~??鄜屈
上面的ramdisk都是添加了uboot的头的,去掉头部再试试。去掉了还是不行,go方法的ramdisk的地址是怎么设置的??要详细看下uboot在ramdisk这块是如何跟内核交互的?
4.2 Mkimage参数意义解析
通过mkimage这个tool可以给zImage添加一个header:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
此header是如何生成的?利用u-boot里面的mkimage工具来生成uImage (u-boot源码包/tools/mkimage.c )
这里解释一下参数的意义:
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type' “kernel或是ramdisk”
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)(内核启动时在此位置查询完整的内核印象)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place,即不进行文件的拷贝,在当前位置执行)
对于ARM linux内核映象用法:
-A arm -------- 架构是arm
-O linux -------- 操作系统是linux
-T kernel -------- 类型是kernel
-C none/bzip/gzip -------- 压缩类型
-a 20008000 ---- image的载入地址(hex),通常为0xX00008000
-e 200080XX---- 内核的入口地址(hex),XX为0x40或者0x00
-n linux-XXX --- image的名字,任意
-d nameXXX ---- 无头信息的image文件名,你的源内核文件
uImageXXX ---- 加了头信息之后的image文件名,任意取
4.3 Bootm的流程分析
Bootm命令在/common/cmd_bootm.c中do_bootm函数
》》》》》》》》》》》获取当前内核的地址,默认地址或者bootm的第一个参数
默认的加载地址或传递给bootm命令(优先)与实际的内核存放地址需要一致
if (argc < 2) {
addr = load_addr; // load_addr = CFG_LOAD_ADDR;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
printf ("## Booting image at %08lx .../n", addr);
》》》》》》》》》》》》获得image头,没有mkimage的就返回了
memmove (&header, (char *)addr, sizeof(image_header_t));
》》》》》》》》》》》》打印头部信息
print_image_hdr ((image_header_t *)addr);
实例:
Image Name: dd-kernel-
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869574 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
》》》》》》》》》》》》校验image头部
printf (" Verifying Checksum ... "); printf ("OK/n");
》》》》》》》》》》》》检查image支持的体系结构即—A 选项是否为arm或者ppc等
》》》》》》》》》》》》检查image的类型
TYPE_MULTI 是否指内核与文件系统一起,内核后面有个分界线
switch (hdr->ih_type)
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
case IH_TYPE_MULTI:
》》》》》》》》》》判断内核的压缩类型
此处的内核是否压缩非zImage和Image的概念,而是指内核在被mkimage处理前是否用gunzip等压缩过
switch (hdr->ih_comp) {
case IH_COMP_NONE: // 非压缩内核
if(ntohl(hdr->ih_load) == addr) {
// 当前内核存放的地址与-a指定的一致,则不搬动,-e必须必-a大0x40
printf (" XIP %s ... ", name);
} else {
//当前内核存放的地址与-a指定的不一致,则将内核搬到-a地址,此时-a与-e必相同
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
。。。。
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
//压缩内核,将除去头部的内核解压到-a 指定的地址了,要求-a与-e相同
// 为防止解压缩时覆盖,对于压缩内核,内核存放地址最好在—a后面
(uchar *)data, (int *)&len) != 0) {
do_reset (cmdtp, flag, argc, argv);
}
break;
》》》》》》》》》》》》》》》》判断操作系统类型
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
//前四个为传给bootm的,addr为内核最初的存放地址,没有用处
break;
#ifdef CONFIG_PPC
static boot_os_Fcn do_bootm_linux;
#else
extern boot_os_Fcn do_bootm_linux;
由上可知,对于ppc和其他体系结构的do_bootm_linux函数实现是不一样的
》》》》》》》》》》》》》》启动Linux内核
do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
int argc, char *argv[],
ulong addr,
ulong *len_ptr,
int verify)
》》》》》》》》》》》》获取命令行参数
if ((s = getenv("bootargs")) == NULL)
s = "";
strcpy (cmdline, s);
》》》》》》》》》》》》赋内核启动地址
kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
注意,对于压缩过的内核,会将内核解压到-a指定的地址,此时-a 与-e 地址必须相同
》》》》》》》》》》》判断bootm的命令参数中是否有initrd