bootm命令分析


bootm命令函数do_bootm位于Cmd_bootm.c,其流程为:

确定是否要校验uImage和ramdisk里的数据,默认校验,若想不校验:设置了环境变量verify=n。 

s = getenv ("verify");
 verify = (s && (*s == 'n')) ? 0 : 1;

判断命令是否制定了 操作系统 的加载地址。如果没有,使用默认地址CFG_LOAD_ADDR,一般CFG_LOAD_ADDR可以在include/configs文件夹自己单板的配置文件里配置。

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


根据image 加载地址是在 flash还是内存,获取image的 头信息。

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

校验头里的MAGIC NUM和CRC是否正确,头部的CRC计算内容是 :头部64B,且头部CRC处按内容0来计算。

if (ntohl(hdr->ih_magic) != IH_MAGIC) {
     {

  .............................................

  puts ("Bad Magic Number\n");
  SHOW_BOOT_PROGRESS (-1);
  return 1;
     }
 }
);

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

 

根据image type进行不同的处理,如果是linux kernel后面再处理:

 switch (hdr->ih_type) {

  .............................................
  case IH_TYPE_KERNEL:
  name = "Kernel Image";
  break;

 .............................................

 }

判断压缩类型,做不同拷贝处理。
未压缩:
如果镜像中load地址与第一个参数一致的话,意思是内核已经在loadaddr准备好了,无需处理。

如果镜像中load地址与第一个参数不一致的话,就要从传递的地址拷贝到 image指定的loadaddr(hdr->ih_load)了。

如果是压缩类型gzip或者bzip2:

就调用相应解压缩函数,将镜像解压到image指定的loadaddr(hdr->ih_load)了。默认预留解压后的大小为8M。当然这个空间大小可以使用这个宏定义CFG_BOOTM_LEN来修改

 switch (hdr->ih_comp) {
 case IH_COMP_NONE:
  if(ntohl(hdr->ih_load) == addr) {
   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 /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
   memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
  }
  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;
 .............................................

 }

判断如果是内核,暂时不用处理


 switch (hdr->ih_type) {
 .............................................
 case IH_TYPE_KERNEL:
 case IH_TYPE_MULTI:
  /* handled below */
  break;

 

根据镜像的不同类型,调用不同的操作系统启动函数。


 switch (hdr->ih_os) {
 default:   /* handled by (original) Linux case */
 case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
     fixup_silent_linux();
#endif
     do_bootm_linux  (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;

 .............................................


如果linux操作系统就调用do_bootm_linux ,其流程为:

如果定义了CONFIG_CMDLINE_TAG,就从环境变量取出bootargs,准备传参数给内核。#ifdef CONFIG_CMDLINE_TAG


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

 

从镜像文件的头里提取entry point,即内核的入口地址。

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

 

确认命令是否传递了initrd地址,如果有进行相应的处理

校验ramdisk的magic num和头的CRC


打印头并校验数据CRC

/*
  * Check if there is an initrd image
  */
 if (argc >= 3) {

 .............................................

}

根据配置,准备向内核传递相应的参数。

setup_end_tag (bd);
#endif

关闭cache和中断,启动内核。

bd->bi_boot_params:传递给内核参数地址。tag类型
bd->bi_arch_number:CPU类型

在各自单板的board_init函数中初始化。

 cleanup_before_linux ();

 theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

相关内容