历史上的今天
返回首页

历史上的今天

今天是:2025年02月22日(星期六)

2021年02月22日 | cortex-a8 uboot系列:第十二章 uboot源码分析 uboot如何启动内核2

2021-02-22 来源:eefocus

一、zImage启动细节

do_bootm函数在after_header_check符号前,都是在进行镜像的头部信息校验。校验时就要根据不同的种类的image类型进行不同的校验。而不同的镜像的开头都是有自己的头信息。


所以do_bootm函数的核心就是去分辨传进来的image到底是什么类型,然后按照这种类型的头信息格式去校验。校验通过则进入下一步,准备启动内核,如果检验失败,则认为镜像有问题,不启动内核。


上一节分析过zImage启动,当判断不是从zImage启动时,会检查其他类镜像。如uImage镜像。

 

Uboot支持多种镜像启动,uboot中定义3种镜像方式。但是只有两种是有效的。

clip_image002

IMAGE_FORMAT_INVALID: 无效的镜像

IMAGE_FORMAT_LEGACY:  uImage镜像启动

IMAGE_FORMAT_FIT:      新的设备树启动

clip_image004

宏IMAGE_FORMAT_LEGACY,表示启动镜像是uImage镜像方式。


uImage方式是uboot本身发明的支持linux启动的镜像格式,但是后来这种方式被一种新的方式替代,这个新的方式就是设备树方式启动(在do_bootm中叫FIT)。


1.uImage启动方式

boot_get_kernel函数(common/cmd_bootm.c)寻找镜像的头信息,并校验,得到真正的kernel的起始位置去启动。

clip_image006

 

获取镜像地址。

clip_image008

genimg_get_image函数(common/image.c)

clip_image010

clip_image012

clip_image014

判断镜像类型,是uImage还是设备树。通过genimg_get_format函数(common/image.c)获取。

clip_image016

判断镜像的魔数。uImage的魔数是0x27051956。

clip_image018

 

判断完镜像类型后,打印镜像类型的值。启动镜像为uImage,IMAGE_FORMAT_LEGACY的值为1,所以会打印1。

最终执行完这个函数后,会打印如下信息。

clip_image019

 

clip_image021

获取镜像的大小。

clip_image023

从外部的dataflash中读取kernel到ddr中。

 

以上的程序都没有执行,因为此时,uboot已经把kernel拷贝到DDR中。在函数的最开始的if判断不成立,所以后面的代码都不执行。

clip_image025

clip_image027

调用image_get_kernel函数(common/cmd_bootm.c)从镜像中获取kernel。

clip_image029

clip_image031

判断魔数和头信息校验是否正确。

clip_image033

image_print_contents函数(common/image.h),打印uImage镜像的信息。

clip_image035

 

clip_image037

对kernel数据进行校验。

clip_image039

image_check_target_arch函数(common/cmd_bootm.c),判断镜像和soc架构是否匹配。

clip_image041

上面执行完毕后,要获取os数据和长度。通过判断镜像的类型。这里是IH_TYPE_KERNEL,表示数据是一个内核。

调用两个函数获取os数据和os长度

clip_image043

clip_image045

构建images全部变量。

至此boot_get_kernel函数执行完毕。

 

获取镜像参数。

clip_image047 

继续获取到一些信息。

clip_image049

clip_image051

判断镜像的压缩类型,然后根据该类型,uboot中对镜像进行解压缩。但是对于现在的uboot是使用镜像自己提供的解压缩程序,所以comp值为IH_COMP_NONE,uboot对镜像文件不解压。

 

执行到after_header_check标号,表示bootm第二阶段结束。头信息校验结束。

clip_image053

2. 其他启动方式

uboot本身设计时只支持uImage启动,原来uboot的代码也是如此。后来又了fdt方式之后,就把uImage方式命令为LEGACY方式,fdt方式命令为FIT方式,于是多了很多#if #endif添加的代码。


后来移植的人添加了zImage启动的方式,添加在uImage或fdt启动方式之前。而当zImage进行校验过后,直接跳转到after_header_check标号处,跳过uImage和FIT的校验。


3.启动linux内核

第二阶段校验镜像头信息结束,进入第三阶段,第三阶段主要任务是启动linux内核,调用do_bootm_linux函数。

clip_image055

判断操作系统类型,然后启动对应的操作系统。对于我们使用的LINUX,调用do_bootm_linux函数(lib_arm/bootm.c)来启动linux。

clip_image057

 

clip_image059

获取启动参数

clip_image061

获取kernel的入口地址。在boot_get_kernel函数中将images->legacy_hdr_valid置1,说明头有效,然后获取镜像的入口地址。

clip_image063

 

ep是entrypoint的缩写,就是程序入口。一个镜像文件的起始执行部分不是在镜像的开头(镜像的开头有n个字节的头信息),真正的镜像文件执行时第一句代码相对于镜像起始是有一定偏移量的。这个偏移量是记录在头信息中。


一般执行一个镜像都是:第一步先读取头信息,然后在头信息的特定地址找MAGIC_NUM,由MAGIC_NUM_确定镜像的类型;第二步对镜像进行校验;第三步再次读取头信息,由头信息知道镜像的各种信息(镜像长度、镜像种类、入口地址);第四步跳转到entrypoint处执行镜像。

clip_image065

找到ep后,赋值给函数指针clip_image067

获取机器码,以便后面传递给kernel。


以下这部分代码,就是uboot准备给linux内核准备传递的参数处理。

clip_image069

 

clip_image071

Uboot最后打印的starting kernel。这句如果能出现,说明uboot整个是成功的,也成功加载了内核镜像,也校验通过了,也找到入口地址了,也试图去执行。如果这句输出后,串口没有输出,说明内核没有被成功执行。


原因有:传参问题、内核在DDR中加载地址不对、kernel烧录地址不对。

 

调用函数指针theKernel去执行linux内核,至此uboot完成了使命。

 

二、uboot给linux内核传参

传参在do_bootm_linux函数中。


1.tag方式传参

struct tag,tag是一个数据结构,在uboot和linux kernel中都有定义tag数据结构,而且定义一致。

clip_image073clip_image075

里面有两个参数。一个是tag_header结构体(include/asm-arm/setup.h中),一个是tag多种类型属性的联合体。

clip_image076

tag_header表征tag的类型编码和大小。kernel得到tag后,先分析这个tag_header得到tag的类型和大小,然后将tag剩余部分当做tag_xxx处理

 

clip_image078

定义了不同参数的tag值以及不同参数的结构体。

 

tag_start和tag_end。Kernel接收到的传参是若干个tag构成的,这些tag由tag_start起始,到tag_end结束。


当需要使用tag传参时,首先传递tag_start,然后传递tag参数,最后传递tag_end。

 

clip_image080

判断有哪些类型的tag需要传递。当有tag需要传递后,使用setup_start_tag函数开始传递。

clip_image082

首先通过bd->bi_boot_params参数获取传参参数的起始地址。然后在这个地方放入一个ATAG_CORE类型的tag,表示参数传递开始。然后params指向下一个待传输tag的地址。

 

X210_sd.h中配置传参宏

CONFIG_SETUP_MEMORY_TAGS, tag_mem,传参内容是内存配置信息。两个参数,一个内存大小,一个内存起始地址。

clip_image084

clip_image086

 

CONFIG_CMDLINE_TAG: tag_cmdline,传参内容是启动命令行参数,也就是uboot环境变量的bootargs。这个参数很重要,uboot要将这个参数传递给linux内核的

clip_image088

clip_image090

去掉命令中开头无效的空格。

 

CONFIG_INITRD_TAG: tag_initrd,内存磁盘。

clip_image092

 

CONFIG_MTDPARTITION: tag_mtdpartition,传参内容是iNand/sd卡的分区表。

clip_image094

clip_image096

 

起始tag是ATAG_CORE、结束tag是ATAG_NONE,其他的ATAG_XXX都是有效信息tag。

 

参数传递后,内核是怎么拿到这些tag信息了?

就是通过调用的启动内核的函数指针theKernel的3个参数中的其中之一的参数来确定tag信息。

 

启动内核的函数有3个参数,这3个参数是uboot直接传递给linux内核的,通过寄存器来实现传参的。(第一个参数放在r0中,第二个参数放在r1中,第三个参数放在r2中)。

第一个参数固定为0

第二个参数为机器码

第三个参数为启动传参tag的DDR首地址


三、移植时需要注意的问题

Uboot移植时一般只需要配置相应的宏即可。

Kernel启动不成功,注意传参是否成功。传参不成功首先看uboot中bootarg设置是否正确,其次看uboot是否开启了相应宏以支持传参。


四、Uboot启动内核的总结

启动4步骤:

第一步:将内核搬移到DDR中

第二步:校验DDR中内核镜像、CRC等

第三步:准备传参,设置需要传参的tag

第四步:跳转执行内核,使用函数指针

 

设计到的主要函数:do_bootm和do_bootm_linux

do_bootm包括两部分:一部分对内核镜像进行校验,另一部分就是执行do_bootm_linux。

do_bootm_linux对传递的参数进行设置,然后去启动linux内核

 

Uboot能启动的内核格式:zImage  uImage  fdt格式

 

跳转与函数指针的方式运行内核,要带3个参数。


推荐阅读

史海拾趣

Global Navigation Systems公司的发展小趣事
如果远程无线防盗报警系统无法报警,首先需要检查各个部件是否正常工作。可以依次检查探测器是否触发、发射机是否发射信号、接收控制器是否接收到信号并触发报警装置等。如果某个部件出现故障,需要及时进行维修或更换。同时,还需要检查系统的电源和通讯是否正常,确保系统能够稳定运行。
CETC公司的发展小趣事

CETC深知人才是企业发展的根本。因此,公司一直注重人才培养和团队建设。公司设立了完善的培训体系,为员工提供各种学习和发展的机会。同时,CETC还积极引进国内外优秀人才,打造了一支高素质、专业化的团队。这支团队在公司的各个领域都发挥着重要作用,为公司的持续创新和发展提供了有力保障。

西安航天民芯公司的发展小趣事

随着公司业务的不断拓展和市场规模的逐步扩大,西安航天民芯吸引了众多投资者的关注。多家知名投资机构纷纷入股公司,为公司的发展提供了强大的资本支持。这些资金的注入不仅加速了公司的技术研发和市场拓展步伐,也为公司的持续发展注入了新的活力。

这五个故事展示了西安航天民芯公司在电子行业中的发展历程和取得的成就。通过不断地技术创新和市场拓展,西安航天民芯已经逐渐成为了电子行业中的领军企业之一,为行业的发展做出了积极的贡献。

Aerotronics Marketing Inc公司的发展小趣事

Aerotronics Marketing Inc公司在市场定位上独具慧眼,准确抓住了电子行业中无人机市场的增长趋势。公司制定了一系列精准的营销策略,包括线上线下推广、行业展会展示、合作伙伴计划等,有效提升了品牌知名度和市场占有率。同时,公司还注重客户关系管理,通过提供优质的售后服务和技术支持,赢得了客户的信任和忠诚。

AIM - American Iron and Metal公司的发展小趣事

为了进一步提升竞争力,AIM开始着手整合电子行业的上下游产业链。公司通过收购、合资等方式,与多家供应商和合作伙伴建立了紧密的合作关系。这种整合不仅降低了生产成本,还提高了产品的质量和交付速度,使AIM在激烈的市场竞争中保持领先地位。

HANBIT Electronics公司的发展小趣事

在电子行业的初期,AIM主要以传统的铁和金属加工业务为主。然而,随着电子技术的迅猛发展,公司管理层意识到转型的必要性。AIM开始投资研发,逐步将业务扩展到电子元件和设备的制造领域。通过引进先进的生产线和技术人才,AIM成功开发出一系列高性能的电子零部件,逐渐在市场中站稳脚跟。

问答坊 | AI 解惑

数据采集资料

数据采集,大家多交流问题,多提问!…

查看全部问答>

什么是单片机?单片机有什么用?

单片机又称单片微控制器]它不是完成某一个逻辑功能的芯片,而是把一个计算机系统集成到一个芯片上。概括的讲:一块芯片就成了一台计算机。它的体积小、质量轻、价格便宜、为学习、应用和开发提供了便利条件。同时,学习使用单片机是了解计算机原理与 ...…

查看全部问答>

基于arm的大楼灯光控制器需要的硬件设备.

这是我的毕业设计题目,还不知道怎么做,希望得到帮助! …

查看全部问答>

关于NDIS显示数据包内容的问题

如果我要在NDIS中间驱动层拦截网络数据包,那么,如果要把NDIS中间驱动层中截获的一个NDIS封包全部输出来,包括头还有数据部分,以二进制的形式全部显示出来,应如何实现呢?…

查看全部问答>

To 一个简单的提问

我现在想开发一款USB驱动程序,我目前 的操作系统是Win2003,请问使用win2003DDK开发的驱动程序,能够在WIN2000及XP上使用吗?DDK有什么要求吗?谢谢,见笑了.…

查看全部问答>

遇到这事,你怎么办?

接到了二个offer,一个是作电子词典嵌入式开发,一个是作linux计费开发,现在不知去哪个好? 我以前的工作是在linux下的开发,但我有很想往嵌入式那边尝试,但现在不知道作电子词典开发有没有前途?因为那家公司的电子词典的操作系统不是linux,也不 ...…

查看全部问答>

信号切换问题

A,B两路信号选择一路通过,当然这是两路都有信号的情况,如果只有一路有就直接让它通过就是了.现在我用的是ls123触发器来判断哪路有信号但是试验板做出来老是有问题不知道还有没有别的什么思路不用ls123触发器.谢谢希望高手指点…

查看全部问答>

DS1302有关问题求指教

在定入单字节中为什么是for(i=0;i>1;         sck=1;         }这个时候的SDA是怎么样放的,为什么是与上0X01呢?而读出单字节的for(i=0;i>1;               &nbs ...…

查看全部问答>

拜托大家帮帮忙,电子秤的程序,帮我看看哪里错了,谢谢!!!

#include   \"60S2_V3.h\"    char bdata bittest ; sbit TIbit = bittest ^ 0 ; unsigned long i , sum = 0 , result = 0 , temp , ASC[] , count ; u8 T_COUNT , adc_hi , adc_low , a , sign ; void main(void) { / ...…

查看全部问答>

困扰我多久的问题,望高手解答

下面一个问题,已经困扰我很久了,一直找不到罪魁祸首,希望有高手帮我解答,非常感谢! 问题背景: 1.此项目为BLDC电机供电,提供310V,15V,GND,电机的VSP与FG由其他控制板输出或接收 2.附件原理图只是电源部分的简要示意图,非完整原理图, ...…

查看全部问答>