U-Boot学习(7):内核启动之bootz启动zImage源码分析
在上一节中,我们分析了U-BOOT初始化的流程,最后就是进入U-Boot的命令行中执行了,如果用户没有任何操作,则经过固定延时后将执行默认的bootcmd环境变量里的指令,那这里面肯定就是启动内核了。在U-BOOT简介及命令行指令详解中,我们知道最后执行的应该是bootz指令,那本节就来看一下这个指令如何启动内核的。
文章目录
- 1 bootz的执行时机
- 1.1 do_bootz函数
- 2 bootz_start函数
- 2.1 bootm_headers_t结构体
- 2.2 do_bootm_states:BOOTM_STATE_START
- 2.2.1 bootm_start
- 2.3 bootz_setup
- 2.4 bootm_find_images
- 3 do_bootm_states:启动内核
- 3.1 bootm_os_get_boot_func
- 3.2 boot_prep_linux
- 3.3 boot_selected_os
- 3.3.1 boot_jump_linux
- 3.3.1.1 announce_and_cleanup
- 3.3.1.2 kernel_entry
1 bootz的执行时机
首先我们来看一下在U-Boot命令行中输入bootz后会执行到什么函数中。在cmd/bootz.c中,我们发现了下面的声明:

这个指令定义也会注册到cli_loop中,在U-Boot输入bootz命令时,就会执行do_bootz函数。
1.1 do_bootz函数
下面就来看一下do_bootz函数:

在这个函数中,首先我们执行了bootz_start函数,看样子应该是根据我们提供的参数做一些初始化处理。
接着执行bootm_disable_interrupts关闭中断。在执行 bootz 命令启动加载操作系统之前,U-Boot处于加载操作系统的状态,在这个阶段需要手动关闭中断,确保中断不会干扰到关键的操作。但实际上在ARM中这个函数的实现为空。
最后调用do_bootm_states,这里一方面是告诉U-Boot我们启动的是Linux,另一方面第五个参数为启动需要执行的状态,包括BOOTM_STATE_OS_PREP、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO。
- 其中
BOOTM_STATE_RAMDISK用于实现RAM的文件系统,这里没有执行
所以接下来我们就来分析一下bootz_start和do_bootm_states函数。
2 bootz_start函数
完整函数如下:

2.1 bootm_headers_t结构体
在分析这个函数之前,我们发现有一个bootm_headers_t的images变量在这里经常用到,使用到的bootm_headers_t变量在bootm.c中有声明:
bootm_headers_t images;
完整结构体声明如下:
typedef struct bootm_headers {/** Legacy os image header, if it is a multi component image* then boot_get_ramdisk() and get_fdt() will attempt to get* data from second and third component accordingly.*/image_header_t *legacy_hdr_os; /* image header pointer */image_header_t legacy_hdr_os_copy; /* header copy */ulong legacy_hdr_valid;/** The fit_ members are only used with FIT, but it involves a lot of* #ifdefs to avoid compiling that code. Since FIT is the standard* format, even for SPL, this extra data size seems worth it.*/const char *fit_uname_cfg; /* configuration node unit name */void *fit_hdr_os; /* os FIT image header */const char *fit_uname_os; /* os subimage node unit name */int fit_noffset_os; /* os subimage node offset */void *fit_hdr_rd; /* init ramdisk FIT image header */const char *fit_uname_rd; /* init ramdisk subimage node unit name */int fit_noffset_rd; /* init ramdisk subimage node offset */void *fit_hdr_fdt; /* FDT blob FIT image header */const char *fit_uname_fdt; /* FDT blob subimage node unit name */int fit_noffset_fdt;/* FDT blob subimage node offset */void *fit_hdr_setup; /* x86 setup FIT image header */const char *fit_uname_setup; /* x86 setup subimage node name */int fit_noffset_setup;/* x86 setup subimage node offset */#ifndef USE_HOSTCCimage_info_t os; /* os image info */ulong ep; /* entry point of OS */ulong rd_start, rd_end;/* ramdisk start/end */char *ft_addr; /* flat dev tree address */ulong ft_len; /* length of flat device tree */ulong initrd_start;ulong initrd_end;ulong cmdline_start;ulong cmdline_end;struct bd_info *kbd;
#endifint verify; /* env_get("verify")[0] != 'n' */#define BOOTM_STATE_START (0x00000001)
#define BOOTM_STATE_FINDOS (0x00000002)
#define BOOTM_STATE_FINDOTHER (0x00000004)
#define BOOTM_STATE_LOADOS (0x00000008)
#define BOOTM_STATE_RAMDISK (0x00000010)
#define BOOTM_STATE_FDT (0x00000020)
#define BOOTM_STATE_OS_CMDLINE (0x00000040)
#define BOOTM_STATE_OS_BD_T (0x00000080)
#define BOOTM_STATE_OS_PREP (0x00000100)
#define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */
#define BOOTM_STATE_OS_GO (0x00000400)int state;#if defined(CONFIG_LMB) && !defined(USE_HOSTCC)struct lmb lmb; /* for memory mgmt */
#endif
} bootm_headers_t;
- Legacy Image Header
legacy_hdr_os:指向传统OS镜像头部的指针。legacy_hdr_os_copy:用于存储OS镜像头部的备份。legacy_hdr_valid:标志变量,指示legacy_hdr_os中的头部是否有效。
- FIT Format Members
fit_uname_cfg:指向配置节点的名称(适用于FIT格式)。fit_hdr_os、fit_hdr_rd、fit_hdr_fdt、fit_hdr_setup:指向FIT格式中OS、initramdisk、FDTblob和x86setup镜像头部的指针。fit_uname_os、fit_uname_rd、fit_uname_fdt、fit_uname_setup:分别是FIT格式中OS、initramdisk、FDTblob和x86setup镜像节点的名称。fit_noffset_os、fit_noffset_rd、fit_noffset_fdt、fit_noffset_setup:分别是FIT格式中OS、initramdisk、FDTblob和x86setup镜像节点的偏移。
- Image Information and Execution Pointers
os:存储OS镜像信息的结构体。ep:存储OS的入口点。rd_start、rd_end:RAMdisk的起始和结束地址。ft_addr、ft_len:FlatDeviceTree(平面设备树)的地址和长度。initrd_start、initrd_end:initramdisk的起始和结束地址。cmdline_start、cmdline_end:命令行的起始和结束地址。kbd:键盘设备的信息。
- Verification and Boot State Variables
verify:标志变量,指示是否进行引导镜像的验证。state:用于跟踪引导过程的状态,通过定义的宏表示不同的引导状态。
- Logical Memory Block(LMB)
lmb:用于内存管理的结构体。仅在配置启用LMB且非主机编译环境下可用。
该结构体主要用于存储引导过程中各个阶段的头部信息,包括传统的OS镜像头部和FIT格式的头部信息。这些信息在引导过程中被不同的部分使用,以确保正确加载和执行操作系统。
2.2 do_bootm_states:BOOTM_STATE_START
do_bootm_states这里states参数为BOOTM_STATE_START,执行流程大概如下:
do_bootm_statesret = bootm_start(cmdtp, flag, argc, argv);boot_fn = bootm_os_get_boot_func(images->os.os);...
实际上只执行了bootm_start函数,后面的boot_fn执行后续引导函数,但在BOOTM_STATE_START状态下没有执行。
2.2.1 bootm_start

这个函数就是重置images变量,然后调用一些函数来填充images里的一些参数,最后设置当前images的状态为BOOTM_STATE_START。
其中boot_start_lmb函数,lmb的意思是Logical Memory Block,这个函数的作用是通过环境变量获取引导过程中可用内存的起始地址和大小,然后赋值给images中的相关变量。这里执行了boot_start_lmb函数,在2 bootz_start中函数就不会执行下面这一行了:
lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);
所以等会就不分析这个函数了。
2.3 bootz_setup
函数如下:

这个函数比较简单了,就是zImage镜像有一个固定的头,固定头有一些固定的字段:
#define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
#define BAREBOX_IMAGE_MAGIC 0x00786f62
判断这两个字段其一是否匹配,若匹配则表示这段内存中保存了zImage,我们就把zImage所在内存的头尾赋值start和end变量。
我们可以看一下zImage的二进制:

我们可以发现头信息中有LINUX_ARM_ZIMAGE_MAGIC。
2.4 bootm_find_images
由于我们没有使用ram disk,再去掉没有执行的宏定义,该函数如下:

实际上就是获取设备树文件的相关信息,然后做一些内存检测。
3 do_bootm_states:启动内核
现在回到do_bootz函数最后的do_bootm_states函数中,由前面的2.2 do_bootm_states:BOOTM_STATE_START可知,在bootz_start中执行了这个函数,但是参数是BOOTM_STATE_START。
而在这里states参数为BOOTM_STATE_OS_PREP|BOOTM_STATE_OS_FAKE_GO|BOOTM_STATE_OS_GO。
为了方便分析,这里我把与这三个states无关的代码删掉了,函数如下

3.1 bootm_os_get_boot_func
首先来看一下bootm_os_get_boot_func函数,这个函数很简单:

其中boot_os的定义如下:

在do_bootz中,我们已经设置images->os.os为IH_OS_LINUX了。所以bootm_os_get_boot_func返回的就是do_bootm_linux函数。
3.2 boot_prep_linux
我们继续往下分析代码:

这里就是执行boot_fn函数,实际上就是do_bootm_linux函数。看一下这个函数:

由于我们的参数是BOOTM_STATE_OS_PREP,所以这里仅执行了boot_prep_linux(images)。由函数名可以猜测出,这是为了准备启动Linux而初始化一些参数。看一下这个函数:
static void boot_prep_linux(bootm_headers_t *images)
{char *commandline = env_get("bootargs");if (CONFIG_IS_ENABLED(OF_LIBFDT) && images->ft_len) {
#ifdef CONFIG_OF_LIBFDTdebug("using: FDT\n");if (image_setup_linux(images)) {panic("FDT creation failed!");}
#endif} else if (BOOTM_ENABLE_TAGS) {debug("using: ATAGS\n");setup_start_tag(gd->bd);if (BOOTM_ENABLE_SERIAL_TAG)setup_serial_tag(¶ms);if (BOOTM_ENABLE_CMDLINE_TAG)setup_commandline_tag(gd->bd, commandline);if (BOOTM_ENABLE_REVISION_TAG)setup_revision_tag(¶ms);if (BOOTM_ENABLE_MEMORY_TAGS)setup_memory_tags(gd->bd);if (BOOTM_ENABLE_INITRD_TAG) {/** In boot_ramdisk_high(), it may relocate ramdisk to* a specified location. And set images->initrd_start &* images->initrd_end to relocated ramdisk's start/end* addresses. So use them instead of images->rd_start &* images->rd_end when possible.*/if (images->initrd_start && images->initrd_end) {setup_initrd_tag(gd->bd, images->initrd_start,images->initrd_end);} else if (images->rd_start && images->rd_end) {setup_initrd_tag(gd->bd, images->rd_start,images->rd_end);}}setup_board_tags(¶ms);setup_end_tag(gd->bd);} else {panic("FDT and ATAGS support not compiled in\n");}board_prep_linux(images);
}
如果使能了OF_LIBFDT或BOOTM_ENABLE_TAGS就执行对应的if分支,大概都是根据GD或命令行参数填充全局变量params,它定义如下:
static struct tag *params;struct tag {struct tag_header hdr;union {struct tag_core core;struct tag_mem32 mem;struct tag_videotext videotext;struct tag_ramdisk ramdisk;struct tag_initrd initrd;struct tag_serialnr serialnr;struct tag_revision revision;struct tag_videolfb videolfb;struct tag_cmdline cmdline;/** Acorn specific*/struct tag_acorn acorn;/** DC21285 specific*/struct tag_memclk memclk;} u;
};
3.3 boot_selected_os
最后do_bootm_states就执行boot_selected_os了,从注释可以看出来,这里就会启动Linux内核了,如果正常启动,这个函数就不会返回了。

该函数实际上也是调用boot_fn(do_bootm_linux)函数,如果成功的话函数就不会再返回了:

在来看一下do_bootm_linux函数:

这次就是执行第三个if分支的boot_jump_linux(images, flag)函数了,从函数名可以看出,就是最终我们跳转运行Linux内核的函数。
3.3.1 boot_jump_linux
最后就来看一下这个函数,为了代码简洁,省略了不会执行的部分:

首先我们从GD中获取machine id赋值给machid,然后从环境变量中获取machid对比一下是否一致。接下来的bootstage_mark是输出boot进度的函数。
3.3.1.1 announce_and_cleanup

这里实际上就是做一些清除函数,比如在U-Boot中我们初始化了USB,我们就要关闭。和所有的BootLoader一样,上电时系统是什么状态,在运行APP前就要恢复什么状态。否则可能会影响后续Linux内核程序的运行。具体可以参考我的这篇文章单片机中BootLoader的严谨实现详解。
3.3.1.2 kernel_entry
最后我们就执行:
kernel_entry(0, machid, r2);
前面我们设置了:
kernel_entry = (void (*)(int, int, uint))images->ep;
这个函数实际上是Linux内核中的第一行代码,类似于:
static void (*farewellBootloader)(void) = 0;
farewellBootloader = (void (*)(void))appEntry;
farewellBootloader();
这样就跳转到Linux中执行代码了。至于这边传了三个参数,分别作为r0,r1和r2寄存器的值,传递给Linux内核使用。
至于Linux内核如何使用这些参数,后续我们分析Linux内核启动源码的时候再来看。
相关文章:
U-Boot学习(7):内核启动之bootz启动zImage源码分析
在上一节中,我们分析了U-BOOT初始化的流程,最后就是进入U-Boot的命令行中执行了,如果用户没有任何操作,则经过固定延时后将执行默认的bootcmd环境变量里的指令,那这里面肯定就是启动内核了。在U-BOOT简介及命令行指令详…...
[GN] DP学习笔记板子
文章目录 Bitset滚动数组多重背包区间DP树形dp状压dp模拟退火 Bitset 使用bitset需要引用<bitset>头文件。 其声明方法为: std::bitset<N>s; (N为s长度)常用函数: b.any() 判断b中是否存在值为1的二进制位 b.none() 判断b中是否不存在值为1的二…...
GLog开源库使用
Glog地址:https://github.com/google/glog 官方文档:http://google-glog.googlecode.com/svn/trunk/doc/glog.html 1.利用CMake进行编译,生成VS解决方案 (1)在glog-master文件夹内新建一个build文件夹,用…...
微信小程序如何实现点击上传图片功能
如下所示,实际需求中常常存在需要点击上传图片的功能,上传前显示边框表面图片显示大小,上传后将图形缩放到边框大小。 实现如下: .wxml <view class="{{img_src==?blank-area:}}" style="width:100%;height:40%;display:flex;align-items: center;jus…...
Windows Qt C++ VTK 绘制三维曲线
Qt 自带数据可视化从文档上看,只能实现三维曲面。 QwtPlot3D在Qt6.6.0上没编译通过。 QCustomPlot 只能搞二维。 VTK~搞起。抄官网demo。 后续需求: 1、对数轴 2、Y轴逆序 3、Z轴值给色带,类似等高线图的色带 期待各位大佬多多指导。…...
Android T 远程动画显示流程(更新中)
序 本地动画和远程动画区别是什么? 本地动画:自给自足。对自身SurfaceControl矢量动画进行控制。 远程动画:拿来吧你!一个app A对另一个app B通过binder跨进程通信,控制app B的SurfaceControl矢量动画。 无论是本地动画还是远程…...
【计算机网络】【练习题及解答】【新加坡南洋理工大学】【Computer Control Network】
说明: 仅供学习使用。 一、题目描述 题目共4问,描述网络通信中的 帧传输时延(Frame Delay)、传播时延(Propagation Delay),以及 链接利用率(Link Utilization) 的相关…...
云计算HCIE备考经验分享
大家好,我是来自深圳信息职业技术学院22级鲲鹏3-1班的刘同学,在2023年9月19日成功通过了华为云计算HCIE认证,并且取得了A的成绩。下面把我的考证经验分享给大家。 转专业进鲲鹏班考HCIE 大一上学期的时候,在上Linux课程的时候&…...
Threejs API——`OrbitControls`相机控件
文章目录 API用法API OrbitControls 相机控制用法 导入import {OrbitControls } from three/examples/jsm/controls/OrbitControls.js import {DRACOLoader,AmbientLight,Color,MOUSE,...
远程教育:低代码在教育技术领域的重塑之力
新冠肺炎大流行对世界各地的行业产生了影响,其中一些行业的影响远远超过其他行业。食品、零售、供应链、娱乐和航空业是受影响最大的行业,为确保不间断运营,这引发了一场数字革命。相信,这种数字化的采用将长期保持下去࿰…...
vue 模板语法值class操作
class.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>class</title><!-- 确保引入正确的Vue版本库,下面只是示例,需要替换为实际可工作的CDN地址 --><sc…...
MySQL的原生API实现插入数据后在可视化工具上不显示的问题解决
显示表中有两行数据,该表也设置了主键和唯一索引 点进表里看却没有数据 问题原因出现在这里,虽然很多常用的数据库连接池都会开启自动提交,但ibatis的SqlSession使用sessionFactory.openSession()创建时,默认的自动提交是false&am…...
Blender教程(基础)-内插面、分离、环切、倒角-08
一、内插面 菜单位置如下图位置。 单击需要处理的面,出现一个黄色的圈。 1、菜单选中内插 鼠标悬停在黄色圈内单击左键可以来回实现内插,但是发现并不好操作。 2、快捷键内插 在选中需要操作的面之后,鼠标移动到外面,键盘在英…...
Unity 自动轮播、滑动轮播
如图所示,可设置轮播间隔,可左右滑动进行轮播 1.在UGUI创建个Image,添加自动水平组件 2.添加并配置脚本 3.代码如下,都有注释 using UnityEngine; using UnityEngine.UI;public class IndicatorManager : MonoBehaviour {public …...
纯html+js+css个人博客
首页 <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8"><title>主页</title><!-- 引入layui css文件 --><link rel"stylesheet" href"layui-…...
二百二十一、HiveSQL报错:return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask
一、目的 在运行HiveSQL时,执行报错 tatement: FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask 二、在yarn上查看任务报错 The required MAP capability is more than the supported max container capability in t…...
JavaEE学习笔记 2024-1-25 --VUE的入门使用
上一篇 个人整理非商业用途,欢迎探讨与指正!! 文章目录 14.VUE基础14.1VUE的入门使用14.2条件判断14.3循环渲染14.4v-bind绑定标签属性14.5v-model表单标签的双向绑定14.6事件处理14.7axios 14.VUE基础 前端框架 UI框架:页面渲染Bootstrap,L…...
php-fpm详细讲解
PHP-FPM(FastCGI Process Manager)是PHP的一种运行模式,用于处理动态HTTP请求。 它与传统的模块式PHP(如Apache模块)相比,将PHP解析和执行过程单独封装为一个独立的进程池,通过FastCGI协议与We…...
小白水平理解面试经典题目LeetCode 455 Assign Cookies【Java实现】
455 分配cookies 小白渣翻译: 假设你是一位很棒的父母,想给你的孩子一些饼干。但是,你最多应该给每个孩子一块饼干。 每个孩子 i 都有一个贪婪因子 g[i] ,这是孩子满意的 cookie 的最小大小;每个 cookie j 都有一个…...
uniapp 问题汇总-问题数(2)
ios scroll-view无法滚动 使用uview折叠面板嵌套scroll-view 嵌套之后安卓可以滚动,ios无法滚动 <u-collapse accordion opencollapseOpen changecollapseChange ref"uCollapse" :valueuCollapseValue><u-collapse-item :nameindex :title&quo…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
