Chinaunix首页 | 论坛 | 博客
  • 博客访问: 328913
  • 博文数量: 67
  • 博客积分: 668
  • 博客等级: 上士
  • 技术积分: 1591
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-16 22:08
文章分类

全部博文(67)

文章存档

2015年(1)

2014年(13)

2013年(28)

2012年(23)

2011年(2)

分类: 嵌入式

2012-02-09 20:05:17

main_loop函数,将一些没用用到的条件编译去掉后,大致如下。

  1. void main_loop (void)
  2. {
  3. #ifndef CONFIG_SYS_HUSH_PARSER
  4.     static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };//定义命令字符串等变量
  5.     int len;
  6.     int rc = 1;
  7.     int flag;
  8. #endif

  9. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  10.     char *s;
  11.     int bootdelay;
  12. #endif

  13. #ifdef CONFIG_SYS_HUSH_PARSER
  14.     u_boot_hush_start (); //初始化hush功能,?
  15. #endif

  16. #ifdef CONFIG_AUTO_COMPLETE
  17.     install_auto_complete(); //安装自动补全的函数。?
  18. #endif

  19. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  20.     s = getenv ("bootdelay"); //从环境变量中获取bootdelay延迟时间
  21.     bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
  22.     //若环境变量中有值则使用,否则使用配置的默认值

  23.     debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
  24.         s = getenv ("bootcmd"); //获取引导命令
  25.     debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "");
  26.     if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
  27.     //延时大于0,abortboot函数将延时bootdelay的时间并返回是否有按键按下。

  28. # ifndef CONFIG_SYS_HUSH_PARSER
  29.         run_command (s, 0); //执行环境变量中保存的开机后定义的要执行的命令
  30. # else
  31.         parse_string_outer(s, FLAG_PARSE_SEMICOLON |
  32.                     FLAG_EXIT_FROM_LOOP);
  33. # endif
  34.     }
  35. #endif /* CONFIG_BOOTDELAY */

  36.     /*
  37.      * Main Loop for Monitor Command Processing
  38.      */
  39. #ifdef CONFIG_SYS_HUSH_PARSER
  40.     parse_file_outer(); //执行命令,该函数一般不返回 就等待执行命令
  41.     /* This point is never reached */
  42.     for (;;);
  43. #else
  44.     for (;;) {
  45.         len = readline (CONFIG_SYS_PROMPT);

  46.         flag = 0; /* assume no special flags for now */
  47.         if (len > 0)
  48.             strcpy (lastcommand, console_buffer);
  49.         else if (len == 0)
  50.             flag |= CMD_FLAG_REPEAT;

  51.         if (len == -1)
  52.             puts ("\n");
  53.         else
  54.             rc = run_command (lastcommand, flag);

  55.         if (rc <= 0) {
  56.             /* invalid command or not repeatable, forget it */
  57.             lastcommand[0] = 0;
  58.         }
  59.     }
  60. #endif /*CONFIG_SYS_HUSH_PARSER*/
  61. }

这个函数其实必须完成的任务并不多 主要是初始化语法功能,安装一些补全命令函数,从环境变量中获取启动延时值,延时中检测是否有按键按下,然后如果没有按键按下就执行bootcmd中的命令,否则进入等待执行命令状态。?

下面简要分析一下引导内核的启动
uboot
启动后不按键会执行bootcmd环境变量中定义的命令,默认值在configs/mini2440.h头文件中定义。bootcmd中一般会包含bootm命令。此命令引导内核。执行bootm命令的函数是do_bootm.,然后调用bootm_start获取内核必要信息,下面贴出此函数并简要注释一下。

  1. static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  2.  {
  3.          ulong mem_start;
  4.          phys_size_t mem_size;
  5.          void *os_hdr;
  6.          int ret;
  7.  
  8.          memset ((void *)&images, 0, sizeof (images)); //将保存内核镜像信息的结构体清空。images是一个bootm_headers_t 类型的全局变量,这个结构体中又包含了image_header_t。
  9.          images.verify = getenv_yesno ("verify"); //是否需要校验镜像 从环境变量中获取是否
  10.  
  11.          lmb_init(&images.lmb); //初始化lmb
  12.  
  13.          mem_start = getenv_bootm_low(); //获取内存起始地址
  14.          mem_size = getenv_bootm_size(); //获取内存大小
  15.  
  16.          lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size); //添加信息到lmb中
  17.  
  18.          arch_lmb_reserve(&images.lmb); //
  19.          board_lmb_reserve(&images.lmb); //
  20.  
  21.          /* get kernel image header, start address and length */
  22.          os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
  23.                          &images, &images.os.image_start, &images.os.image_len);
  24.     //此函数会从指定地址尝试获得内核镜像,校验镜像头部信息,填充images结构体,此函数返回映像起始地址,稍后详解
  25.          if (images.os.image_len == 0) {
  26.                  puts ("ERROR: can't get kernel image!\n");
  27.                  return 1;
  28.          }

  29.     /*此后的部分也都是一些内核信息的获取设置,在这仅贴出源码中重要注释 */
  30.      /* get image parameters */ //这一部分将填充images.os中的数据.

  31.     /* find kernel entry point */ //设置images.ep

  32.      images.os.start = (ulong)os_hdr;
  33.          images.state = BOOTM_STATE_START;
  34.     return 0;
  35. }

boot_start函数分析完毕 

boot_get_kernel
分析,只将函数中涉及到重要的部分保留

  1. static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
  2.                  bootm_headers_t *images, ulong *os_data, ulong *os_len)
  3. {
  4.  if (argc < 2) {
  5.                   img_addr = load_addr;
  6.     //获得映像加载地址,load_addr定义:ulong load_addr = CONFIG_SYS_LOAD_ADDR
  7.                   debug ("* kernel: default image load address = 0x%08lx\n",
  8.                                   load_addr);
  9.         }
  10. /* copy from dataflash if needed */
  11.          img_addr = genimg_get_image (img_addr);
  12.     //此函数检测img_addr地址是在dataflash上,如果是就移动到RAM中,如果在RAM则直接返回原值.

  13.     //检测映像的格式,基本思想是根据加载地址赋值给结构体,利用结构体中的位置结构到相应的地址处检查
  14. switch (genimg_get_format ((void *)img_addr)) {
  15. case IMAGE_FORMAT_LEGACY:
  16.                  printf ("## Booting kernel from Legacy Image at %08lx ...\n",
  17.                                  img_addr);
  18.                  hdr = image_get_kernel (img_addr, images->verify);//此函数根据加载地址,检测镜像正确性。再以iamge_header_t的形式返回映像头部地址。
  19.                  if (!hdr)
  20.                          return NULL;
  21.                  show_boot_progress (5);
  22.  
  23.                  /* get os_data and os_len */
  24.                  switch (image_get_type (hdr)) {
  25.                  case IH_TYPE_KERNEL:
  26.                          *os_data = image_get_data (hdr); //得到内核起始地址,os_data是上层函数传过来的image结构成员的指针,所以会写到image结构中.
  27.                          *os_len = image_get_data_size (hdr); //得到内核大小
  28.                          break;

  29.         }
  30.  /*
  31.                   * copy image header to allow for image overwrites during kernel
  32.                   * decompression.
  33.                   */
  34.                  memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t)); //拷贝映像头信息
  35. /* save pointer to image header */
  36.                  images->legacy_hdr_os = hdr;
  37.  
  38.                  images->legacy_hdr_valid = 1;
  39. }
  40.         return (void *)img_addr; //返回映像加载地址
  41.  
  42. }

boot_get_kernel分析完毕

再回到do_bootm函数,调用bootm_load_os函数。此函数检测内核镜像是否压缩,如果没有压缩,则查看当下的内核起始地址os.image_start(映像加载地址+映像头长度得到) 是否和映像头中指定的地址相同,如果不同则移动到指定的地址。如果有压缩则根据压缩类型解压缩到映像头中指定的内核起始地址。bootm_load_os函数任务完毕。
do_bootm
函数后期关键一句boot_fn = boot_os[images.os.os]; boot_os是一个boot_os_fn类型函数指针的数组。linux对应的函数为do_bootm_linux。此函数在lib_arm/bootm.c中,分析此函数。
此函数平台系统相关,是为启动linux作最后设置,获取machid,和将参数信息写入bd->bi_boot_params所指的地址。最终使用 theKernel (0, machid, bd->bi_boot_params);跳到内核。函数如下(删除了不编译的部分)

  1. int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
  2.  {
  3.          bd_t *bd = gd->bd;
  4.          char *s;
  5.          int machid = bd->bi_arch_number;
  6.          void (*theKernel)(int zero, int arch, uint params);
  7.  
  8.  #ifdef CONFIG_CMDLINE_TAG
  9.          char *commandline = getenv ("bootargs"); //从环境变量中获得bootargs
  10.  #endif
  11.  
  12.          if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
  13.                  return 1;
  14.  
  15.          theKernel = (void (*)(int, int, uint))images->ep; //将内核入口点转成theKernel函数,用于启动内核
  16.     
  17.          s = getenv ("machid"); //如果环境变量中有机器ID则使用环境变量中的
  18.          if (s) {
  19.                  machid = simple_strtoul (s, NULL, 16);
  20.                  printf ("Using machid 0x%x from environment\n", machid);
  21.          }
  22.     
  23.          show_boot_progress (15);
  24.  
  25.          debug ("## Transferring control to Linux (at address %08lx) ...\n",
  26.                 (ulong) theKernel);
  27.  
  28.  #if defined (CONFIG_SETUP_MEMORY_AGS) || \
  29.      defined (CONFIG_CMDLINE_TAG) || \
  30.      defined (CONFIG_INITRD_TAG) || \
  31.      defined (CONFIG_SERIAL_TAG) || \
  32.      defined (CONFIG_REVISION_TAG) || \
  33.      defined (CONFIG_LCD) || \
  34.      defined (CONFIG_VFD)
  35.     //将开始节点信息写到bd->bi_boot_params地址,此地址在board_init函数中设置为0x30000100,此后的节点信息都依次写入,linux内核将从此地址处读取需要的参数
  36.          setup_start_tag (bd);
  37.  #ifdef CONFIG_SETUP_MEMORY_TAGS
  38.          setup_memory_tags (bd); //写内存地址和大小信息的节点
  39.  #endif
  40.  #ifdef CONFIG_CMDLINE_TAG
  41.          setup_commandline_tag (bd, commandline); //bootargs写入节点
  42.  #endif
  43.  #ifdef CONFIG_INITRD_TAG
  44.          if (images->rd_start && images->rd_end)
  45.                  setup_initrd_tag (bd, images->rd_start, images->rd_end);
  46.  #endif
  47.          setup_end_tag (bd); //写结束节点
  48.  #endif
  49.  
  50.          /* we assume that the kernel is in place */
  51.          printf ("\nStarting kernel ...\n\n");
  52.  
  53.  #ifdef CONFIG_USB_DEVICE
  54.          {
  55.                  extern void udc_disconnect (void);
  56.                  udc_disconnect ();
  57.          }
  58.  #endif
  59.  
  60.          cleanup_before_linux (); //关中断,cache等
  61.  
  62.          theKernel (0, machid, bd->bi_boot_params); //跳转到内核.
  63.          /* does not return */
  64.  
  65.          return 1;
  66.  }

uboot就此结束。

阅读(5222) | 评论(0) | 转发(8) |
给主人留下些什么吧!~~