韩万里,email:hanwanli910816@126.com
以Tiny4412为例,整体流程:
抛开uboot的初始化,程序的运行循环体为main_loop。在man_loop里,会等待用户输入并执行相关命令行,如果没有输入,会执行默认的命令行bootcmd启动内核。
默认的bootcmd执行了什么,可以通过default_environment数组来获取。此数组里保存了默认的命令行及参数。我们可以看到,bootcmd默认执行的是"movi read kernel 0 40008000;movi read rootfs 0 41000000400000;bootm 40008000 41000000",首先读取kernel,再读取rootfs,之后跳转到40008000地址运行。
以上是默认的执行动作。那uboot是怎么把用户的命令行关联到相关的函数上的呢?首先我们看一下command.h里的U_BOOT_CMD宏:
这里有两种定义方式,区别在于是否保留帮助信息。
这个宏的作用是定义一个cmd_tbl_t结构体,并且将此结构体的存储区域通过Struct_Section限制在了u_boot_cmd域。同时我们看一下u-boot.lds文件中u_boot_cmd域的定义:
这里定义了u_boot_cmd域的位置,同时也定义了两个变量:__u_boot_cmd_start和__u_boot_cmd_end,用来标识存储空间的起始位置,在查找命令时,防止数组溢出。
通过U_BOOT_CMD宏,uboot可以在一块连续的空间中定义若干个cmd_tbl_t变量。cmd_tbl_t的定义如下:
这个结构体将一个命令行与参数个数、用法信息、帮助信息和对应的执行函数关联起来。以bootm为例,我们看一下结构体的实现。找到cmd_bootm.c文件里的do_bootm函数,先忽略它的实现,看一下函数后面紧跟的U_BOOT_CMD调用:
在此处,定义了一个如下结构体:
cmd_tbl_t__u_boot_cmd_bootm Struct_Section = {bootm, 16, 1, do_bootm, ... , ...}的结构体,并且存放在u_boot_cmd域。
其他的命令如:nandread/write,可以在第一个单词前加do_搜索查看,如do_nand。
了解了这些,我们看一下通过串口输入的命令是怎么找到对应的函数并且把参数传递进去的。
其实原理很简单,就是获取输入的第一个单词,从扫描所有的cmd_tbl_t结构体,与name成员进行比较,如果匹配,则将后续输入整理成参数列表,调用cmd指针指向的函数完成命令行。
首先在main_loop函数中,有len =readline (CONFIG_SYS_PROMPT);此处用于从控制台接收一串字符,直到遇到回车键,并且将数据保存在console_buffer中。
run_command函数的功能是把输入的数据进行整理,识别出cmd和参数表,并且调用相关的功能函数。有兴趣的可以自己研究一下,这里不在叙述。