12_Uboot启动流程_4

news2024/10/7 12:28:00

目录

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命令的执行过程,如图所示:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/507570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2023/05/09】Scratch

Hello!大家好,我是霜淮子,2023倒计时第4天。 Share The mighty desert is burning for the love of a blade of grass who shaks her head and laughs and flies away. 译文: 无垠的沙漠热烈追求一叶绿草的爱,她摇摇…

PCL中点云分割算法简析

文章目录 前言一、点云分割算法简介1.1 基于RANSAC的点云分割1.2 基于聚类的点云分割1.2.1 欧式聚类分割 1.3 基于深度学习的点云分割 二、算法示例2.1 基于RANSAC的平面分割2.2 欧式聚类2.3 基于PointNet的点云分割 总结 前言 点云分割算法广泛应用于激光遥感、无人驾驶、工业…

centos安装nginx教程

安装所需环境 Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境。 一. gcc 安装 安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境&#xff0c…

【ESD专题】案例:TVS管钳位电压能不能通过TLP测试数据表征?

这几天遇到一个问题,就是还是想说TVS管导入的时候需要进行IEC61000-4-2 8kV接触静电的钳位波形测试。 比如有时可以看到规格书中给出对应的在IEC61000-4-2 8kV接触时的真实钳位波形: 根据我们文章【ESD专题】TVS管的选择的误区及钳位电压测试方法和一些参考手册所说…

计算机操作系统第四版第七章文件管理—课后习题答案

1.何谓数据项、记录和文件? 数据项:是最低级的数据组织形式,可以分为两种类型:基本数据项和组合数据项。基本数据项是用于描述一个对象的某种属性的字符集,是数据组织中可以命名的最小逻辑数据单位,又称为字…

使用volta对node版本进行控制

安装volta 首先下载volta 下载完成之后在电脑上使用命令行工具查看是否安装成功 volta -v 使用 volta -h 命令可以查看volta的一些用法 安装全局的node版本,可以有三种,第一种是安装最新的,第二种是安装某一个大版本下的,第三种是安装指定的node版本(安装的时候需要等待一段时…

Rest风格复习

Rest风格复习 简介三种注解的风格快速开发 简介 通过一段路径和访问方式来确定访问资源的行为方式 使用POST方式 // value定义 路径 method定义访问的方式RequestMapping(value "/users",method RequestMethod.POST)ResponseBodypublic String save(){System.out.…

Andrew Ng和OpenAI教你写prompt

课程地址: https://learn.deeplearning.ai/chatgpt-prompt-engb站搬运: https://www.bilibili.com/video/BV1No4y1t7Zn 教学人员:Lsa Fulford, Andrew NG LLM的两种样式 Base LLM:基于文本训练数据预测下一个词的概率&#xff0…

制作Alpine Linux镜像报错errors: 15 distinct packages available

1.执行报错 执行docker build -t 镜像:版本 -f Dockerfile . 报错: 2.查看网上的解决思路 网上文档解决思路: 这边我做了一下改变把这些写入了dockerfile 加了几个RUN RUN rm -rf /var/cache/apk RUN mkdir -p /var/cache/apk RUN apk update -v 发现还…

Java基本数据类型详解及应用示例

Java作为一门强类型语言,基本数据类型是非常重要的概念。Java中基本数据类型分为四种类别:整数类型、浮点数类型、字符类型和布尔类型。其中,每一种数据类型都有着不同的占用字节数和表示范围,合理使用选择不同的数据类型可以提高…

数组低效的“插入”和“删除”

插入操作 假设数组的长度为 n,现在,如果我们需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来,给新来的数据,我们需要将第 k~n 这部分的元素都顺序地往后挪一位。那插入操作的时间复杂度是多少呢&a…

ChatGPT 中文指令指南,教会你如何使用chatgpt实现中文你想要的答案

🧠 ChatGPT 中文指令指南,教会你如何使用chatgpt实现中文你想要的答案 1.学习英语–替代词典 App 场景例子Prompts解释中文英文意思,并解释单词的词根词缀。可以替代词典。告诉我 Egocentric 的词性和音标,并使用中文和英文解释…

OpenAI开源语音识别模型Whisper在Windows系统的安装详细过程

1、安装Python Python的安装很简单,点击这里进行下载。 安装完成之后,输入python -V可以看到版本信息,说明已经安装成功了。 如果输入python -V命令没有看到上面的这样的信息,要么是安装失败,要么是安装好之后没有自…

【自然语言处理】【大模型】CodeGen:一个用于多轮程序合成的代码大语言模型

CodeGen:一个用于多轮程序合成的代码大语言模型 《Code Gen: An Open Large Language Model For Code with Multi-Turn Program Synthesis》 论文地址:https://arxiv.org/pdf/2203.13474.pdf?trkpublic_post_comment-text 相关博客 【自然语言处理】【大…

【EasyPoi实战系列】Spring Boot使用EasyPoi实现一对多的导出 - 第469篇

​ 历史文章(文章累计460) 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》…

Exams/2012 q1g

module top_module (input [4:1] x,output f ); assign f ~x[2]&~x[4] | ~x[1]&x[3] | x[2] & x[3] & x[4]; endmodule

4-Tr0ll-1百个靶机渗透(精写-思路为主)

文章目录 目录 文章目录 前言 一、信息收集 二、提权 1.提权方法1 2.提权方法2 3.提权方法3 4.提权方法4 5.root免密登录的方法 总结 前言 思路清晰: 1.信息收集,寻找内网靶机,得到可以利用的ftp、ssh和web站点的常规端口。 2.因为这个靶机…

Visual Studio 2019离线安装包获取和安装教程

摘要 介绍Visual Studio 2019离线安装方法和配置及注意事项 关键词 VS2019 离线安装 Visual Studio 2019版本与以往的2015、2013、2012版本不同,采用了新的模块化安装方法。微软官方也并未提供ISO镜像,根据官方提供的离线下载方案(docs.mic…

「AI 孙燕姿」翻唱华语乐坛歌曲爆红全网,AI 翻唱将带来哪些影响?是否会有版权等问题?

在某视频平台上,“AI孙燕姿”成了新网红,它翻唱过周杰伦的《发如雪》、翻唱过郭顶的《水星记》、翻唱过赵雷的《我记得》,受到了网友的追捧,甚至有网友宣布“这是2023年最火的声音”。 网上除了AI孙燕姿,还有AI周杰伦…

AOP、spring事务管理

目录 AOP简介 AOP入门案例 AOP配置管理 AOP通知类型 业务层接口执行效率 AOP通知获取数据 百度网盘密码数据兼容处理 AOP事务管理 AOP简介 什么是AOP? AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结…