12_Uboot启动流程_4

目录

images全局变量

do_bootz函数

bootz_start函数

do_bootm_states函数

bootm_os_get_boot_func函数

do_bootm_linux函数


images全局变量

不管是bootz还是bootm命令,在启动Linux内核的时候都会用到一个重要的全局变量:images, images在文件cmd/bootm.c中有如下定义:

 images是bootm_headers_t类型的全局变量,bootm_headers_t是个boot头结构体,在文件include/image.h中的定义如下(删除了一些条件编译代码):

第335行的os成员变量是image info t类型的,为系统镜像信息。

第352-362行这11个宏定义表示BOOT的不同阶段。

接下来看一下结构体image_info_t,也就是系统镜像信息结构体,此结构体在文件include/image.h中的定义如下:

 全局变量images会在bootz 命令的执行中频繁使用到,相当于Linux内核启动的“灵魂”。

 

do_bootz函数

bootz命令的执行函数为do_bootz,在文件cmd/bootm.c中有如下定义:

第629行,调用 bootz_start函数。

第636行,调用函数bootm_disable_interrupts关闭中断。

第638行,设置images.os.os为IH_OS_LINUX,也就是设置系统镜像为Linux,表示我们要启动的是Linux系统!后面会用到images.os.os来挑选具体的启动函数。

第639行,调用函数do bootm states来执行不同的BOOT阶段,这里要执行的BOOT阶段有: BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO

bootz_start函数

bootz_srart函数也定义在文件cmd/bootm.c 中,函数内容如下:

 第584行,调用函数do_bootm_states,执行BOOTM_STATE_START阶段。

第593行,设置images的ep成员变量,也就是系统镜像的入口点,使用bootz命令启动系统的时候就会设置系统在DRAM中的存储位置,这个存储位置就是系统镜像的入口点,因此images->ep=0X80800000。

第598行,调用bootz_setup函数,此函数会判断当前的系统镜像文件是否为Linux的镜像文件,并且会打印出镜像相关信息, bootz setup函数稍后会讲解。

第608行,调用函数 bootm_find_images查找ramdisk 和设备树(dtb)文件,但是我们没有用到ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件,此函数稍后也会讲解。

先来看一下bootz_setup函数,此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

 第370行,宏LINUX_ARM_ZIMAGE_MAGIC就是ARM Linux系统魔术数。

第376行,从传递进来的参数image(也就是系统镜像首地址)中获取zimage头。zImage头结构体为 zimage_header。

第377-380行,判断image是否为ARM的Linux系统镜像,如果不是的话就直接返回,并且打印出“Bad Linux ARM zImage magic!”,比如我们输入一个错误的启动命令:

bootz 80000000 - 900000000

因为我们并没有在0X80000000处存放Linux镜像文件(zImage),因此上面的命令肯定会执行出错的,结果如图所示:

第382、383行初始化函数bootz_setup的参数start和end。

第385行,打印启动信息,如果Linux系统镜像正常的话就会输出图所示的信息:

 

 接下来看一下函数bootm_find_images,此函数定义在文件common/bootm.c中,函数内容如下:

 

 接下来看一下函数bootmfind_images,此函数定义在文件common/bootm.c中,函数内容如下:

第230-235行是跟查找ramdisk,但是我们没有用到ramdisk,因此这部分代码不用管。

第237~244行是查找设备树(dtb)文件,找到以后就将设备树的起始地址和长度分别写到images的ft_addr和ft_len成员变量中。我们使用 bootz 启动 Linux的时候已经指明了设备树在DRAM 中的存储地址,因此 images.ft_addr-0X83000000,长度根据具体的设备树文件而定,比如我现在使用的设备树文件长度为0X8C81,因此images.ft_len-0X8C81。 bootz_start函数就讲解到这里,bootz_start主要用于初始化images的相关成员变量。

 

do_bootm_states函数

do-bootz最后调用的就是函数do_bootm_states,而且在bootz_start中也调用了do_bootm_states 函数,看来do_bootm_states函数还是个香饽饽。此函数定义在文件common/bootm.c中,函数代码如下: 

函数do-bootm_states根据不同的BOOT状态执行不同的代码段,通过如下代码来判断BOOT 状态:

states & BOOTM_STATE_XXX

在do_bootz函数中会用到BOOTM_STATE_OS_PREP、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO这三个BOOT状态,bootz_start函数中会用到 BOOTM_STATE_START这个BOOT 状态。为了精简代码,方便分析,因此我们将示例代码32.3.4.1中的函数do_bootm_states 进行精简,只留下下面这 4 个BOOT 状态对应的处理代码:

 第604、605行,处理BOOTM_STATE_START阶段, bootz_start会执行这一段代码,这里调用函数bootm_start,此函数定义在文件common/bootm.c中,函数内容如下:

 接着回到示例代码32.3.4.2中,继续分析函数do_bootm_states。第 658 行非常重要!通过函数bootm_os_get_boot_func来查找系统启动函数,参数images->os.os就是系统类型,根据这个系统类型来选择对应的启动函数,在do-bootz中设置images.os.os- IH-OS-LINUX。函数返回值就是找到的系统启动函数,这里找到的Linux系统启动函数为do_bootm_linux,因此boot_fn-do_bootm_linux,后面执行boot_fn函数的地方实际上是执行的do_bootm_linux函数。

第676行,处理BOOTM_STATE_OS_PREP状态,调用函数do_bootm_linux, do_bootm_linux也是调用boot_prep_linux来完成具体的处理过程。boot_prep_linux主要用于处理环境变量bootargs,bootargs保存着传递给Linux kernel的参数。

第699行,调用函数 boot_selected_os启动Linux内核,此函数第4个参数为Linux系统镜像头,第5个参数就是Linux系统启动函数do_bootm_linux。boot_selected_os 函数定义在文件common/bootm os.c中,函数内容如下:

 第480行调用boot_fn函数,也就是do_bootm_linux函数来启动Linux内核。

 

 

bootm_os_get_boot_func函数

do_bootm_states会调用bootm_os_get_boot_func来查找对应系统的启动函数,此函数定义在文件common/bootm os.c中,函数内容如下:

第495-508行是条件编译,在本uboot中没有用到,因此这段代码无效,只有509行有效。在509行中boot_os是个数组,这个数组里面存放着不同的系统对应的启动函数。boot_os也定义在文件 common/bootm os.c中,如下所示: 

第438行就是Linux系统对应的启动函数:do_bootm_linux。 

 

 

do_bootm_linux函数

经过前面的分析,我们知道了do_bootm_linux就是最终启动Linux内核的函数,此函数定义在文件arch/arm/lib/bootm.c,函数内容如下:

 第351行,如果参数flag等于BOOTM_STATE_OS_GO或者BOOTM_STATE_OS_FAKE_GO的话就执行boot_jump_linux 函数。boot_selected_os函数在调用do_bootm_linux的时候会将flag设置为BOOTM_STATE_OS_GO。

第352行,执行函数boot_jump_linux,此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

第351行,如果参数flag等于BOOTM_STATE_OS_GO或者BOOTM_STATE_OS_FAKE_GO的话就执行boot_jump_linux 函数。boot_selected_os函数在调用do_bootm_linux的时候会将flag设置为BOOTM_STATE_OS_GO。

第352行,执行函数boot_jump_linux,此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

 第274-292行是64位ARM芯片对应的代码, Cortex-A7是32位芯片,因此用不到。

第293行,变量machid保存机器ID,如果不使用设备树的话这个机器ID会被传递给Linux内核,Linux内核会在自己的机器ID列表里面查找是否存在与uboot传递进来的machid匹配的项目,如果存在就说明Linux内核支持这个机器,那么Linux就会启动!如果使用设备树的话这个machid就无效了,设备树存有一个“兼容性”这个属性, Linux内核会比较“兼容性”属性的值(字符串)来查看是否支持这个机器。

第295行,函数kenel_entry,看名字“内核_进入”,说明此函数是进入Linux内核的,也就是最终的大boos!!此函数有三个参数: zero, arch, params,第一个参数zero同样为0;第二个参数为机器 ID;第三个参数ATAGS或者设备树(DTB)首地址,ATAGS是传统的方法,用于传递一些命令行信息啥的,如果使用设备树的话就要传递设备树(DTB)。

第299行,获取kernel_entry函数,函数kernel_entry并不是uboot定义的,而是Linux内核定义的,Linux内核镜像文件的第一行代码就是函数kernel_entry,而images->ep保存着Linux内核镜像的起始地址,起始地址保存的正是Linux内核第一行代码!

第313行,调用函数announce_and_cleanup来打印一些信息并做一些清理工作,此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

 第74行,在启动Linux之前输出"Starting kernel ..."信息,如图所示:

 第87行调用cleanup_before_linux函数做一些清理工作。

继续回到示例代码32.3.6.2 的函数boot_jump_linux,第315-318行是设置寄存器r2的值?为什么要设置r2的值呢? Linux内核一开始是汇编代码,因此函数kernel_entry就是个汇编函数。向汇编函数传递参数要使用r0、r1和r2(参数数量不超过3个的时候),所以r2寄存器就是函数kernel entry的第三个参数。

第316行,如果使用设备树的话,r2应该是设备树的起始地址,而设备树地址保存在images的 ftd_addr成员变量中。

第317行,如果不使用设备树的话, r2应该是uboot传递给Linux的参数起始地址,也就是环境变量bootargs的值

第328行,调用kernel_entry函数进入Linux内核,此行将一去不复返, uboot的使命也就完成了,它可以安息了!

总结一下bootz命令的执行过程,如图所示:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值