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

 

快乐虾

http://blog.csdn.net/lights_joy/

[email protected]

   

 

本文适用于

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这是一个已经将rootfs编译进内核的文件,它是ELF格式的,带了符号表,此文件不能用bootm引导。

l         linux.initramfs.gz这是对linux.initramfs文件的压缩。

l         rootfs.initramfs这个是rootfs,不带内核。

l         rootfs.initramfs.contents这是个文本文件,指明了rootfs中的文件及目录信息。

l         rootfs.initramfs.gzrootfs.initramfs文件的压缩。

l         System.map.initramfs这是个文本文件,指明了内核符号表。

l         uImage.initramfs这是个带image_header的文件,带rootfs,通常传给u-boot的也是这个文件。

l         vmImage这是个带image_header的文件,带是不带rootfs,仅仅是一个内核。可用bootm引导。

l         vmlinux这是个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-bootvdsp 4.5下的移植( 2007/11/12 )

u-boot for bf561中使用nand flash( 2007/11/15 )

u-bootuClinux的参数传递( 2007/12/1 )

关于u-boot-1.1.6(bf561) for VDSP的移植( 2008/3/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录:#if( 2008/4/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(2) .macro( 2008/4/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(3): 汇编空语句( 2008/4/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(4):提示信息( 2008/4/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(5):ENDPROC( 2008/4/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(6):使用u-bootcrt代码( 2008/4/10 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(7):改造u-boot.lds.s( 2008/4/11 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(8):链接错误_bss_start( 2008/4/11 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(9):bool的问题( 2008/4/11 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(10):__xchg( 2008/4/14 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(11):bsz( 2008/4/14 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(12):第二阶段的程序入口( 2008/4/14 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(13):使用L1( 2008/4/14 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(14):使用VDSP( 2008/4/15 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(15):DECLARE_GLOBAL_DATA_PTR( 2008/4/15 )

 U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(16):*cplb_add( 2008/4/17 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(17): Entry.h( 2008/4/17 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(18):const( 2008/4/17 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(19):分号惹祸( 2008/4/17 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(20):INPUT_SECTION_ALIGN( 2008/4/17 )

U-boot-1.1.6-2008R1vdsp5(bf561)的移植记录(21):收工( 2008/4/17 )

 

 

 

 

 

 

 

你可能感兴趣的:(linux,image,command,header,application,compression)