ARM Linux 内核启动1 —— 汇编阶段

news/2024/5/3 13:07:37/文章来源:https://blog.csdn.net/weixin_42109053/article/details/129979565

一、Makefile分析

1、Makefile 分析

(1) kernel 的 Makefile 写法和规则等,和 uboot 的 Makefile 是一样的,甚至 Makefile 中的很多内容都是一样的。

在这里插入图片描述


(2) kernel 的 Makefile 比 uboot 的 Makefile 要复杂,这里我们并不会一行一行的详细分析。

(3) Makefile 中只有一些值得关注的会强调一下,其他不强调的地方暂时可以不管。

(4) Makefile 中刚开始,定义了 kernel 的内核版本号。这个版本号挺重要(在模块化驱动安装时会需要用到),要注意会查,会改。

在这里插入图片描述


(5) 在 make 编译内核时,也可以通过命令行给内核 makefile 传参(跟 uboot 配置编译时传参一样)。譬如 make O=xxx 可以指定不在源代码目录下编译,而到另外一个单独文件夹下编译。


(6) kernel 的顶层 Makefile 中定义了 2 个变量很重要,一个是 ARCH,一个是 CROSS_COMPILE

ARCH 决定当前配置编译的路径,譬如 ARCH = arm 的时候,将来在源码目录下去操作的 arch/arm 目录CROSS_COMPILE 用来指定交叉编译工具链的路径和前缀。


(7) CROSS_COMPILE = xxxARCH = xxxO=xxx 这些都可以在 make 时,通过命令行传参的方式传给顶层 Makefile。

所以有时候你会看到别人编译内核时:make O=/tmp/mykernel ARCH=arm CROSS_COMPILE=/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-


2、链接脚本分析

(1) 分析链接脚本的目的,就是找到整个程序的 entry。

(2) kernel 的链接脚本并不是直接提供的,而是提供了一个汇编文件 vmlinux.lds.S,然后在编译的时候,再去编译这个汇编文件得到真正的链接脚本 vmlinux.lds

(3) vmlinux.lds.Sarch/arm/kernel/ 目录下。

在这里插入图片描述


(4) 思考:为什么 linux kernel 不直接提供 vmlinux.lds ,而要提供一个 vmlinux.lds.S ,然后在编译时才去动态生成 vmlinux.lds 呢

猜测:.lds 文件中只能写死,不能用条件编译。但是我们在 kernel 中,链接脚本确实有条件编译的需求(但是 lds 格式又不支持),于是 kernel 工作者找了个投机取巧的方法,就是把 vmlinux.lds 写成一个汇编格式,然后汇编器处理的时候顺便条件编译给处理了,得到一个不需要条件编译的 vmlinux.lds。

在这里插入图片描述


(5) 程序的入口在哪里?从 vmlinux.ldsENTRY(stext) 可以知道,入口符号是 stext,在 kernel 工程中搜索这个符号,发现 arch/arm/kernel/ 目录下的 head.S 和 head-nommu.S 中都有。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


(6) head.S 是启用了 MMU 情况下的 kernel 启动文件,相当于 uboot 中的start.S 。head-nommu.S 是未使用 MMU 情况下的 kernel 启动文件。

在这里插入图片描述


二、head.S 文件分析

1、内核运行的物理地址与虚拟地址

(1) KERNEL_RAM_VADDR(VADDR 就是 virtual address),这个宏 定义了内核运行时的虚拟地址。值为 0xC0008000

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


(2) KERNEL_RAM_PADDR(PADDR 就是 physical address),这个宏 定义内核运行时的物理地址。值为 0x30008000

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


(3) 总结:内核运行的物理地址是 0x30008000,对应的虚拟地址是 0xC0008000。


2、内核的真正入口

(1) 内核的真正入口就是 ENTRY(stext) 处。

(2) 前面的 __HEAD 定义了后面的代码属于 段名为.head.text 的段。

在这里插入图片描述


3、内核运行的硬件条件

(1) 内核的起始部分代码,是被解压代码调用的。回忆之前讲 zImage 的时候,uboot 启动内核后,实际调用运行的是 zImage 前面的那段未经压缩的解压代码,解压代码运行时,先将 zImage 后段的内核解压开,然后再去调用运行真正的内核入口。


(2) 内核启动不是无条件的,而是有一定的先决条件,这个条件由启动内核的 bootloader(我们这里就是 uboot )来构建保证。


(3) ARM 体系中,函数调用时实际是通过寄存器传参的(函数调用时,传参有两种设计:一种是寄存器传参,另一种是栈内存传参)。所以 uboot 中最后 theKernel (0, machid, bd->bi_boot_params); 执行内核时,运行时 实际把 0 放入 r0 中,machid 放入到了 r1 中,bd->bi_boot_params 放入到了 r2 中。ARM 的这种处理技巧刚好满足了 kernel 启动的条件和要求。

在这里插入图片描述


(4) kernel 启动时,MMU 是关闭的,因此硬件上需要的是物理地址

但是内核是一个整体(zImage),只能被链接到一个地址(不能分散加载)这个链接地址肯定是虚拟地址。

因此内核运行时,前段 head.S 中尚未开启 MMU 之前的这段代码就很难受。所以这段代码必须是位置无关码,而且其中涉及到操作硬件寄存器等时,必须使用物理地址

在这里插入图片描述


三、内核启动的汇编阶段

1、__lookup_processor_type

(1) 我们从 cp15 协处理器的 c0 寄存器中,读取出硬件的 CPU ID 号,然后调用这个函数来进行合法性检验。如果合法则继续启动,如果不合法则停止启动,转向 __error_p 启动失败。

在这里插入图片描述


(2) 该函数检验 cpu id 的合法性方法是:内核会维护一个 本内核支持的 CPU ID 号码的数组,然后该函数所做的就是:将从硬件中读取的 cpu id 号码,和数组中存储的各个 id 号码依次对比,如果没有一个相等,则不合法,如果有一个相等的,则合法。


(3) 内核启动时设计这个校验,也是为了内核启动的安全性着想。


2、__lookup_machine_type

(1) 该函数的设计理念和思路,和上面校验 cpu id 的函数一样的。不同之处是本函数校验的是机器码。

在这里插入图片描述


3、__vet_atags

(1) 该函数的设计理念和思路,和上面 2 个一样,不同之处是用来校验 uboot 给内核的传参 ATAGS 格式是否正确。这里说的传参指的是,uboot 通过 tag 给内核传的参数(主要是板子的内存分布 memtag、uboot 的 bootargs)。

在这里插入图片描述

在这里插入图片描述


(2) 内核认为,如果 uboot 给我的传参格式不正确,那么我就不启动。


(3) uboot 给内核传参的部分 如果不对,是会导致内核不启动的。 譬如 uboot 的 bootargs 设置不正确,内核可能就会不启动。


4、__create_page_tables

(1) 顾名思义,这个函数用来建立页表。

(2) linux 内核本身被链接在虚拟地址处,因此 kernel 希望尽快建立页表,并且启动 MMU 进入虚拟地址工作状态。

但是 kernel 本身工作起来后,页表体系是非常复杂的,建立起来也不是那么容易的。kernel 想了一个好办法。

(3) kernel 建立页表其实分为 2 步

第一步,kernel 先建立了一个段式页表(和 uboot 中之前建立的页表一样,页表以 1MB 为单位来区分的),这里的函数就是建立段式页表的。段式页表本身比较好建立(段式页表 1MB 一个映射,4GB 空间需要 4096 个页表项,每个页表项 4 字节,因此一共需要 16KB 内存来做页表),坏处是比较粗,不能精细管理内存;

在这里插入图片描述

第二步,再去建立一个细页表(4kb 为单位的细页表),然后启用新的细页表,废除第一步建立的段式映射页表。

(4) 内核启动的早期建立段式页表,并在内核启动的前期使用;内核启动后期,就会再次建立细页表并启用。等内核工作起来之后就只有细页表了。


5、__switch_data

(1) 建立了段式页表后,进入了__switch_data 部分,这东西是个函数指针数组。

(2) 分析得知下一步要执行 __mmap_switched 函数。

在这里插入图片描述

(3) 复制数据段、清除bss段(目的是构建 C 语言运行环境)。

(4) 保存起来 cpu id 号、机器码、tag传参的首地址。

(5) b start_kernel 跳转到 C 语言运行阶段。

在这里插入图片描述


总结:汇编阶段其实也没干啥,主要原因是 uboot 干了大部分的工作。

汇编阶段主要就是:校验启动合法性、建立段式映射的页表,并开启MMU,以方便使用内存、跳入 C 阶段。


源自朱有鹏老师.

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【20】核心易中期刊推荐——计算机科学电子通信(EI索引)

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

进阶C语言:文件操作

文件操作不仅仅是我们使用鼠标用来操作文件的各项功能,还可以使用C语言来操作文件的内容,可以使用C语言来对文件的读、写、拷贝...等等,话不多说,直接开始: 目录 1.为什么要使用文件 2.什么是文件 2.1程序文件 2.…

蓝桥杯【第14届省赛】Python B组

本题解不一定正确,欢迎大家指正 A:2023 【问题描述】 请求出在 12345678 至 98765432 中,有多少个数中完全不包含 2023 。 完全不包含 2023 是指无论将这个数的哪些数位移除都不能得到 2023 。 例如 20322175,33220022 都完全不包…

python实现获取当前目录下的树形结构

python实现获取当前目录下的树形结构 程序实现 在有些Linux上自带了tree命令来一树状结构显示一个目录,但是在有些linux上没有自带这个程序,所以这里用python写了一个小程序来实现这个功能,代码如下: import osdef print_tree(…

共模电感(扼流圈) 差模电感(扼流圈)

共模电感 共模电感,也叫共模扼流圈。常用于电脑的开关电源,过滤共模的电磁干扰信号。 共模电感器由软磁铁芯(铁氧体磁芯)和两组同相绕制的线圈组成。对于共模信号,由于两组线圈产生的磁场不是抵消。而是相互叠加&…

存量市场之下,电商之战深入腹地且逻辑未变

纵然是在流量依然见顶的大背景下,电商行业的竞争并未结束。无论是以百亿补贴为代表的烧钱策略,还是以跨境出海为代表的规模策略,几乎都是这样一种现象的直接体现。种种迹象表明,未来的电商行业,依然会有新的战役发生。…

Hadoop集群环境搭建与应用回顾

文章目录一、 实训项目名称二、 学习情况小结三、 项目中用到的知识点四、 实训项目中负责功能板块五、 实训项目实现六、 实训项目过程中遇到的问题及解决方法七、实训体会与心得一、 实训项目名称 Hadoop集群环境搭建与应用 二、 学习情况小结 实操一部分: 通…

linux 服务器 docker 安装 nacos 2.0.3

docker 镜像 https://registry.hub.docker.com/r/nacos/nacos-server/tags 1.下载nacos镜像 这里下载的是2.0.3 docker pull nacos/nacos-server:2.0.32.查看镜像是否下载成功 如下图 docker images3.创建文件夹 注: 自定义创建,用于把docker内部的文件…

【基金学习】基金的相关计算题目

文章目录一、基金收益计算1. 累计收益/持有收益2. 年化收益/年化收益率3. 涨跌幅4. 分红二、风险计算1. 贝塔系数2. 波动率三、费用计算1. 托管费2. 管理费3. 销售服务费4. 申购/赎回费一、基金收益计算 1. 累计收益/持有收益 累计收益 持仓金额 - 本金 累计收益率 &#xf…

TCP 的 NACK 与 SACK

可靠传输,一旦丢包,必然 HoL blocking,NACK 和 SACK 必须二选一驱动重传: NACK:receiver 主动通告没有收到的报文,sender 收到后重传。 SACK:receiver 主动通告收到的报文,sender …

一文带你深入了解算法笔记中的前缀与差分(附源码)

📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段,因为最近参加新星计划算法赛道(白佬),所以加快了脚步,果然急迫感会增加动力>——…

笔记本上就能跑的 LLM 好使吗?GPT4ALL 体验

是骡子是马,牵出来遛遛正名看到这个标题,你可能会觉得是噱头。谁不知道 ChatGPT 模型有 1750 亿以上的参数,别说是在笔记本电脑上跑,就是使用高性能 GPU 的台式机,也无法带得动啊。老老实实调用 API 不好吗&#xff1f…

torchvision.transforms 常用方法解析(含图例代码以及参数解释)

本文代码和图片完全源于 官方文档: TRANSFORMING AND AUGMENTING IMAGES 中的 Illustration of transforms,参数介绍源自函数对应的官方文档。 代码中的变换仅仅使用了最简单的参数:pad,size 等,这里展现的只是简单的变换&#xf…

中间表示- 数据流分析

数据流分析往往与优化绑定在一起,如下图所示。 优化的一般模式 程序分析 (1)控制流分析、数据流分析、依赖分析等。 (2)得到被优化程序的静态保守信息,是对动态运行行为的近似。 程序重写 以上一步得到…

用于3D分子生成的等变扩散模型ICML2022

现代深度学习方法开始对分子科学产生重要影响。在Alphafold在蛋白质折叠预测方面取得成功的背后,越来越多的工作开发了深度学习模型来分析或合成分子。分子存在于物理3D空间中,因此受制于几何对称性,如平移、旋转。这些对称性被称为三维欧几里…

Compose(?/N) - 标准布局

组合函数不指定布局默认是Box堆叠。 一、纵向 Colum inline fun Column( modifier Modifier, verticalArrangement Arrangement.Top, //子元素纵向排列规则 horizontalAlignment Alignment.Start, //子元素横向排列规则 content: Composable ColumnScope.() -…

蓝桥杯基础12:BASIC-3试题 字母图形

资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 利用字母可以组成一些美丽的图形,下面给出了一个例子: ABCDEFG BABCDEF CBABCDE DCBABCD EDC…

【设计模式】设计模式概述以及UML图

设计模式概述以及UML图设计模式概述1. 设计模式的概念2. 学习设计模式的必要性3. 设计模式的分类UML图1. 什么是UML图2. 类图的概念和作用3. 类图的表示法4. 类与类之间的表示方式4.1 关联关系4.2 聚合关系4.3 组合关系4.4 依赖关系4.5 继承关系4.6 实现关系设计模式概述 1. 设…

工作面试老大难-MySQL中的锁类型

MySQL 是支持ACID特性的数据库。我们都知道”C”代表Consistent,当不同事务操作同一行记录时,为了保证一致性,需要对记录加锁。在MySQL 中,不同的引擎下的锁行为也会不同,本文将重点介绍 MySQL InnoDB引擎中常见的锁。…

卷,卷,卷,SAM发布不到24h | BAAI、浙大、北大联合推出SegGPT

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 昨天都被Meta的SAM模型刷屏了吧!但在其发布24h不到! 北京智源人工智能研究院联合浙大、北大发布SegGPT: Segmenting Everything In Context。让我们来一探究竟。 我们提出了SegGPT…