I.MX6ULL_Linux_系统篇(17) uboot分析-启动linux

news2024/10/5 15:02:11

bootz 启动 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 函数, 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!”。

第 382、 383 行初始化函数 bootz_setup 的参数 start 和 end。
第 385 行,打印启动信息,如果 Linux 系统镜像正常的话就会输出如图所示的信息:

 接下来看一下函数 bootm_find_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 中,函数代码如下:

 

 

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

接着回到do_bootm_states函数中,继续分析函数 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 的参数。
第 679~689 行是处理 BOOTM_STATE_OS_FAKE_GO 状态的,但是要我们没用使能 TRACE功能,因此宏 CONFIG_TRACE 也就没有定义,所以这段程序不会编译。
第 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 中,函数内容如下:

第 274~292 行是 64 位 ARM 芯片对应的代码, Cortex-A7 是 32 位芯片,因此用不到。
第 293 行,变量 machid 保存机器 ID,如果不使用设备树的话这个机器 ID 会被传递给 Linux内核, Linux 内核会在自己的机器 ID 列表里面查找是否存在与 uboot 传递进来的 machid 匹配的
项目,如果存在就说明 Linux 内核支持这个机器,那么 Linux 就会启动!如果使用设备树的话这个 machid 就无效了,设备树存有一个“兼容性”这个属性, Linux 内核会比较“兼容性”属
性的值(字符串)来查看是否支持这个机器。
第 295 行,函数 kernel_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 函数做一些清理工作。
继续回到函数 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/360533.html

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

相关文章

[Golang实战]github.io部署个人博客hugo[新手开箱可用][小白教程]

[Golang实战]github.io部署个人博客hugo[新手开箱可用][小白教程]1.新手教程(小白也能学会)2.开始准备2.1myBlog是hugo的项目1.安装Hugo2.创建hugo项目2.2 xxxx.github.io是github.io中规定的pages项目3.成功部署4.TODO自动化workflows部署github.io1.新手教程(小白也能学会) …

linux-rockchip-电池相关

可以先安装d-feet,可以查看系统里所有的DBus连接 sudo apt-get install d-feet查看电池的状态: cat /sys/class/power_supply/BAT0/status查看剩余电量百分比: cat /sys/class/power_supply/BAT0/capacity如果有多块电池,查看第…

【Spark分布式内存计算框架——Spark SQL】15. Catalyst 优化器

第九章 Catalyst 优化器 在第四章【案例:电影评分数据分析】中,运行应用程序代码,通过WEB UI界面监控可以看出,无论使用DSL还是SQL,构建Job的DAG图一样的,性能是一样的,原因在于SparkSQL中引擎…

图卷积神经网络分类的pytorch实现

图神经网络(GNN)目前的主流实现方式就是节点之间的信息汇聚,也就是类似于卷积网络的邻域加权和,比如图卷积网络(GCN)、图注意力网络(GAT)等。下面根据GCN的实现原理使用Pytorch张量,和调用torch_geometric包,分别对Cora数据集进行…

Java函数式接口

3 函数式接口 3.1 函数式接口概述 函数式接口:有且仅有一个抽象方法的接口 Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口只有确保接口中有且仅有一个抽象方法, Java中的Lambda才能顺利地进行推导…

不容错过!飞桨深度学习与大模型产业应用专场24日等你来!

人工智能教父Hinton曾评价,“深度学习将无所不能”,从聊天机器人、自动驾驶到语音助手,深度学习早已在不知不觉中渗透进我们的生活,而AI大模型又是一项深度学习技术的新突破。深度学习、大模型作为人工智能发展的重要方向&#xf…

前端开发项目规范写法介绍

1. 基本原则 结构、样式、行为分离 尽量确保文档和模板只包含 HTML 结构,样式都放到样式表里,行为都放到脚本里。 缩进 统一两个空格缩进(总之缩进统一即可),不要使用 Tab 或者 Tab、空格混搭。 文件编码 使用不带 BOM 的 UTF-8 编码。 在 HTML中指定编码 <meta c…

C# 利用FluentFTP实现FTP上传下载功能

FTP作为日常工作学习中&#xff0c;非常重要的一个文件传输存储空间&#xff0c;想必大家都非常的熟悉了&#xff0c;那么如何快速的实现文件的上传下载功能呢&#xff0c;本文以一个简单的小例子&#xff0c;简述如何通过FluentFTP实现文件的上传和下载功能。仅供学习分享使用…

c++提高篇——queque容器

一、queque容器基本概念 Queue是一种先进先出(FIFO)的教据结构&#xff0c;它有两个出口 队列容器允许从一端新增元素&#xff0c;从另一端移除元素。队列中只有队头和队尾才可以被外界使用&#xff0c;因此队列不允许有遍历行为队列中进数据。 queque容器可以形象化为生活中…

第一个Java程序(初识Java)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【JavaSE_primary】 文章目录1.Java概述1.1什么是Java1.2Java之父2.0第一个Java程序编译运行.class3.0程序如何跑起来的&#xff1f;3.1J…

39、基于51单片机声控光控灯人体感应路灯照明灯系统设计

摘 要 随着社会的不断进步&#xff0c;人们对低碳生活逐步认识和接受&#xff0c;并从很多方面开始关注&#xff0c;尤其是在环保上做出了很多努力。利用声音和光线的强弱来控制开关的断开或者闭合的电子产品来能够有效的降低能耗&#xff0c;节约能源。它不仅适用于住宅区的…

CMake构建静态库与动态库以及使用

CMake构建静态库与动态库一、任务二、准备工作三、编译共享库四、ADD_LIBRARY指令五、编译静态库5.1、SET_TARGET_PROPERTIES指令5.2、GET_TARGET_PROPERTY指令六、动态库版本号七、安装共享库和头文件八、使用外部共享库和头文件8.1、准备工作8.2、引入头文件搜索路径8.3、为 …

leetcode 11~20 学习经历

LeetCode 习题 11 - 2011. 盛最多水的容器12. 整数转罗马数字13. 罗马数字转整数14. 最长公共前缀15. 三数之和16. 最接近的三数之和17. 电话号码的字母组合18. 四数之和19. 删除链表的倒数第 N 个结点20. 有效的括号小结11. 盛最多水的容器 给定一个长度为 n 的整数数组 heigh…

【Servlet篇】Request请求转发详细解读

文章目录1. 前言2. 实战案例3. 特点1. 前言 请求转发是一种在服务器内部的资源跳转方式&#xff0c;如图&#xff1a; 上图的大致过程为&#xff0c;浏览器发送请求给服务器&#xff0c;服务器中 a 资源接收到请求&#xff0c;资源 a 处理完请求后将请求发送给资源 b&#xff…

cdr最新2023版本发布更新及CorelDraw功能介绍

CDR作为一款专业的平面设计软件&#xff0c;拥有着庞大的用户群体&#xff0c;而每年春天CorelDRAW新版本的发布也牵动着每一位小伙伴的心。CorelDraw2023近期刚刚发布本人就开始试用&#xff0c;感觉非常良好&#xff0c;特别给大家提出升级的N个理由!CorelDRAW2023最新版内置…

【基础算法】差分的应用(一维差分和二维差分)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

FPGA 20个例程篇:20.USB2.0/RS232/LAN控制并行DAC输出任意频率正弦波、梯形波、三角波、方波(一)

在最后一个例程中笔者精挑细选了一个较为综合性的项目实战&#xff0c;其中覆盖了很多知识点&#xff0c;也是从一个转产产品中所提炼出来的&#xff0c;所以非常贴近实战项目。 整个工程实现了用户通过对上位机PC端人机界面的操作&#xff0c;即可达到控制豌豆开发并行DAC输出…

Java---高级流

目录 一、转换流 &#xff08;1&#xff09;指定的字符集读写数据 二、序列化流和反序列化流 三、解压缩流和压缩流 &#xff08;1&#xff09;解压缩流 &#xff08;2&#xff09;压缩流 一&#xff1a;压缩文件 二&#xff1a;压缩文件夹 注&#xff1a;本文并未介绍J…

CVE-2023-24055 KeePass信息明文传输漏洞复现

前言 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 如果文章中的漏洞出现敏感内容产生了部分影响&#xff0c;请及时联系作者&#xff0c;望谅解。 一、漏洞描述 漏洞简述 Kee…

STM32 SystemInit()函数学习总结

拿到程序后如何看系统时钟&#xff1f;User文件夹——system_stm32f4xx程序&#xff0c;先找systemcoreclock(系统时钟&#xff09;但是这里这么多个系统时钟应该如何选择?点击魔法棒&#xff0c;然后点击C/C可以看到define的是F40_41XXX.USE这一款 &#xff0c;对应着就找出了…