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

这篇具有很好参考价值的文章主要介绍了ARM Linux 内核启动1 —— 汇编阶段。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、Makefile分析

1、Makefile 分析

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,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/ 目录下。

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


二、head.S 文件分析

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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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


2、内核的真正入口

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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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 启动的条件和要求。

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


三、内核启动的汇编阶段

1、__lookup_processor_type

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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


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


2、__lookup_machine_type

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


3、__vet_atags

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


(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 内存来做页表),坏处是比较粗,不能精细管理内存;

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

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

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


5、__switch_data

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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel

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

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

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

汇编linux内核,ARM  S5PV210 朱有鹏,linux 内核驱动,linux,ARM,s5pv210,arm开发,kernel


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

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


源自朱有鹏老师.文章来源地址https://www.toymoban.com/news/detail-520276.html

到了这里,关于ARM Linux 内核启动1 —— 汇编阶段的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux 内核ARM32启动

    基于linux4.19内核翻译理解 为了启动ARM Linux,您需要一个引导加载程序,它是在主内核之前运行的小程序。 引导加载程序被期望初始化各种设备,并最终调用Linux内核,向内核传递信息。 基本上,引导加载程序应该提供(至少)以下内容: 1.设置和初始化RAM。 2.初始化一个串

    2024年01月18日
    浏览(51)
  • ARM uboot 启动 Linux 内核

    此处,我使用的是九鼎提供的 uboot : 进入 uboot 的 sd_fusing 目录,执行命令烧写 uboot : ./sd_fusing.sh /dev/sdb 。 按任意键,进入 uboot 命令行: 将 Linux kernel 拷贝到 30008000。

    2024年02月11日
    浏览(54)
  • linux内核bitmap之setbit汇编实现

    内核版本:kernel 0.12 首先看一段代码,下面这段代码来自内核版本0.12的 mm/swap.c 中: 这段代码通过宏定义了三个位操作函数,分别是 bit() 测试位,setbit() 置位,clrbit() 清除位。 将上述代码进行改造,对 setbit() 封装后: 反汇编后: 执行 gcc -c -o main.o main.c objdump -s -d main.o bt:

    2024年02月13日
    浏览(99)
  • ARM 话说 Linux 内核

    1、到底什么是操作系统 (1) linux、windows、android、ucos 就是操作系统。 (2) 操作系统本质上是一个程序,由很多个源文件构成,需要编译、链接成操作系统程序(vmlinz、zImage)。 (3) 操作系统的主要作用就是管理计算机硬件,给应用程序提供一个运行环境。 2、操作系统核心功能

    2023年04月13日
    浏览(34)
  • ARM Linux 内核的配置和编译原理

    1、源码从哪里来 (1) 之前讲过,我们使用 2.6.35.7 版本的内核。 这个版本的内核有三种: 第一种,是 kernel.org 上的官方版本; 第二种,是三星移植过的; 第三种,是九鼎 X210 的移植版本。 我们讲课时使用第三种内核来讲解,后面的移植实验使用第二种内核来移植。 (2) 源码

    2024年02月09日
    浏览(50)
  • ARM & Linux 基础学习 / 配置交叉编译工具链 / 编译 Linux 应用和驱动 / 编译内核

    编辑整理 by Staok。 本文部分内容摘自 “100ask imx6ull” 开发板的配套资料(如 百问网的《嵌入式Linux应用开发完全手册》,在 百问网 imx6ull pro 开发板 页面 中的《2.1 100ASK_IMX6ULL_PRO:开发板资料》或《2.2 全系列Linux教程:在线视频与配套资料》里面可以下载到),还有参考 菜

    2024年02月04日
    浏览(50)
  • VSCode+GDB+Qemu调试ARM64 linux内核

    俗话说,工欲善其事 必先利其器。linux kernel是一个非常复杂的系统,初学者会很难入门。 如果有一个方便的调试环境,学习效率至少能有5-10倍的提升。 为了学习linux内核,通常有这两个需要 可以摆脱硬件,方便的编译和运行linux 可以使用图形化的工具来调试linux 笔者使用

    2024年02月08日
    浏览(44)
  • 【Linux内核解析-linux-5.14.10-内核源码注释】内核启动kernel_init解释

    static int __ref kernel_init(void *unused) : 声明一个静态整型函数 kernel_init() ,该函数不会被其他文件访问,使用 __ref 标记表示该函数是可重定位的,并且该函数不需要任何参数。 wait_for_completion(kthreadd_done); : 等待 kthreadd 线程完成初始化, wait_for_completion() 函数会阻塞当前进程,直到

    2024年02月02日
    浏览(69)
  • linux内核启动分析(三)

    我们前面看了start_kernel的一些函数,现在我们继续追setup_arch: setup_arch函数主要是处理cpu体系相关架构,我们是arm64平台,这个函数处理arm64的一些初始化,主要包括: 初始化内核的mm结构体的代码段、数据段和栈的结束地址; 调用函数early_fixmap_init进行早期固定映射初始化;

    2023年04月20日
    浏览(47)
  • Linux内存初始化-启动阶段的内存初始化

    本文代码基于ARM64平台, Linux kernel 5.15 在加载kernel 之前, kernel对于系统是有一定要求的,明确规定了boot阶段必须要把MMU关闭: 那么在进入kernel之后, 就必须有一个使能MMU, 建立映射的过程, 本文描述kernel启动阶段进行内存初始化相关的操作。 在初始化阶段,我们mapping二段

    2024年02月08日
    浏览(76)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包