前面我们分析了uboot 的整个流程,我们知道uboot启动以后所有功能都是通过命令来实现的,启动kernel就是执行了bootcmd里面的命令。命令执行过程在uboot中是非常重要的现在我们就来看uboot命令的实现过程。

在main_loop()代码 中可以知道,uboot处理命令的函数是run_command()

代码在bootable\bootloader\uboot-imx\common\main.c中

  1. int run_command (const char *cmd, int flag)
  2. {
  3. cmd_tbl_t *cmdtp;
  4. char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
  5. char *token; /* start of token in cmdbuf */
  6. char *sep; /* end of token (separator) in cmdbuf */
  7. char finaltoken[CONFIG_SYS_CBSIZE];
  8. char *str = cmdbuf;
  9. char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
  10. int argc, inquotes;
  11. int repeatable = 1;
  12. int rc = 0;
  13.  
  14. #ifdef DEBUG_PARSER
  15. printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
  16. puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
  17. puts ("\"\n");
  18. #endif
  19.  
  20. clear_ctrlc(); /* forget any previous Control C 清除所有的ctrl+c*/
  21.  
  22. if (!cmd || !*cmd) {
  23. return -1; /* 命令为空就返回 */
  24. }
  25.  
  26. if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
  27. puts ("##命令过长\n");
  28. return -1;
  29. }
  30.  
  31. strcpy (cmdbuf, cmd);//拷贝到buff中
  32.  
  33. /* Process separators and check for invalid
  34. * repeatable commands
  35. */
  36.  
  37. #ifdef DEBUG_PARSER
  38. printf ("[PROCESS_SEPARATORS] %s\n", cmd);
  39. #endif
  40. while (*str) {
  41.  
  42. /*
  43. * Find separator, or string end
  44. * Allow simple escape of ';' by writing "\;"
  45. */
  46. //提取出一条命令,命令的结尾是';'或者‘\’
  47. //注意这里的';'也就是说如果要一次输入多条命令的话,可以用';'隔开
  48. for (inquotes = 0, sep = str; *sep; sep++) {
  49. if ((*sep=='\'') &&
  50. (*(sep-1) != '\\'))
  51. inquotes=!inquotes;
  52.  
  53. if (!inquotes &&
  54. (*sep == ';') && /* separator */
  55. ( sep != str) && /* past string start */
  56. (*(sep-1) != '\\')) /* and NOT escaped */
  57. break;
  58. }
  59.  
  60. /*
  61. * Limit the token to data between separators
  62. */
  63. token = str;
  64. if (*sep) {
  65. str = sep + 1; /* start of command for next pass */
  66. *sep = '\0';
  67. }
  68. else
  69. str = sep; /* no more commands for next pass */
  70. #ifdef DEBUG_PARSER
  71. printf ("token: \"%s\"\n", token);
  72. #endif
  73.  
  74. /* find macros in this token and replace them */
  75. //处理一些宏定义比如字符串引用时候的“${}”
  76. //命令分行符'\'等等
  77. process_macros (token, finaltoken);
  78.  
  79. /* Extract arguments */
  80. //将命令按照格式分配到argv数组中
  81. //比如我们输入的命令 bootdelay=3;
  82. //转换以后我们得到argv[0]="bootdelay"
  83. //argv[1]="3"
  84. if ((argc = parse_line (finaltoken, argv)) == 0) {
  85. rc = -1; /* no command at all */
  86. continue;
  87. }
  88.  
  89. /* Look up command in command table */
  90. if ((cmdtp = find_cmd(argv[0])) == NULL) {//查找这条命令并得到描述它的结构体
  91. printf ("Unknown command '%s' - try 'help'\n", argv[0]);
  92. rc = -1; /* give up after bad command */
  93. continue;
  94. }

这里我们来分析一下uboot是怎么查找命令的也就是find_cmd()这个函数是怎么实现的。

  1. cmd_tbl_t *find_cmd (const char *cmd)
  2. {
  3. int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
  4. return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
  5. }

//这里的两个参数__u_boot_cmd_end,__u_boot_cmd_start都是在u-boot.lds中定义的
//是uboot所有命令的结构体的一个列表的起始地址和结束地址。

  1. /***************************************************************************
  2. * find command table entry for a command
  3. */
  4. cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
  5. {
  6. cmd_tbl_t *cmdtp;
  7. cmd_tbl_t *cmdtp_temp = table; /*Init value */
  8. const char *p;
  9. int len;
  10. int n_found = 0;
  11.  
  12. /*
  13. * Some commands allow length modifiers (like "cp.b");
  14. * compare command name only until first dot.
  15. */
  16. len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
  17. //从首地址开始逐条往下查找,看能否找到相应的命令,如果找到就返回相应命令的结构体指针,找不到就返回NULL
  18. for (cmdtp = table;
  19. cmdtp != table + table_len;
  20. cmdtp++) {
  21. if (strncmp (cmd, cmdtp->name, len) == 0) {
  22. if (len == strlen (cmdtp->name))
  23. return cmdtp; /* full match */
  24.  
  25. cmdtp_temp = cmdtp; /* abbreviated command ? */
  26. n_found++;
  27. }
  28. }
  29. if (n_found == 1) { /* exactly one match */
  30. return cmdtp_temp;
  31. }
  32.  
  33. return NULL; /* not found or ambiguous command */
  34. }

到了这里我们得到了命令的结构体指针,就可以与命令的处理函数来交互了。下面继续看run_command()

  1. /* found - check max args */
  2. //检测最大参数个数
  3. if (argc > cmdtp->maxargs) {
  4. cmd_usage(cmdtp);
  5. rc = -1;
  6. continue;
  7. }
  8.  
  9. #if defined(CONFIG_CMD_BOOTD)
  10. /* avoid "bootd" recursion */
  11. if (cmdtp->cmd == do_bootd) {
  12. #ifdef DEBUG_PARSER
  13. printf ("[%s]\n", finaltoken);
  14. #endif
  15. if (flag & CMD_FLAG_BOOTD) {
  16. puts ("'bootd' recursion detected\n");
  17. rc = -1;
  18. continue;
  19. } else {
  20. flag |= CMD_FLAG_BOOTD;
  21. }
  22. }
  23. #endif
  24.  
  25. /* OK - call function to do the command */
  26. if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {//执行命令处理函数
  27. rc = -1;
  28. }
  29.  
  30. repeatable &= cmdtp->repeatable;
  31.  
  32. /* Did the user stop this? */
  33. //处理用户输入的ctrl+c
  34. if (had_ctrlc ())
  35. return -1; /* if stopped then not repeatable */
  36. }
  37.  
  38. return rc ? rc : repeatable;
  39. }

到了这里命令的处理流程就完成了。下面我们重点来说明一下,cmdtp是怎么定义的。

cmd_tbl_t *cmdtp;

  1. struct cmd_tbl_s {
  2. char *name; /* Command Name命令名称 */
  3. int maxargs; /* maximum number of arguments 最大参数个数*/
  4. int repeatable; /* autorepeat allowed? 是否允许重复输入 */
  5. /* Implementation function */
  6. int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);//命令处理函数
  7. char *usage; /* Usage message(short) 比较短的帮助信息*/
  8. #ifdef CONFIG_SYS_LONGHELP
  9. char *help; /* Help message(long)比较长的帮助信息 */
  10. #endif
  11. #ifdef CONFIG_AUTO_COMPLETE
  12. /* do auto completion on the arguments */
  13. int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
  14. #endif
  15. };

可以看出命令的结构体包含这些项中有一个是命令处理函数,这会命令跟函数关联的重点。那么每个命令的结构体是在哪定义的呢?这里涉及到一个宏U_BOOT_CMD(),这个宏就是定义一个这样的结构体然后,编译的时候,编译器会自动将这个结构体计入到命令列表里面去。我们以bootcmd为例子来屡一下整个过程

在我用的板子上有环境变量bootcmd=booti mmc3,也就是说,系统启动的时候要执行booti mmc3这条命令,那么booti的结构体的定义在\bootable\bootloader\uboot-imx\common\cmd_bootm.c

  1. U_BOOT_CMD(
  2. booti, 3, 1, do_booti,
  3. "booti - boot android bootimg from memory\n",
  4. "[<addr> | mmc0 | mmc1 | mmc2 | mmcX] [<partition>] \n - boot application image stored in memory or mmc\n"
  5. "\t'addr' should be the address of boot image which is zImage+ramdisk.img\n"
  6. "\t'mmcX' is the mmc device you store your boot.img, which will read the boot.img from 1M offset('/boot' partition)\n"
  7. "\t 'partition' (optional) is the partition id of your device, if no partition give, will going to 'boot' partition\n"
  8. );

可知booti的处理函数是do_booti

  1. /* booti <addr> [ mmc0 | mmc1 [ <partition> ] ] */
  2. int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  3. {
  4. unsigned addr = 0;
  5. char *ptn = "boot";
  6. int mmcc = -1;
  7. boot_img_hdr *hdr = (void *)boothdr;
  8.  
  9. if (argc < 2) //至少有1个有效参数
  10. return -1;
  11.  
  12. if (!strncmp(argv[1], "mmc", 3))
  13. mmcc = simple_strtoul(argv[1]+3, NULL, 10); //得到启动的mmc名字
  14. else
  15. addr = simple_strtoul(argv[1], NULL, 16);
  16.  
  17. if (argc > 2)
  18. ptn = argv[2];
  19.  
  20. if (mmcc != -1) {
  21. #ifdef CONFIG_MMC
  22. struct fastboot_ptentry *pte;
  23. struct mmc *mmc;
  24. disk_partition_t info;
  25. block_dev_desc_t *dev_desc = NULL;
  26. unsigned sector, partno = -1;
  27.  
  28. memset((void *)&info, 0 , sizeof(disk_partition_t));
  29. /* i.MX use MBR as partition table, so this will have
  30. to find the start block and length for the
  31. partition name and register the fastboot pte we
  32. define the partition number of each partition in
  33. config file
  34. */
  35. mmc = find_mmc_device(mmcc); //查找启动的mmc设备
  36. if (!mmc) {
  37. printf("booti: cannot find '%d' mmc device\n", mmcc);
  38. goto fail;
  39. }
  40. dev_desc = get_dev("mmc", mmcc);//得到设备描述
  41. if (NULL == dev_desc) {
  42. printf("** Block device MMC %d not supported\n", mmcc);
  43. goto fail;
  44. }
  45.  
  46. /* below was i.MX mmc operation code */
  47. if (mmc_init(mmc)) { //初始化mmc
  48. printf("mmc%d init failed\n", mmcc);
  49. goto fail;
  50. }
  51.  
  52. #ifdef CONFIG_ANDROID_BOOT_PARTITION_MMC
  53. #ifdef CONFIG_ANDROID_RECOVERY_PARTITION_MMC
  54. if (!strcmp(ptn, "boot"))
  55. partno = CONFIG_ANDROID_BOOT_PARTITION_MMC;
  56. if (!strcmp(ptn, "recovery"))
  57. partno = CONFIG_ANDROID_RECOVERY_PARTITION_MMC;
  58.  
  59. if (get_partition_info(dev_desc, partno, &info)) { //获取mmc的分区信息
  60. printf("booti: device don't have such partition:%s\n", ptn);
  61. goto fail;
  62. }
  63. #endif
  64. #endif
  65.  
  66. #ifdef CONFIG_FASTBOOT
  67. 。。。。。。。。//没配置fastboot
  68. #else
  69. // 这里首先说明一下bootimage的构成
  70. /*
  71. ** +-----------------+
  72. ** | boot header | 1 page
  73. ** +-----------------+
  74. ** | kernel | n pages
  75. ** +-----------------+
  76. ** | ramdisk | m pages
  77. ** +-----------------+
  78. ** | second stage | o pages
  79. ** +-----------------+
  80. **
  81. ** n = (kernel_size + page_size - 1) / page_size
  82. ** m = (ramdisk_size + page_size - 1) / page_size
  83. ** o = (second_size + page_size - 1) / page_size
  84. **
  85. ** 0. all entities are page_size aligned in flash
  86. ** 1. kernel and ramdisk are required (size != 0)
  87. ** 2. second is optional (second_size == 0 -> no second)
  88. ** 3. load each element (kernel, ramdisk, second) at
  89. ** the specified physical address (kernel_addr, etc)
  90. ** 4. prepare tags at tag_addr. kernel_args[] is
  91. ** appended to the kernel commandline in the tags.
  92. ** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
  93. ** 6. if second_size != 0: jump to second_addr
  94. ** else: jump to kernel_addr
  95. */
  96. if (mmc->block_dev.block_read(mmcc, info.start,//读取mmc的第一块数据,即boot header
  97. 1, (void *)hdr) < 0) {
  98. printf("booti: mmc failed to read bootimg header\n");
  99. goto fail;
  100. }
  101. /* flush cache after read */
  102. //这是个空函数
  103. flush_cache((ulong)hdr, 512); /* FIXME *///
  104.  
  105. if (memcmp(hdr->magic, BOOT_MAGIC, 8)) {//看是否是android
  106. printf("booti: bad boot image magic\n");
  107. goto fail;
  108. }
  109.  
  110. sector = info.start + (hdr->page_size / 512);//得到kernel的首地址
  111. #endif
  112. if (mmc->block_dev.block_read(mmcc, sector, //读取kernel到内存中
  113. (hdr->kernel_size / 512) + 1,
  114. (void *)hdr->kernel_addr) < 0) {
  115. printf("booti: mmc failed to read kernel\n");
  116. goto fail;
  117. }
  118. /* flush cache after read */
  119. flush_cache((ulong)hdr->kernel_addr, hdr->kernel_size); /* FIXME */
  120. sector += ALIGN_SECTOR(hdr->kernel_size, hdr->page_size) / 512;得到ramdisk的首地址
  121. if (mmc->block_dev.block_read(mmcc, sector, //读取ramdisk到内存中
  122. (hdr->ramdisk_size / 512) + 1,
  123. (void *)hdr->ramdisk_addr) < 0) {
  124. printf("booti: mmc failed to read kernel\n");
  125. goto fail;
  126. }
  127. /* flush cache after read */
  128. flush_cache((ulong)hdr->ramdisk_addr, hdr->ramdisk_size); /* FIXME */
  129. #else
  130. return -1;
  131. #endif
  132. } else {
  133. //如果没有得到mmc设备,就从参数中得到bootimage的地址然后初始化hdr
  134. unsigned kaddr, raddr;
  135.  
  136. /* set this aside somewhere safe */
  137. memcpy(hdr, (void *) addr, sizeof(*hdr));
  138.  
  139. if (memcmp(hdr->magic, BOOT_MAGIC, 8)) {
  140. printf("booti: bad boot image magic\n");
  141. return 1;
  142. }
  143.  
  144. bootimg_print_image_hdr(hdr);
  145.  
  146. kaddr = addr + hdr->page_size;
  147. raddr = kaddr + ALIGN_SECTOR(hdr->kernel_size, hdr->page_size);
  148.  
  149. memmove((void *) hdr->kernel_addr, (void *)kaddr, hdr->kernel_size);
  150. memmove((void *) hdr->ramdisk_addr, (void *)raddr, hdr->ramdisk_size);
  151. }
  152.  
  153. printf("kernel @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size);
  154. printf("ramdisk @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size);
  155.  
  156. do_booti_linux(hdr);//启动内核
  157.  
  158. puts ("booti: Control returned to monitor - resetting...\n");
  159. do_reset (cmdtp, flag, argc, argv);
  160. return 1;
  161.  
  162. fail:
  163. #ifdef CONFIG_FASTBOOT
  164. return do_fastboot(NULL, 0, 0, NULL);
  165. #else
  166. return -1;
  167. #endif
  168. }

这个函数主要就是把boot.img文件按照格式读取到hdr结构体中传递给do_booti_linux(hdr);进而进一步启动内核我们想继续看do_booti_linux()

  1. void do_booti_linux (boot_img_hdr *hdr)
  2. {
  3. ulong initrd_start, initrd_end;
  4. void (*theKernel)(int zero, int arch, uint params);
  5. bd_t *bd = gd->bd;
  6. #ifdef CONFIG_CMDLINE_TAG
  7. char *commandline = getenv("bootargs");//读取环境变量中的bootargs
  8.  
  9. /* If no bootargs env, just use hdr command line */
  10. if (!commandline)
  11. commandline = (char *)hdr->cmdline;
  12.  
  13. /* XXX: in production, you should always use boot.img 's cmdline !!! */
  14.  
  15. printf("kernel cmdline: \n\tuse %s command line:\n\t%s \n",
  16. getenv("bootargs") ? "uboot" : "boot.img",
  17. commandline);
  18. #endif
  19.  
  20. theKernel = (void (*)(int, int, uint))(hdr->kernel_addr);//得到内存中kernel的main函数指针
  21.  
  22. initrd_start = hdr->ramdisk_addr;
  23. initrd_end = initrd_start + hdr->ramdisk_size;
  24.  
  25. #if defined (CONFIG_SETUP_MEMORY_TAGS)
  26. setup_start_tag(bd);
  27. #ifdef CONFIG_SERIAL_TAG
  28. setup_serial_tag ms);
  29. #endif
  30. #ifdef CONFIG_REVISION_TAG
  31. setup_revision_tag ms);
  32. #endif
  33. #ifdef CONFIG_SETUP_MEMORY_TAGS
  34. setup_memory_tags (bd);
  35. #endif
  36. #ifdef CONFIG_CMDLINE_TAG
  37. setup_commandline_tag (bd, commandline);//将命令行参数加入到bd中
  38. #endif
  39. #ifdef CONFIG_INITRD_TAG
  40. if (hdr->ramdisk_size)
  41. setup_initrd_tag (bd, initrd_start, initrd_end);
  42. #endif
  43. #if defined (CONFIG_VFD) || defined (CONFIG_LCD)
  44. setup_videolfb_tag ((gd_t *) gd);
  45. #endif
  46. setup_end_tag (bd);
  47. #endif
  48.  
  49. /* we assume that the kernel is in place */
  50. printf ("\nStarting kernel ...\n\n");
  51.  
  52. #ifdef CONFIG_USB_DEVICE
  53. {
  54. extern void udc_disconnect (void);
  55. udc_disconnect ();
  56. }
  57. #endif
  58.  
  59. cleanup_before_linux ();
  60.  
  61. theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//直接传递参数启动kernel
  62. }

到了这里uboot就已经驱动了kernel。

整个过程比较简单,关键是uboot命令实现过程很重要,在自己移植uboot时候很有肯恩更要修改这些代码。需要进一步掌握

uboot启动内核的实现的更多相关文章

  1. UBOOT启动内核过程

    1.摘要 (1).启动4步骤第一步:将内核搬移到DDR中第二步:校验内核格式.CRC等第三步:准备传参第四步:跳转执行内核(2).涉及到的主要函数是:do_bootm和do_bootm_linux(3 ...

  2. U-boot 启动内核

    1:什么是UBOOT,为什么要有UBOOT? UBOOT的主要作用是用来启动linux内核,因为CPU不能直接从块设备中执行代码,需要把块设备中的程序复制到内存中,而复制之前还需要进行很多初始化工作, ...

  3. linux的几个内核镜像格式Image 和 u-boot启动内核和文件系统时的一些环境变量的设置

    关于编译powerpc linux的几个Image参考原文 http://blog.sina.com.cn/s/blog_86a30b0c0100wfzt.html 转载▼   PowerPC架构 L ...

  4. 嵌入式linux开发uboot启动内核的机制(二)

    一.嵌入式系统的分区 嵌入式系统部署在Flash设备上时,对于不同SoC和Flash设备,bootloader.kernel.rootfs的分区是不同的.三星S5PV210规定启动设备的分区方案如下: ...

  5. 【转】UBOOT——启动内核

    转自:https://www.cnblogs.com/biaohc/p/6403863.html 1:什么是UBOOT,为什么要有UBOOT? UBOOT的主要作用是用来启动linux内核,因为CPU ...

  6. 嵌入式Linux驱动学习之路(六)u-boot启动内核

    内核启动是需要必要的启动参数.不能开机自动完全从0开始启动,需要uboot帮助内核实现重定位并提供参数. 首先,uboo会从Kernel分区中读取bootcmd环境变量,根据环境变量可自动启动. 分区 ...

  7. 使用Uboot启动内核并挂载NFS根文件系统

    配置编译好内核之后,将生成的内核文件uImage拷贝到/tftpboot/下,通过tftp服务器将内核下载到开发板,使用命令:tftp 31000000 uImage.下载完成之后配置bootargs ...

  8. tiny4412学习(一)之从零搭建linux系统(烧写uboot、内核进emmc+uboot启动内核)【转】

    本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74080109 版权声明:本文为博主原创文章,转载请注明http://blog.c ...

  9. u-boot学习(五):u-boot启动内核

    u-boot的目的是启动内核.内核位于Flash中,那么u-boot就要将内核转移到内存中.然后执行命令执行之.这些操作是由bootcmd命令完毕的. bootcmd=nand read.jffs2 ...

随机推荐

  1. rust 入门

    hello rust fn main() { println!("Hello, world!"); } 从hello world入手,rust的语法是比较简洁. 在mac os中, ...

  2. 在C#中用MediaInfo获取视频或音频的属性

    MediaInfo是一个开源的获取视频或音频的信息的非常便利的工具,它本身就带有一个GUI界面,可以非常方便我们查看视频信息.但是,当我们写一些转码程序时,往往需要在程序中获取视频信息的时候. 以前我 ...

  3. android 动态改变控件位置和大小 .

    动态改变控件位置的方法: setPadding()的方法更改布局位置. 如我要把Imageview下移200px:             ImageView.setPadding( ImageVie ...

  4. 虚拟机NAT网络设置

    1. 虚拟机设置 2. 本地网络设置 3. 本地虚拟网卡设置 4. 安装虚拟机,设置网络为NAT方式即可访问外网.

  5. python redis-string、list、set操作

    string操作 redis中的string在内存中都是按照一个key对应一个value来存储的 方法: set() 方法 : 写入一条数据 mset() 方法: 写入多条数据 , 可是Key-Val ...

  6. 使用Git Wiki 管理文档时,文档编写的基本用法

    自己初次接触GitLab,通过百度和自己查找资料,了解了一部分.在自己的工作中,主要用到GitLab的Wiki文档版本管理能力.我总结了一小部分文本编辑需要用到的东西. 一.文本的排版 为了让文本/文 ...

  7. LINUX gcc安装rpm包顺序

    rpm -ivh cpp-4.1.2-42.el5.i386.rpm rpm -ihv kernel-headers-2.6.18-92.el5.i386.rpm rpm -ivh glibc-hea ...

  8. LightOJ 1024 Eid(高精度乘法+求n个数最小公约数)

    题目链接:https://vjudge.net/contest/28079#problem/T 题目大意:给你n个数求这些数的最小公倍数(约数). 解题思路:还太菜了,看了别人的题解才会写,转自这里, ...

  9. Yii 简明学习教程

    Yii是一个基于组件的高性能PHP框架,用于快速开发Web应用程序(下面内容基于Yii 1.1) 1. 典型的工作流 用户向入口脚本index.php发起请求 入口脚本加载应用配置config.php ...

  10. 开源IDS系列--snorby 2.6.2 undefined method `run_daily_report' for Event:Class (NoMethodError)

    rails runner "Event.run_daily_report"测试邮件配置undefined method `run_daily_report' for Event:C ...