uboot引导linux启动
uboot的使命在于启动操作系统,在uboot启动流程文章中,我们已经对uboot从复位到进入命令行模式的过程进行了一个梳理(uboot启动分析-CSDN博客),下面,我们主要讲解uboot启动linux操作系统的流程。
uboot启动linux的相关命令
在uboot命令行模式中,我们执行bootz命令即可启动linux,或者倒计时结束时会自动执行bootcmd中的命令,其实也是执行了bootz命令。bootargs用于设置控制台设备,根文件系统等信息,最终会传递给linux内核。uboot中可配置的信息如下:
bootz【zImage地址】 【initrd[:size]】 【设备树地址】
bootargs=console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.139.100:/home/yj/linux/nfs/rootfs,proto=tcp,nfsvers=3 ip=192.168.139.50:192.168.139.100:192.168.1.1:255.255.0.0::eth0:off
bootcmd=tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000
总括
uboot启动linux主要由以下三个部分组成:处理bootargs传参、传入设备树地址参数、调用linux入口。
-
bootargs是uboot中的一个环境变量,用于记录传递给linux的参数。uboot在调用linux入口之前会在设备树的/chosen节点下创建一个属性bootargs,这样就可以通过设备树将bootargs的内容传递给linux。而linux在启动的时候也会去/chosen下寻找该属性,并进行相关配置。
-
设备树的地址是在bootz命令中指定的参数,这样uboot就知道这个地址了,然后将这个地址作为第三个参数调用linux入口。因为linux在起始位置出留出了r0,r1,r2三个ABI给uboot,见内核注释如下:
; Uboot - kernel ABI ; r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2 【标识r2参数的值】 ; r1 = magic number (board identity, unused as of now 【magic id,有设备树的情况不用】 ; r2 = pointer to uboot provided cmdline or external DTB in mem 【设备树地址】 ; These are handled later in setup_arch()
-
调用linux入口,即可转到linux中运行,uboot的使命就结束了。
其中,不论是bootargs还是r2的传参形式,都属于linux留有这样的一个接口,而uboot利用这个接口进行参数的传递。
详解
uboot启动linux的过程相对来说还是比较简单的,以下解析可能并不完整,但对于目前来说所要解释的部分都有提及。
uboot启动linux的两种方式
uboot中有两种方法启动linux,一种是倒计时结束,自动启动,执行bootcmd中的命令列表,可以看到,是执行了bootz;另一种进入命令行模式,使用bootz命令。其实两者都是使用bootz命令,bootz命令最终会执行其回调函数do_bootz。下面是两种方式的代码追踪:
uboot中命令的体现是struct cmd_tbl_a结构体,其中保存了该命令的名字,回调函数等信息。定义一个命令可以使用uboot实现的特定宏,传入名字回调函数等信息,它就会在某个特殊的段里定义一个struct cmd_tbl_a结构,表示该命令。
命令的执行过程主要就是根据命令名在特殊段中寻找该命令,得到该命令的结构体信息,之后调用该命令。
do_bootz
上文说到,执行bootz命令会执行do_bootz函数,do_bootz函数就是uboot启动linux的函数实现。首先看一下整个流程的图解:
这一部分是重点。可以看到,我们先是将zImage,dtb设备树镜像加载到内存指定位置,之后调用bootz命令,传入其内存地址。其实uboot中有一系列boot启动命令例如bootz、bootm、boot等,适用于不同情况,例如bootz是用于加载zImage类型的镜像的,可能在加载linux前会按照zImage格式读取头部信息做一些校验等;bootm用于启动uImage镜像等。可以看到,bootz命令后面其实也是执行的bootm的相关函数。
images
在bootz中,有一个bootm_headers_t结构体类型的全局变量images,记录了boot启动时的一些重要的全局变量信息,比如os镜像信息,ep为镜像开始地址,ft_addr为设备树起始地址等;另外还define了一些阶段,用于在程序中根据state阶段来执行不同的启动部分。images变量贯穿整个uboot启动linux的过程,很重要。
bootz_start
首先执行bootz_start开始引导,其中做一些初始化、校验以及其他images的获取。bootz_start先清空images变量,然后设置images->ep为zImages镜像地址,之后调用bootz_setup函数获取镜像的zImages头,以做镜像的magic校验和获取镜像的大小。最后,会调用bootm_find_images函数查找其他的镜像,比如ramdisk以及设备树镜像,将相关信息保存在images全局变量中。
在bootz_start函数结束后,还会关中断(不知是否是linux镜像对启动的要求),以及images.os.os = IH_OS_LINUX确定引导系统为linux。之后便会调用do_bootm_states来执行inux启动的各个部分。
do_bootm_states
这里调用该函数使用了阶段BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO。在该函数中,首先根据os类型寻找os对应的启动函数,linux的启动函数是do_bootm_linux,之后的很多部分都是在该函数中做的。在do_bootm_linux中的BOOTM_STATE_OS_PREP阶段会将bootargs环境变量添加到设备树的chosen节点中作为一个属性;BOOTM_STATE_OS_GO阶段会调用函数boot_jump_linux来跳转到linux中运行,主要是会传入r0,r1,r2三个参数,r2为设备树s地址,来调用(void (*)(int, int, uint))images->ep函数。到这里,就进入linux里了,uboot引导linux启动结束。