当前位置: 首页 > news >正文

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

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

相关文章:

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

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

C/C++每日一练(20230221)

目录 1. 格雷编码 2. 矩阵问题 3. 搜索旋转排序数组 II 1. 格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。 给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。即使有多个不同答案&#…...

用Python采集热门城市景点数据并简单制作数据可视化图

前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 模块使用: 爬虫部分: requests parsel csv 数据分析部分: pandas pyecharts 如何安装模块: win R 输入 cmd 输入安装命令: pip install 模块名 回车 pycharm里面安装 terminal 输入安装命令…...

论文阅读:pixelNeRF: Neural Radiance Fields from One or Few Images

中文标题:从一或少量图像中构建神经辐射场 提出问题 NeRF效果虽然惊艳,但是其需要大量环绕图像以及长时间的训练。 创新点 与原始的NeRF网络不使用任何图像特征不同,pixelNeRF将与每个像素对齐的空间图像特征作为输入。也可以集合更多输入…...

C++项目——高并发内存池(1)--介绍及定长内存池

1.什么是内存池 1.1 池化技术 将程序中需要经常使用的核心资源先申请出来,放在一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量。 比如之前博文实现的线程池,就是预先的申请出…...

网络有线无线配置

一、需求 在无线接入区内,当Lsw1的上联口出现故障时,需要通过AP1-LSw1-LSw2-LSw3的路径访问公网server3。这是因为AP1通过无线网连接到LSw1,而LSw1与LSw3之间的链路出现故障,无法直接访问公网server3。因此,流量需要通…...

阅读笔记——Feature-Point Matching for Aerial and Ground Images方法

阅读笔记——Feature-Point Matching for Aerial and Ground Images 1.应用背景 在三维重建应用中,常用的是航空影像,但在城市环境中,面对大量的高层建筑物,航空影像往往不能很好的重建出完整的三维模型。因为航空影像视角约束以…...

前端工程师面试题10条必会笔试题

布局 左边20% 中间自适应 右边200px 不能用定位答案:圣杯布局/双飞翼布局或者flex什么叫优雅降级和渐进增强?渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进…...

【技术分享】Web自动化之Selenium安装

Web 应用程序的验收测试常常涉及一些手工任务,例如打开一个浏览器,并执行一个测试用例中所描述的操作。但是手工执行的任务容易出现人为的错误,也比较费时间。因此,将这些任务自动化,就可以消除人为因素。Selenium 可以…...

【Linux】进程状态的理解

✍作者:阿润菜菜 📖专栏:Liunx系统编程 本文目录概述两个先行概念我们为啥创建进程Linux下的进程状态1. R 运行状态2.S 休眠状态 --- 可中断休眠状态3. D 磁盘休眠状态 ---不可中断休眠4.T 暂停状态 (t 追踪暂停状态)5…...

打游戏哪种蓝牙耳机比较好?适合玩游戏的无线蓝牙耳机

2023年耳机市场一如既往地卷,不只是卷音质,还在外观和功能上做了许多的改进,以至于现在哪怕不懂耳机的人从各电商平台都能闭眼入一个款平价品牌耳机且极少会踩雷,玩游戏是当前年轻人的娱乐方式,下面整理了几款适合玩游…...

Fortinet推出新一代自研安全芯片,跨所有网络边缘加速网络与安全融合

专注网络与安全融合的全球网络安全领导者 Fortinet(NASDAQ:FTNT),近日宣布推出新一代自研安全芯片 FortiSP5,作为 Fortinet ASIC 技术的最新突破,有力推动了分布式网络边缘安全的重大飞跃。FortiSP5 源自 F…...

ChatGPT爆火背后的真相:学编程已经成为必选项

这一阵最热门的话题,莫过于人工智能新选手——ChatGPT,在推出后只用了两个月就积累了1亿用户!它的出现在科技圈掀起了一阵“惊涛骇浪”,有人称ChatGPT的意义,堪比当年蒸汽机的出现,它足以爆发新一轮的“工业…...

Unity UI框架

一、简介 最近在各大网站看了一下 Unity3d 的 UI 框架,各种 UI 框架已经有很多的版本了,各有千秋,有的功能虽然写的完善,但用起来太复杂,有的框架功能不完善,搞个课程就上架了,还有什么 MVC 框…...

vue2提取vue-router的title单独存放,使用i18n实现

成品效果 首先引入i18n(vue-i18n官网文档) 依赖包 npm install vue-i18n8然后单独在src目录下新建一个文件夹lang,存放相对应的变量名称,我这里只做显示中文所以其他引入我都注释了,具体目录如下: src\lang/zh.js部分代码 export…...

【Linux操作系统】【综合实验三 用户帐号、文件系统与系统安全管理】

文章目录一、实验目的二、实验要求三、实验内容四、实验报告要求一、实验目的 要求掌握Linux系统用户的创建、删除与管理操作;熟悉Linux文件系统的管理模式,学会创建用户文件系统并装载和卸载文件系统;掌握超级用户的管理方式与权限&#xf…...

sqlite3数据库-sqlite语句1(五)

DML(Data Manipulation Language,数据操作语言) SELECT:查询表中的数据;SELECT语句中使用WHERE子句SELECT <列名>,... FROM <表名> WHERE <条件表达式>;SELECT id,name,purchase_price FROM Product; /*使用逗号分隔查询多列,顺序同子句顺序*/ SELECT * FROM…...

【图像分类】卷积神经网络之LeNet5网络模型实现MNIST手写数字识别

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在上一篇博文中我们对LeNet5网络模型的结构进行了剖析,本篇博文,我们将使用PyTorch搭建LeNet5实现MNIST手写数字…...

前端开发环境搭建

文章目录Node.js是什么安装查看版本入门示例NPM使用 npm 命令安装模块常见命令使用淘宝 NPM 镜像TypeScript安装入门示例从github拉取构建项目如何从零创建一个TypeScript项目规划目录结构新建项目Web App运行服务添加依赖打包使用browserify打包使用webpack打包推荐流程目录配…...

学习Flask之四、网页表单

第二章介绍的request对象&#xff0c;使用了客户端请求的所有信息。特别地&#xff0c;request.form提供了对POST请求提交的表单数据的访问。尽管Flask请求对象的支持足于处理网页单&#xff0c;但是还有很多作务很繁锁和重复。两个很好的例子是产生HTML表单代码和验证表单数据…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

【UE5 C++】通过文件对话框获取选择文件的路径

目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 &#xff0c;这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器&#xff0c;右键点击 .uproject 文件&#xff0c;选择 "Generate Visual Studio project files"&#xff0c;重…...

CVE-2023-25194源码分析与漏洞复现(Kafka JNDI注入)

漏洞概述 漏洞名称&#xff1a;Apache Kafka Connect JNDI注入导致的远程代码执行漏洞 CVE编号&#xff1a;CVE-2023-25194 CVSS评分&#xff1a;8.8 影响版本&#xff1a;Apache Kafka 2.3.0 - 3.3.2 修复版本&#xff1a;≥ 3.4.0 漏洞类型&#xff1a;反序列化导致的远程代…...

华为OD机考- 简单的自动曝光/平均像素

import java.util.Arrays; import java.util.Scanner;public class DemoTest4 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint[] arr Array…...