上一节说到,u-boot中最重要的就是命令,那么这些命令是怎么被识别并去执行相应的动作的,这里详细分析一下。

首先可以想象一下,u-boot需要完成对很多命令的响应,当用户输入一个命令时,它需要去解析识别该命令,然后调用与该命令相对应的函数(即执行相应的动作),由于命令有很多,命令与执行动作需要对应起来,所以这里应该是一个命令对应一个结构体,结构体里面至少包括两个变量:命令的名字name、命令对应函数指针void(*do_name)(void var)。输入命令,根据命令的名字去找到相对应的结构体,然后执行相关函数。

不管是启动内核还是响应控制界面,u-boot从串口获取到命令后,都是执行run_command()函数,去run_command()函数里看一下:

首把命令复制到 char *str,存起给后面使用

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

往下看:对命令进行分离并且支持";"分离命令

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

接着是: 提取参数,解析命令,解析完后,要去找到与这个命令对应的命令结构体cmd_tbl_t *cmdtp。如何找到?重点分析cmd_tbl_t *find_cmd (const char *cmd)这个函数

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

先看下结构体cmd_tbl_t ,在Command.h中定义:

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

cmd_tbl_t *find_cmd (const char *cmd)函数

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

为什么在find_cmd ()中遍历结构体使用:

for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end;cmdtp++) 

通过分析bootm命令来解释:

在链接脚本U-boot.lds中有:

__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

分析bootm命令,在串口输入bootm命令后会执行什么,

在代码中搜索“bootm”,可以在Cmd_bootm.c中找到U_BOOT_CMD宏,

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

U_BOOT_CMD宏 在include/command.h中定义如下:

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

Struct_Section 在include/command.h中定义如下:

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

把与bootm对应的U_BOOT_CMD宏展开可以得到:
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
    替换name,##表示连接符:
cmd_tbl_t __u_boot_cmd_bootm Struct_Section = {#name, maxargs, rep, cmd, usage, help}
    替换Struct_Section:
cmd_tbl_t __u_boot_cmd_bootm  __attribute__ ((unused,section (".u_boot_cmd"))) = {#name, maxargs, rep, cmd, usage, help}

目前可以看出bootm对应的U_BOOT_CMD宏表示的是:定义了一个cmd_tbl_t类型的结构体__u_boot_cmd_bootm,这个结构体的段属性强制设为".u_boot_cmd",和链接脚本U-boot.lds里的.u_boot_cmd : { *(.u_boot_cmd) }对应起来了
  __u_boot_cmd_start = .;
  .u_boot_cmd : { *(.u_boot_cmd) }
  __u_boot_cmd_end = .;
  
继续展开:定义的结构体__u_boot_cmd_bootm的内容是什么
cmd_tbl_t __u_boot_cmd_bootm  __attribute__ ((unused,section (".u_boot_cmd"))) 
= {#name, maxargs, rep, cmd, usage, help} /*名字、最大参数个数、是否可重复、执行函数、短的帮助信息、长的帮助信息*/
= {
"bootm", CFG_MAXARGS, 1, do_bootm, "bootm   - boot application image from memory\n", 
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
#endif
  }  

通过以上分析可知在代码中所有用 U_BOOT_CMD宏定义的东西到最后都和链接脚本里的
            __u_boot_cmd_start = .;
            .u_boot_cmd : { *(.u_boot_cmd) }
            __u_boot_cmd_end = .;

关联起来了,当我们要去寻找输入的命令与哪个结构体相对应时,只需要在__u_boot_cmd_start与__u_boot_cmd_end之间遍历寻找。

总结命令执行过程

在u-boot控制台中输入“bootm”命令执行时,u-boot控制台接收输入的字符串“bootm”,传递给run_command函数。

run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回boot命令的cmd_tbl_t结构。

 然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用bootm命令的响应函数do_bootd,从而完成了命令的执行。

最后自己写一条命令hello添加到U-boot中,来加深对这个过程的理解。


u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

1.首先在u-boot的common目录下添加一个cmd_hello.c,并将cmd_bootm的头文件复制进来,并定义命令宏和操作函数:

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

2.然后修改common目录下的Makefile:在后面添加一个cmd_hello.o

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

3. 编译u-boot,并在开发板上实验:

make 100ask24x0_config

make

下载u-boot.bin到开发板实验

u-boot分析3:第二阶段代码分析2--命令解析过程及自定义命令

实验结果看出:hello命令制作成功!


相关文章:

  • 2021-09-08
  • 2021-06-12
  • 2021-12-18
  • 2022-12-23
  • 2021-06-17
  • 2021-10-11
  • 2022-12-23
  • 2021-09-12
猜你喜欢
  • 2021-06-08
  • 2022-12-23
  • 2022-12-23
  • 2021-11-19
  • 2021-08-24
  • 2021-12-02
  • 2021-08-26
相关资源
相似解决方案