首先可以想象一下,u-boot需要完成对很多命令的响应,当用户输入一个命令时,它需要去解析识别该命令,然后调用与该命令相对应的函数(即执行相应的动作),由于命令有很多,命令与执行动作需要对应起来,所以这里应该是一个命令对应一个结构体,结构体里面至少包括两个变量:命令的名字name、命令对应函数指针void(*do_name)(void var)。输入命令,根据命令的名字去找到相对应的结构体,然后执行相关函数。
不管是启动内核还是响应控制界面,u-boot从串口获取到命令后,都是执行run_command()函数,去run_command()函数里看一下:
首把命令复制到 char *str,存起给后面使用
往下看:对命令进行分离并且支持";"分离命令
接着是: 提取参数,解析命令,解析完后,要去找到与这个命令对应的命令结构体cmd_tbl_t *cmdtp。如何找到?重点分析cmd_tbl_t *find_cmd (const char *cmd)这个函数
先看下结构体cmd_tbl_t ,在Command.h中定义:
cmd_tbl_t *find_cmd (const char *cmd)函数
为什么在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_CMD宏 在include/command.h中定义如下:
Struct_Section 在include/command.h中定义如下:
把与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中,来加深对这个过程的理解。
1.首先在u-boot的common目录下添加一个cmd_hello.c,并将cmd_bootm的头文件复制进来,并定义命令宏和操作函数:
2.然后修改common目录下的Makefile:在后面添加一个cmd_hello.o
3. 编译u-boot,并在开发板上实验:
make 100ask24x0_config
make
下载u-boot.bin到开发板实验
实验结果看出:hello命令制作成功!