u-boot分析_uboot启动内核

时间:2021-05-30 16:44:29

u-boot分析_uboot启动内核
u-boot 内核启动的时候依赖于以下这两行代码:

s = getenv("bootcmd");
...
run_command(s,0);

第一条命令是从nand把内核把读到到一个地址上去;第二条命令是从内核里面启动内核;

从哪里读?从kernel分区读;
读到哪里去?放到指定地址(0x30007fc0)去;

在PC机上,每一个硬盘前面都有一个分区表。对于嵌入式Linux来说,flash上面没有分区表,显然这个分区就和PC机上不一样;既然没有分区表,这些分区怎么体现?只能在源码里面写死的;

定义分区的源码如下:

#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256K@0(bootloader),"\也即从0256K为bootloader
                           "128k(params),"\接下来的128K放的是uboot的环境变量
                           "2m(kernel),"\ 2M空间放的是kernel
                           "-(root)"  剩下的是root分区

上面定义了各个分区的起始地址;

具体地址从uboot菜单中输入mtd命令即可。下面的这个图是我的四个分区:

u-boot分析_uboot启动内核

所以 nand read.jffs2 0x30007fc0 kernel = nand read.jffs2 0x30007fc0 0x00060000 0x0x00200000

下面分析一下如何读,如何把2M的内核读到0x30007fc0处?
因为启动时do_bootm,所以可以猜测nand read 应该是do_nand函数,do_nand函数代码如下:


int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
    int i, dev, ret;
    ulong addr, off, size;
    char *cmd, *s;
    nand_info_t *nand;
    int quiet = 0;
    const char *quiet_str = getenv("quiet");

    /* at least two arguments please */
    if (argc < 2)
        goto usage;

    if (quiet_str)
        quiet = simple_strtoul(quiet_str, NULL, 0) != 0;

    cmd = argv[1];

    if (strcmp(cmd, "info") == 0) {

        putc('\n');
        for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
            if (nand_info[i].name)
                printf("Device %d: %s, sector size %lu KiB\n",
                    i, nand_info[i].name,
                    nand_info[i].erasesize >> 10);
        }
        return 0;
    }

    if (strcmp(cmd, "device") == 0) {

        if (argc < 3) {
            if ((nand_curr_device < 0) ||
                (nand_curr_device >= CFG_MAX_NAND_DEVICE))
                puts("\nno devices available\n");
            else
                printf("\nDevice %d: %s\n", nand_curr_device,
                    nand_info[nand_curr_device].name);
            return 0;
        }
        dev = (int)simple_strtoul(argv[2], NULL, 10);
        if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
            puts("No such device\n");
            return 1;
        }
        printf("Device %d: %s", dev, nand_info[dev].name);
        puts("... is now current device\n");
        nand_curr_device = dev;

#ifdef CFG_NAND_SELECT_DEVICE
        /* * Select the chip in the board/cpu specific driver */
        board_nand_select_device(nand_info[dev].priv, dev);
#endif

        return 0;
    }

    if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
        strncmp(cmd, "dump", 4) != 0 &&
        strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 &&
        strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 &&
        strcmp(cmd, "biterr") != 0 &&
        strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 )
        goto usage;

    /* the following commands operate on the current device */
    if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE ||
        !nand_info[nand_curr_device].name) {
        puts("\nno devices available\n");
        return 1;
    }
    nand = &nand_info[nand_curr_device];

    if (strcmp(cmd, "bad") == 0) {
        printf("\nDevice %d bad blocks:\n", nand_curr_device);
        for (off = 0; off < nand->size; off += nand->erasesize)
            if (nand_block_isbad(nand, off))
                printf(" %08x\n", off);
        return 0;
    }

    /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */
    if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) {
        nand_erase_options_t opts;
        /* "clean" at index 2 means request to write cleanmarker */
        int clean = argc > 2 && !strcmp("clean", argv[2]);
        int o = clean ? 3 : 2;
        int scrub = !strcmp(cmd, "scrub");

        printf("\nNAND %s: ", scrub ? "scrub" : "erase");
        /* skip first two or three arguments, look for offset and size */
        if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)
            return 1;

        memset(&opts, 0, sizeof(opts));
        opts.offset = off;
        opts.length = size;
        opts.jffs2  = clean;
        opts.quiet  = quiet;

        if (scrub) {
            puts("Warning: "
                 "scrub option will erase all factory set "
                 "bad blocks!\n"
                 " "
                 "There is no reliable way to recover them.\n"
                 " "
                 "Use this command only for testing purposes "
                 "if you\n"
                 " "
                 "are sure of what you are doing!\n"
                 "\nReally scrub this NAND flash? <y/N>\n");

            if (getc() == 'y' && getc() == '\r') {
                opts.scrub = 1;
            } else {
                puts("scrub aborted\n");
                return -1;
            }
        }
        ret = nand_erase_opts(nand, &opts);
        printf("%s\n", ret ? "ERROR" : "OK");

        return ret == 0 ? 0 : 1;
    }

    if (strncmp(cmd, "dump", 4) == 0) {
        if (argc < 3)
            goto usage;

        s = strchr(cmd, '.');
        off = (int)simple_strtoul(argv[2], NULL, 16);

        if (s != NULL && strcmp(s, ".oob") == 0)
            ret = nand_dump_oob(nand, off);
        else
            ret = nand_dump(nand, off);

        return ret == 0 ? 1 : 0;

    }

    /* read write */
    if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
        int read;

        if (argc < 4)
            goto usage;

        addr = (ulong)simple_strtoul(argv[2], NULL, 16);

        read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
        printf("\nNAND %s: ", read ? "read" : "write");
        if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
            return 1;

        s = strchr(cmd, '.');
        if (s != NULL &&
            (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) {
            if (read) {
                /* read */
                nand_read_options_t opts;
                memset(&opts, 0, sizeof(opts));
                opts.buffer = (u_char*) addr;
                opts.length = size;
                opts.offset = off;
                opts.quiet      = quiet;
                ret = nand_read_opts(nand, &opts);
            } else {
                /* write */
                nand_write_options_t opts;
                memset(&opts, 0, sizeof(opts));
                opts.buffer = (u_char*) addr;
                opts.length = size;
                opts.offset = off;
                /* opts.forcejffs2 = 1; */
                opts.pad    = 1;
                opts.blockalign = 1;
                opts.quiet      = quiet;
                ret = nand_write_opts(nand, &opts);
            }
        } else {
            if (read)
                ret = nand_read(nand, off, &size, (u_char *)addr);
            else
                ret = nand_write(nand, off, &size, (u_char *)addr);
        }

        printf(" %d bytes %s: %s\n", size,
               read ? "read" : "written", ret ? "ERROR" : "OK");

        return ret == 0 ? 0 : 1;
    }

    if (strcmp(cmd, "markbad") == 0) {
        addr = (ulong)simple_strtoul(argv[2], NULL, 16);

        int ret = nand->block_markbad(nand, addr);
        if (ret == 0) {
            printf("block 0x%08lx successfully marked as bad\n",
                   (ulong) addr);
            return 0;
        } else {
            printf("block 0x%08lx NOT marked as bad! ERROR %d\n",
                   (ulong) addr, ret);
        }
        return 1;
    }
    if (strcmp(cmd, "biterr") == 0) {
        /* todo */
        return 1;
    }

    if (strcmp(cmd, "lock") == 0) {
        int tight  = 0;
        int status = 0;
        if (argc == 3) {
            if (!strcmp("tight", argv[2]))
                tight = 1;
            if (!strcmp("status", argv[2]))
                status = 1;
        }

        if (status) {
            ulong block_start = 0;
            ulong off;
            int last_status = -1;

            struct nand_chip *nand_chip = nand->priv;
            /* check the WP bit */
            nand_chip->cmdfunc (nand, NAND_CMD_STATUS, -1, -1);
            printf("device is %swrite protected\n",
                   (nand_chip->read_byte(nand) & 0x80 ?
                "NOT " : "" ) );

            for (off = 0; off < nand->size; off += nand->oobblock) {
                int s = nand_get_lock_status(nand, off);

                /* print message only if status has changed * or at end of chip */
                if (off == nand->size - nand->oobblock
                    || (s != last_status && off != 0))  {

                    printf("%08x - %08x: %8d pages %s%s%s\n",
                           block_start,
                           off-1,
                           (off-block_start)/nand->oobblock,
                           ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""),
                           ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""),
                           ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : ""));
                }

                last_status = s;
               }
        } else {
            if (!nand_lock(nand, tight)) {
                puts("NAND flash successfully locked\n");
            } else {
                puts("Error locking NAND flash\n");
                return 1;
            }
        }
        return 0;
    }

    if (strcmp(cmd, "unlock") == 0) {
        if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0)
            return 1;

        if (!nand_unlock(nand, off, size)) {
            puts("NAND flash successfully unlocked\n");
        } else {
            puts("Error unlocking NAND flash, "
                 "write and erase will probably fail\n");
            return 1;
        }
        return 0;
    }

usage:
    printf("Usage:\n%s\n", cmdtp->usage);
    return 1;
}

u-boot 在flash上的存储的内核称为uImage,uimage的格式:头部+真正的内核; 头部主要有:
in-load:加载地址,表示运行内核的时候,内核应该先将其放到哪里;
in_ep:入口地址,表示要运行内核的时候,直接跳到这个地址就可以了;

u-boot分析_uboot启动内核

下面要开始分析如何启动内核,主要是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;

注释开始

这里就是uimage的头部,是一个结构体

typedef struct image_header {
 uint32_t ih_magic; 
 uint32_t ih_hcrc; 
 uint32_t ih_time; 
 uint32_t ih_size; 
 uint32_t ih_load; 表示内核运行的时候你要把它放在哪里。
 uint32_t ih_ep;  运行内核的时候入口地址,之前设置的是0x30007fc0,只要不破坏uboot内存使用分配即可,因为sp后面还有几十M的空间。正是因为uimage中有个header,header结构体中有一个loadaddress。我们把uimage放在某个地址,bootm加上这个地址,去读出头部中的in_load,如果发现内核不在加载地址中,则需要把内核移动到这个加载地址中去,最后跳到in_ep去执行。
 uint32_t ih_dcrc; 
 uint8_t  ih_os;  
 uint8_t  ih_arch; 
 uint8_t  ih_type; 
 uint8_t  ih_comp; 
 uint8_t  ih_name[IH_NMLEN]; 
} image_header_t;

注释结束

 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 lx ...\n", addr);


#ifdef CONFIG_HAS_DATAFLASH
 if (addr_dataflash(addr)){
  read_dataflash(addr, sizeof(image_header_t), (char *)&header);
 } else
#endif
 memmove (&header, (char *)addr, sizeof(image_header_t));

 if (ntohl(hdr->ih_magic) != IH_MAGIC) {
#ifdef __I386__ 
  if (fake_header(hdr, (void*)addr, -1) != NULL) {

   addr -= sizeof(image_header_t);

   verify = 0;
  } else
#endif 
     {
  puts ("Bad Magic Number\n");
  SHOW_BOOT_PROGRESS (-1);
  return 1;
     }
 }
 SHOW_BOOT_PROGRESS (2);

 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);

#ifdef CONFIG_HAS_DATAFLASH
 if (addr_dataflash(addr)){
  len  = ntohl(hdr->ih_size) + sizeof(image_header_t);
  read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
  addr = CFG_LOAD_ADDR;
 }
#endif



 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);

 len_ptr = (ulong *)data;

#if defined(__PPC__)
 if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
 if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
 if (hdr->ih_arch != IH_CPU_I386)
#elif defined(__mips__)
 if (hdr->ih_arch != IH_CPU_MIPS)
#elif defined(__nios__)
 if (hdr->ih_arch != IH_CPU_NIOS)
#elif defined(__M68K__)
 if (hdr->ih_arch != IH_CPU_M68K)
#elif defined(__microblaze__)
 if (hdr->ih_arch != IH_CPU_MICROBLAZE)
#elif defined(__nios2__)
 if (hdr->ih_arch != IH_CPU_NIOS2)
#elif defined(__blackfin__)
 if (hdr->ih_arch != IH_CPU_BLACKFIN)
#elif defined(__avr32__)
 if (hdr->ih_arch != IH_CPU_AVR32)
#else
# error Unknown CPU type
#endif
 {
  printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
  SHOW_BOOT_PROGRESS (-4);
  return 1;
 }
 SHOW_BOOT_PROGRESS (5);

 switch (hdr->ih_type) {
 case IH_TYPE_STANDALONE:
  name = "Standalone Application";

  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]);

  data += 8;
  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);



 iflag = disable_interrupts();

#ifdef CONFIG_AMIGAONEG3SE

 icache_disable();
 invalidate_l1_instruction_cache();
 flush_data_cache();
 dcache_disable();
#endif

 switch (hdr->ih_comp) {
 case IH_COMP_NONE:
  if(ntohl(hdr->ih_load) == addr) {如果ih_load==addr,则打印xip
   printf (" XIP %s ... ", name);
  } else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
   size_t l = len;
   void *to = (void *)ntohl(hdr->ih_load);
   void *from = (void *)data;

   printf (" Loading %s ... ", name);

   while (l > 0) {
    size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
    WATCHDOG_RESET();
    memmove (to, from, tail);
    to += tail;
    from += tail;
    l -= tail;
   }
#else 否则就要移动真正的内核
   memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);这里就是把真正内核开始的data移动到ih_load加载地址中去。韦东山开发板的内核真正地址为0x30008000,而内核头部的起始地址是0x30007fc0,两者相差64字节,正好是头部的长度,这样就不用做搬运真正的内核的工作了。可以加快启动速度。
#endif 
  }
  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;
#ifdef CONFIG_BZIP2
 case IH_COMP_BZIP2:
  printf (" Uncompressing %s ... ", name);

  i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
      &unc_len, (char *)data, len,
      CFG_MALLOC_LEN < (4096 * 1024), 0);
  if (i != BZ_OK) {
   printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
   SHOW_BOOT_PROGRESS (-6);
   udelay(100000);
   do_reset (cmdtp, flag, argc, argv);
  }
  break;
#endif
 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);

 switch (hdr->ih_type) {
 case IH_TYPE_STANDALONE:
  if (iflag)
   enable_interrupts();


  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:

  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);

 switch (hdr->ih_os) {
 default:   
 case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
     fixup_silent_linux();
#endif
     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;

#ifdef CONFIG_LYNXKDI
 case IH_OS_LYNXOS:
     do_bootm_lynxkdi (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
#endif

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

#if (CONFIG_COMMANDS & CFG_CMD_ELF)
 case IH_OS_VXWORKS:
     do_bootm_vxworks (cmdtp, flag, argc, argv,
         addr, len_ptr, verify);
     break;
 case IH_OS_QNX:
     do_bootm_qnxelf (cmdtp, flag, argc, argv,
         addr, len_ptr, verify);
     break;
#endif
#ifdef CONFIG_ARTOS
 case IH_OS_ARTOS:
     do_bootm_artos  (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
#endif
 }

 SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
 puts ("\n## Control returned to monitor - resetting...\n");
 do_reset (cmdtp, flag, argc, argv);
#endif
 return 1;
}

do_bootm有两个作用:
作用1:读取内核头部将内核移动到合适地方,还有一些校验
作用2:启动内核,用的是do_bootm_linux函数。在跳到ih_ep入口之前还要uboot设置内核启动参数,然后才是跳到ih_ep启动内核。

do_bootm_linux函数开始的代码如下:

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
       ulong addr, ulong *len_ptr, int verify)
{
 ulong len = 0, checksum;
 ulong initrd_start, initrd_end;
 ulong data;
 void (*theKernel)(int zero, int arch, uint params);
 image_header_t *hdr = &header;
 bd_t *bd = gd->bd;

#ifdef CONFIG_CMDLINE_TAG
 char *commandline = getenv ("bootargs");
#endif

 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);


 if (argc >= 3) {
  SHOW_BOOT_PROGRESS (9);

  addr = simple_strtoul (argv[2], NULL, 16);

  printf ("## Loading Ramdisk Image at lx ...\n", addr);


#ifdef CONFIG_HAS_DATAFLASH
  if (addr_dataflash (addr)) {
   read_dataflash (addr, sizeof (image_header_t),
     (char *) &header);
  } else
#endif
   memcpy (&header, (char *) addr,
    sizeof (image_header_t));

  if (ntohl (hdr->ih_magic) != IH_MAGIC) {
   printf ("Bad Magic Number\n");
   SHOW_BOOT_PROGRESS (-10);
   do_reset (cmdtp, flag, argc, argv);
  }

  data = (ulong) & header;
  len = sizeof (image_header_t);

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

  if (crc32 (0, (unsigned char *) data, len) != checksum) { printf ("Bad Header Checksum\n"); SHOW_BOOT_PROGRESS (-11); do_reset (cmdtp, flag, argc, argv); }

  SHOW_BOOT_PROGRESS (10);

  print_image_hdr (hdr);

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

#ifdef CONFIG_HAS_DATAFLASH
  if (addr_dataflash (addr)) {
   read_dataflash (data, len, (char *) CFG_LOAD_ADDR);
   data = CFG_LOAD_ADDR;
  }
#endif

  if (verify) {
   ulong csum = 0;

   printf (" Verifying Checksum ... ");
   csum = crc32 (0, (unsigned char *) data, len);
   if (csum != ntohl (hdr->ih_dcrc)) {
    printf ("Bad Data CRC\n");
    SHOW_BOOT_PROGRESS (-12);
    do_reset (cmdtp, flag, argc, argv);
   }
   printf ("OK\n");
  }

  SHOW_BOOT_PROGRESS (11);

  if ((hdr->ih_os != IH_OS_LINUX) ||
      (hdr->ih_arch != IH_CPU_ARM) ||
      (hdr->ih_type != IH_TYPE_RAMDISK)) {
   printf ("No Linux ARM Ramdisk Image\n");
   SHOW_BOOT_PROGRESS (-13);
   do_reset (cmdtp, flag, argc, argv);
  }

#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)

  memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
  data = ntohl(hdr->ih_load);
#endif


 } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
  ulong tail = ntohl (len_ptr[0]) % 4;
  int i;

  SHOW_BOOT_PROGRESS (13);


  data = (ulong) (&len_ptr[2]);

  for (i = 1; len_ptr[i]; ++i)
   data += 4;

  data += ntohl (len_ptr[0]);
  if (tail) {
   data += 4 - tail;
  }

  len = ntohl (len_ptr[1]);

 } else {

  SHOW_BOOT_PROGRESS (14);

  len = data = 0;
 }

#ifdef DEBUG
 if (!data) { printf ("No initrd\n"); }
#endif

 if (data) { initrd_start = data; initrd_end = initrd_start + len; } else { initrd_start = 0; initrd_end = 0; }

 SHOW_BOOT_PROGRESS (15);

 debug ("## Transferring control to Linux (at address lx) ...\n",
        (ulong) theKernel);

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \ uboot设置参数在这里
    defined (CONFIG_CMDLINE_TAG) || \
    defined (CONFIG_INITRD_TAG) || \
    defined (CONFIG_SERIAL_TAG) || \
    defined (CONFIG_REVISION_TAG) || \
    defined (CONFIG_LCD) || \
    defined (CONFIG_VFD)
 setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
 setup_serial_tag (&params);
#endif
#ifdef CONFIG_REVISION_TAG
 setup_revision_tag (&params);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
 setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
 setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
 if (initrd_start && initrd_end)
  setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
 setup_videolfb_tag ((gd_t *) gd);
#endif
 setup_end_tag (bd);
#endif


 printf ("\nStarting kernel ...\n\n");

#ifdef CONFIG_USB_DEVICE
 {
  extern void udc_disconnect (void);
  udc_disconnect ();
 }
#endif

 cleanup_before_linux ();

 theKernel (0, bd->bi_arch_number, bd->bi_boot_params);启动内核在这里
}

do_bootm_linux

作用1:设置内核启动参数,参数的格式是tag,对于韦东山的开发板地址是0x30000100,下面分析两个参数,其中setup_start_tag和setup_end_tag是必须的。

static void setup_start_tag (bd_t *bd)
{
 params = (struct tag *) bd->bi_boot_params;bi_boot_params在代码中搜索发现是

 params->hdr.tag = ATAG_CORE;
 params->hdr.size = tag_size (tag_core);

 params->u.core.flags = 0;
 params->u.core.pagesize = 0;
 params->u.core.rootdev = 0;

 params = tag_next (params);
}

作用2:跳到入口地址去是
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
这样就启动内核了!!!
具体bi_boot_params是多少搜索代码可以知道,其实韦东山是在自己的100ask24x0.c中自己定义的。
setup_start_tag之后我们得到:

static void setup_start_tag (bd_t *bd)
{
 params = (struct tag *) bd->bi_boot_params;

 params->hdr.tag = ATAG_CORE;
 params->hdr.size = tag_size (tag_core);

 params->u.core.flags = 0;
 params->u.core.pagesize = 0;
 params->u.core.rootdev = 0;

 params = tag_next (params);
}

0x30000100|size|tag|flag|page_size|root_dev|
其中header_size=sizeof(struct tag_header) + (sizeof(struct type) >> 2)
也就是说执行完这个函数之后,要明白在内核启动参数区域都放了大小是多少的参数。
执行完setup_memory_tag函数之后:

#ifdef CONFIG_SETUP_MEMORY_TAGS
static void setup_memory_tags (bd_t *bd)
{
 int i;

 for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
  params->hdr.tag = ATAG_MEM;
  params->hdr.size = tag_size (tag_mem32);

  params->u.mem.start = bd->bi_dram[i].start;
  params->u.mem.size = bd->bi_dram[i].size;

  params = tag_next (params);
 }
}
#endif

这时存储内核启动参数的区域类似于setup_start_tag
开始是size|tag|size|start|
下面是start_commandline_tag

static void setup_commandline_tag (bd_t *bd, char *commandline)
{
 char *p;

 if (!commandline)
  return;

 把命令前面的空格给干掉
 for (p = commandline; *p == ' '; p++);


 if (*p == '\0')
  return;

 params->hdr.tag = ATAG_CMDLINE;
 params->hdr.size =
  (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;

 strcpy (params->u.cmdline.cmdline, p);

 params = tag_next (params);
}

commandline被传入一个参数*commandline,而这个参数是getev(“bootargs”),用print命令在uboot命令行中查看bootargs,其中包括了内核的console的信息从哪里打印出来,是同ttyssa0也即串口0打出来。
size|tag|bootargs
最后一个是setup_end_tag-

static void setup_end_tag (bd_t *bd)
{
 params->hdr.tag = ATAG_NONE;
 params->hdr.size = 0;
}

这两个参数全是0。