U-boot的命令架构和启动内核

一、U-boot的命令架构

1、U-boot的每个命令都是通过都是通过U-BOOT-CMD宏来定义的(include/command.h)。

格式如下图所示,

其中:
name表示命令的名字,这里需要注意的是不要用双引号将其括起来,因为它不是字符串;
maxarges表示最大的参数个数;
rep表示命令是否可重复(repeatable),可重复是指运行一个命令后,下次敲回车即可再次运行;
cmd对应函数的指针,就是命令执行的时候它要调用那些函数,类型为(cmd)(struct cmd_tbl_s,int,int,char*[]);
usage表示简短的使用说明,这是个字符串,需要加双引号,比如在uboot的串口输入help命令就会打印出很多命令并且每条命令都有简短的信息,每条命令后的简短信息就是usage;
help表示较详细的使用说明,这里同样也是字符串,需要加双引号,比如在uboot的串口输入help再加上某个具体命令来获取该命令的帮助信息时打印出的便是较详细的使用说明;
cmd_tbl_t表示结构体的类型;其在本文件中也有定义,如下:
U-boot的命令架构和启动内核_第1张图片
u_boot_cmd##name表示命令结构体的名字,##是连字符号;
Struct_Section也是在本文件中定义,如下所示;
attribute ((unused,section (".u_boot_cmd")))中__attribute表示属性,这里将段属性强制指定为段属性.u_boot_cmd格式,这里就和连接脚本对应起来了。
在这里插入图片描述

2、以bootm命令为例将其U-BOOT-CMD宏展开
bootm命令在cmd_bootm.c中定义,如下
U-boot的命令架构和启动内核_第2张图片

因此将其展开为
cmd_tbl_t __u_boot_cmd_bootm attribute ((unused,section (".u_boot_cmd"))) = {bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, “boot application image from memory”, “[addr [arg …]]\n - boot application image stored in memory\n” “\tpassing arguments ‘arg …’; when booting a Linux kernel,\n” “\t’arg’ can be the address of an initrd image\n”…}

3、对于每个使用U-BOOT-CMD宏来定义的命令,其实都是在”.u_boot_cmd“段中定义了一个cmd_tbl_t结构体
打开链接脚本u-boot.lds有如下代码,*(.u_boot_cmd)便是用来存放u_boot_cmd这个段的,最后所有的命令都会集中到这里来。
在这里插入图片描述

4、程序中就是根据命令的名字,在内存段__u_boot_cmd_start ~ __u_boot_cmd_end找到它的cmd_tbl_t结构,然后调用它的函数的
当在串口中输入每一个命令时候,这个命令就会找到它对应的执行函数,然后调用函数去执行;命令和其对应的执行函数就组成了一个结构体cmd_tbl_t;而run_command函数(commom/main.c)就会对我们输入的命令的名字(通过获得,例如 getenv(“bootcmd”))进行读取解析,去和结构体中的名字比较,之后find_cmd函数就会去找该命令的函数。那么find_cmd函数又是如何去找的呢?用SourceInsight工具很快就能知道find_cmd函数在common/command.c中,例如:
在这里插入图片描述

U-boot的命令架构和启动内核_第3张图片

这里面有__u_boot_cmd_start和__u_boot_cmd_end是在上面说的链接脚本u-boot.lds中定义的

二、启动内核

我们在uboot命令行界面中所看到的bootm环境变量的设置值一般包括二部分:
第一部分就是从哪里读出内核,读出后又会放到哪里去
例如 nand read.jffs2 0x30007fc0 kernel;就表示从flash的kernel分区里读内核(具体怎么读就要去看其源码中对应的函数了commom/cmd_nand.c),读出后放到0x30007fc0(这个地址是可以随便放的)
注意:分区的名字不重要,重要的是与分区对应的起始位置和分区大小。在嵌入式linux中flash上本身是没有严格分区的,所谓bootloader、kernel、rootfs的分区以及各分区的大小其实在源码的配置文件中写固定了,如下所示。我们在uboot的命令行输入mtd命令便可以看到各个分区的起始位置以及大小。
在这里插入图片描述
第二部分就是启动内核
例如 bootm 0x30007fc0;
这就要看源码中bootm的执行函数了(common/cmd_bootm.c),它主要就是读取头部,根据头部移动内核到它的加载地址,然后启动内核(由do_bootm_linux函数完成),
do_bootm_linux函数会在和内核约定好的某个地址(例如0x30001000)按照tag格式去存放启动参数,然后跳到内核的入口地址去启动内核。
thekernel等于头部的入口地址

在这里插入图片描述
注意:flash上存放的内核就是uImage,它由头部和真正的内核(vmlinux)二部分组成。其中头部定义了内核的加载地址(由于头部大小为64字节,所以由以上第一部分可知若0x30007fc0加上64字节后的大小为0x30008000,即加载地址一定为0x30008000)和入口地址。

你可能感兴趣的:(Bootloader,uboot命令,启动内核)