linux-uboot 移植二 uboot-main_loop

1、main_loop 主循环

main_loop 函数定义在文件 common/main.c 里面,代码如下
linux-uboot 移植二 uboot-main_loop_第1张图片

  • 第48行bootstage_mark_name:会打印出启动进度

  • 第57行setenv:设置环境变量version,进入到uboot后,可通过version命令来打印版本信息

  • 第60行cli_init:跟初始化命令有关,初始化命令链表

  • 第62行run_preboot_environment_command:获取环境变量perboot的内容,perboot是一些预启动命令,一般不使用这个环境变量

  • 第68行bootdelay_process:读取环境变量bootdelay和bootcmd的内容,然后将bootdelay的值赋值给全局变量stored_bootdelay,返回值为环境变量bootcmd的值,同时会根据启动方式是否是从USB启动打印信息,若从USB启动则打印Boot from USB for mfgtools,否则打印Normal Boot,如下
    linux-uboot 移植二 uboot-main_loop_第2张图片

  • 第69~70:由于未定义设备树,因此无效

  • 第72行autoboot_command:函数定义在common/autoboot.c中,检测uboot倒计时是否结束,代码如下linux-uboot 移植二 uboot-main_loop_第3张图片
    里边条件编译比较多,主要的就只有两个函数:abortbootrun_command_list
    abortboot实际执行的代码是abortboot_normal,代码如下:
    linux-uboot 移植二 uboot-main_loop_第4张图片
    bootdelay大于0(默认值是3),则打印"Hit any key to stop autoboot:",因此会在终端上显示如下信息:
    linux-uboot 移植二 uboot-main_loop_第5张图片
    宏CONFIG_ZERO_BOOTDELAY_CHECK未定义!
    第247~266就是倒计时的程序,逻辑较简单,这里不做具体分析,大概逻辑是如果倒计时结之前检测到任何输入,则将abort标记置为1,否则默认为0。
    宏CONFIG_SILENT_CONSOLE未定义!
    结合autoboot_command的代码,若检测到有输入则退出,否则则执行run_command_list函数,执行bootcmd命令,加载内核并跳转。(bootcmd指令是各个板子默认的启动指令,需要自定义,此指令在下一章节解析)

  • 第74行cli_loop:执行到这表示检测到有输入,不会自动跳转到内核

2、cli_loop

函数定义在common/cli.c中,代码如下
linux-uboot 移植二 uboot-main_loop_第6张图片
由于宏CONFIG_SYS_HUSH_PARSER定义过,因此实际执行的函数就是u_boot_hush_start,此函数定义在common/cli_hush.c中,如下
linux-uboot 移植二 uboot-main_loop_第7张图片
宏__U_BOOT__是有定义过,因此主要是setup_file_in_strparse_stream_outer

2.1 setup_file_in_str

代码如下
linux-uboot 移植二 uboot-main_loop_第8张图片
主要初始化了结构体变量input,结构体定义如下
linux-uboot 移植二 uboot-main_loop_第9张图片

2.2 parse_stream_outer

这个函数就是 hush shell 的命令解释器,负责接收命令行输入,然后解析并执行相应的命令,去掉一些预编译宏后代码如下
linux-uboot 移植二 uboot-main_loop_第10张图片
函数入参flag是宏FLAG_PARSE_SEMICOLON,表示以分号为分界线解析命令。
第3197行parse_stream就是调用file_get从终端上读取到输入信息,然后进行命令解析,填充到input结构体内。
第3210行run_list执行解析出来的命令,经过一些列的分隔符解析及处理,最终调用cmd_process来处理命令。

3、cmd_process

3.1 指令定义

再看这个代码之前我们先看看uboot的命令是如何定义的(此种思想在内核代码中随处可见,建议深入理解。此方法在设计程序架构时非常方便,可以写出拓展性、适应性很强的框架!),uboot shell命令的代码都在cmd文件夹内,如下(未展示全部)
linux-uboot 移植二 uboot-main_loop_第11张图片
为了方便分析,我们选择一个最简单的指令version来看看,找到对应的version.c文件,代码如下
linux-uboot 移植二 uboot-main_loop_第12张图片
可以看到,代码很简单,最下边的出现了一个宏U_BOOT_CMD,这个定义在command.h中,如下
linux-uboot 移植二 uboot-main_loop_第13张图片
linux-uboot 移植二 uboot-main_loop_第14张图片

还有个ll_entry_declare_list,定义在include/linker_lists.h 中,如下
在这里插入图片描述
在include/linux/compiler-gcc.h中有一些gcc相关的拓展属性的用法,如下
linux-uboot 移植二 uboot-main_loop_第15张图片
结合上述的几个宏,我们将version.c中的定义展开如下所示:
linux-uboot 移植二 uboot-main_loop_第16张图片
不要嫌麻烦,上述几个嵌套的宏展开后就如上图所示,主要用到了#和##的拼接,这个用法很常见,百度也到处都有讲解,这里不展开讲,主要讲解下展开后的内容。
可以看到,展开后起始就是初始化了一个结构体而已,大家看起来就比平常自己初始化结构体多了两个attribute而已,gcc的一大特点就是提供了很多attrbute的拓展属性,在某些场合有很大的用处,感兴趣的可以百度下gcc的attribute,这里只讲解用到的几个:

  • __attribute __((aligned(4))):告诉编译器在编译过程中按照n字节对齐,这边是满足结构体的对齐要求,主要是内部有几个函数指针,防止不对齐时,造成访问错误
  • __attribute __((unused, section(“.u_boot_list_2_do_version_2_version”))):第一个参数unused是告诉编译器这个可能未使用,消除未使用变量的警告信息。第二个是重点内容,section是自定义段在链接脚本中有定义,这句话的意思是将这个变量放到指定的section中,链接脚本uboot.lds如下
    linux-uboot 移植二 uboot-main_loop_第17张图片
    链接脚本中定义了存储以 .u_boot_list开头的段,并且指定了按照数字升序的排列方式SORT,看到这基本就清楚了U_BOOT_CMD其实就是初始化了一个struct cmd_tbl_s结构体变量,并将其存储到了指定的section中,仅此而已。
    而这个结构体就是uboot shell的管理结构体,可以理解为框架,在平时自己做开发时,可以参考这种方式,定义一个管理结构体,然后存储在一个指定的自定义段中,在框架代码中轮训整个段,即可快速查找完所有用户定义在这里边的内容,第三方人员要使用只用使用宏进行定义即可,而框架代码不用动,不管是增加指令还是删除指令,都是一个c文件内部的事情,一个低耦合高内聚的框架就设计了出来,然而重要的是这种思想,也可以不依赖自定义段来实现。

看完了指令的定义,接下来我们看看框架代码是如何运作的。

3.2 框架运作

函数定义在common.c中,是uboot shell的核心处理程序,如下
linux-uboot 移植二 uboot-main_loop_第18张图片

  • 第509行find_cmd:输入参数argc[0]就是从终端上输入的字符串,代码如下
    linux-uboot 移植二 uboot-main_loop_第19张图片
    linux-uboot 移植二 uboot-main_loop_第20张图片
    linux-uboot 移植二 uboot-main_loop_第21张图片
    linux-uboot 移植二 uboot-main_loop_第22张图片
    上文提到过链接脚本中同一个section内的排列方式,由于uboot是采用数字升序的方式,因此定义一个.u_boot_list_2_xx_1和.u_boot_list_2_xx_3就相当于可以得到整个section段的起始地址和结束地址,(此方法有点麻烦,可以直接在链接脚本内定义两个变量即可,感兴趣的可以自己研究研究)取到起始地址和长度后,传入到find_cmd_tbl函数中,如下
    linux-uboot 移植二 uboot-main_loop_第23张图片
    代码逻辑也不复杂,在命令定义的时候,都定义了一个name,这里就是轮训所有的uboot指令段,来查找是否能够有匹配的指令。

  • 第510~512行:返回NULL,表示此命令未注册过,会打印错误信息,如下
    在这里插入图片描述

  • 第516行:检测参数个数是否超出预设

  • 第519~529行:防止boot指令递归执行

  • 第531~539行:找到命令后执行它的回调函数来执行,并记录了运行时间,以及是否自动repeat

  • 第540~541行:若命令执行失败,则打印出命令的help信息

终上所述,就是uboot的启动流程!

你可能感兴趣的:(linux)