您的位置:首页 > 其它

从bootm看u-boot引导内核的过程

2008-07-15 15:59 501 查看
快乐虾 http://blog.csdn.net/lights_joy/ lights@hb165.com


本文适用于
ADI bf561 DSP
优视BF561EVB开发板
u-boot-1.1.6 (移植到vdsp5)
Visual DSP++ 5.0

欢迎转载,但请保留作者信息
我们知道,在u-boot中可以使用bootm这个命令来引导uclinux内核,那么其具体的过程是怎样的呢?

1.1.1 bootm的命令参数

通过help bootm命令可以知道bootm所带的参数,以下内容来自于u-boot/common/cmd-bootm.c:
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory/n",
"[addr [arg ...]]/n - boot application image stored in memory/n"
"/tpassing arguments 'arg ...'; when booting a Linux kernel,/n"
"/t'arg' can be the address of an initrd image/n"
);
从这里可以知道,bootm后面可以带两个参数,一个是内核所在的地址,这个地址就是通过tftp或者loadx指令下载内核时的存放地址,另一个可以指明initrd所处的位置。

1.1.2 do_bootm

在u-boot检测到bootm命令后,它将调用do_bootm这个函数进行内核引导:
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong addr;
ulong data, len, checksum;
ulong *len_ptr;
uint unc_len = CFG_BOOTM_LEN;
int i, verify;
char *name, *s;
int (*appl)(int, char *[]);
image_header_t *hdr = &header;

/*
1、提取命令行中的地址参数。
*/
s = getenv ("verify");
verify = (s && (*s == 'n')) ? 0 : 1;

if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}

SHOW_BOOT_PROGRESS (1);
printf ("## Booting image at %08lx .../n", addr);

/* Copy header so we can blank CRC field for re-calculation */
memmove (&header, (char *)addr, sizeof(image_header_t));

/*
2、检测内核文件头的签名是否有效。
*/
if (ntohl(hdr->ih_magic) != IH_MAGIC) {
{
puts ("Bad Magic Number/n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
SHOW_BOOT_PROGRESS (2);

/*
3、通过CRC校验确定文件是否损坏。
*/
data = (ulong)&header;
len = sizeof(image_header_t);

checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0;

if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksum/n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
SHOW_BOOT_PROGRESS (3);

/*
4、输出内核文件头的信息。
*/
/* for multi-file images we need the data part, too */
print_image_hdr ((image_header_t *)addr);

data = addr + sizeof(image_header_t);
len = ntohl(hdr->ih_size);

if (verify) {
puts (" Verifying Checksum ... ");
if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC/n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
puts ("OK/n");
}
SHOW_BOOT_PROGRESS (4);

/*
5、检验内核是否为此ARCH编译的。
*/
len_ptr = (ulong *)data;

if (hdr->ih_arch != IH_CPU_BLACKFIN)
{
printf ("Unsupported Architecture 0x%x/n", hdr->ih_arch);
SHOW_BOOT_PROGRESS (-4);
return 1;
}
SHOW_BOOT_PROGRESS (5);

/*
6、判断文件类型,对于uclinux内核,其值为IH_TYPE_KERNEL。
*/
switch (hdr->ih_type) {
case IH_TYPE_STANDALONE:
name = "Standalone Application";
/* A second argument overwrites the load address */
if (argc > 2) {
hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
}
break;
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
case IH_TYPE_MULTI:
name = "Multi-File Image";
len = ntohl(len_ptr[0]);
/* OS kernel is always the first image */
data += 8; /* kernel_len + terminator */
for (i=1; len_ptr[i]; ++i)
data += 4;
break;
default: printf ("Wrong Image Type for %s command/n", cmdtp->name);
SHOW_BOOT_PROGRESS (-5);
return 1;
}
SHOW_BOOT_PROGRESS (6);

/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
* recover from any failures any more...
*/
/*
7、将内核解压缩到文件头中指定的Load Address,从这里也可以知道,下载地址和文件头中的LoadAddress之间应该有足够的空间,否则将造成解压缩的失败。
*/

iflag = disable_interrupts();

switch (hdr->ih_comp) {
case IH_COMP_NONE:
if(ntohl(hdr->ih_load) == addr) {
printf (" XIP %s ... ", name);
} else {
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
}
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
puts ("GUNZIP ERROR - must RESET board to recover/n");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;
default:
if (iflag)
enable_interrupts();
printf ("Unimplemented compression type %d/n", hdr->ih_comp);
SHOW_BOOT_PROGRESS (-7);
return 1;
}
puts ("OK/n");
SHOW_BOOT_PROGRESS (7);

/*
8、由于引导的是内核,跳过本段代码。
*/
switch (hdr->ih_type) {
case IH_TYPE_STANDALONE:
if (iflag)
enable_interrupts();

/* load (and uncompress), but don't start if "autostart"
* is set to "no"
*/
if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
char buf[32];
sprintf(buf, "%lX", len);
setenv("filesize", buf);
return 0;
}
appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
(*appl)(argc-1, &argv[1]);
return 0;
case IH_TYPE_KERNEL:
case IH_TYPE_MULTI:
/* handled below */
break;
default:
if (iflag)
enable_interrupts();
printf ("Can't boot image type %d/n", hdr->ih_type);
SHOW_BOOT_PROGRESS (-8);
return 1;
}
SHOW_BOOT_PROGRESS (8);

/*
9、调用相应的函数进行内核引导。
*/
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);
break;
case IH_OS_NETBSD:
do_bootm_netbsd (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;

case IH_OS_RTEMS:
do_bootm_rtems (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;

}

SHOW_BOOT_PROGRESS (-9);
return 1;
}
这个函数看着挺长的,其实无非就是将内核解压缩,然后调用do_bootm_linux引导内核。

1.1.3 do_bootm_linux

内核的实际引导是由do_bootm_linux这个函数来完成的:
void do_bootm_linux(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong * len_ptr, int verify)
{
int (*appl) (char *cmdline);
char *cmdline;

appl = (int (*)(char *))ntohl(header.ih_ep);
printf("Starting Kernel at = %x/n", appl);
cmdline = make_command_line();
if (icache_status())
icache_disable();
if (dcache_status()) {
flush_data_cache();
dcache_disable();
}
(*appl) (cmdline);
}
这个函数首先调用make_command_line构建了命令行,然后跳转到文件头中指定的Entry Point执行。即header.ih_ep,构建命令行的过程如下所示:
char *make_command_line(void)
{
char *dest = (char *)CMD_LINE_ADDR;
char *bootargs = getenv("bootargs");

if (bootargs == NULL)
return NULL;

strncpy(dest, bootargs, 0x1000);
dest[0xfff] = 0;
return dest;
}
也就是说,它将读取u-boot的环境变量bootargs,并将之做为内核的引导参数。

1.1.4 uclinux生成的文件解析

在编译uclinux时,会在uclinux/images目录下生成多个文件,这些文件都是什么内容呢?从上文对引导过程的分析可以看出,要用bootm进行引导,uclinux内核文件必须有一个定义好的文件头,这个文件头用一个image_head的结构体来表示:

#define IH_MAGIC 0x27051956 /* Image Magic Number */
#define IH_NMLEN 32 /* Image Name Length */

/*
* all data in network byte order (aka natural aka bigendian)
*/

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;
因而使用uedit看一下文件头就大致可以判断出来了,需要注意的是这个结构体中的整数都是big-endian的,在uedit下看需要转换。
以下就是默认生成的几个文件及其意义:
l linux.initramfs[/b]:[/b]这是一个已经将rootfs编译进内核的文件,它是ELF格式的,带了符号表,此文件不能用bootm引导。
l linux.initramfs.gz[/b]:[/b]这是对linux.initramfs文件的压缩。
l rootfs.initramfs[/b]:[/b]这个是rootfs,不带内核。
l rootfs.initramfs.contents[/b]:[/b]这是个文本文件,指明了rootfs中的文件及目录信息。
l rootfs.initramfs.gz[/b]:[/b]对rootfs.initramfs文件的压缩。
l System.map.initramfs[/b]:[/b]这是个文本文件,指明了内核符号表。
l uImage.initramfs[/b]:[/b]这是个带image_header的文件,带rootfs,通常传给u-boot的也是这个文件。
l vmImage[/b]:[/b]这是个带image_header的文件,带是不带rootfs,仅仅是一个内核。可用bootm引导。
l vmlinux[/b]:[/b]这是个ELF格式的内核文件,不带rootfs,不能用bootm引导。

相关文章

哈哈,u-boot终于在bf561的板子上工作了 (2007/9/12)
用u-boot引导uclinux(2007/9/25)
u-boot for vdsp:“裸奔”助手(2007/11/3)
u-boot for bf561中的命令实现(2007/11/3)
u-boot在vdsp 4.5下的移植(2007/11/12)
在u-boot for bf561中使用nand flash(2007/11/15)
u-boot向uClinux的参数传递(2007/12/1)
关于u-boot-1.1.6(bf561) for VDSP的移植(2008/3/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录:#if(2008/4/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(2): .macro(2008/4/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(3): 汇编空语句(2008/4/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(4):提示信息(2008/4/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(5):ENDPROC(2008/4/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(6):使用u-boot的crt代码(2008/4/10)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(7):改造u-boot.lds.s(2008/4/11)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(8):链接错误_bss_start(2008/4/11)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(9):bool的问题(2008/4/11)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(10):__xchg(2008/4/14)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(11):bsz(2008/4/14)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(12):第二阶段的程序入口(2008/4/14)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(13):使用L1(2008/4/14)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(14):使用VDSP库(2008/4/15)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(15):DECLARE_GLOBAL_DATA_PTR(2008/4/15)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(16):*cplb_add(2008/4/17)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(17): Entry.h(2008/4/17)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(18):const(2008/4/17)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(19):分号惹祸(2008/4/17)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(20):INPUT_SECTION_ALIGN(2008/4/17)
U-boot-1.1.6-2008R1到vdsp5(bf561)的移植记录(21):收工(2008/4/17)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: